blob: 95496d13107edc987a0af91031828eee6e9b969e [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
Rich Lane2d6d4822013-01-08 10:49:16 -080026import copy
Dan Talayco48370102010-03-03 15:17:33 -080027
Rich Lanefadf3452012-10-03 16:23:37 -070028root_dir = os.path.dirname(os.path.realpath(__file__))
29
30pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -070031if os.path.exists(os.path.join(pydir, 'oftest')):
32 # Running from source tree
33 sys.path.insert(0, pydir)
34
Rich Lane477f4812012-10-04 22:49:00 -070035import oftest
36from oftest import config
Rich Lanee55abf72012-07-26 20:11:42 -070037import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080038import oftest.help_formatter
Rich Lane3f7098c2013-03-12 10:28:32 -070039import loxi
Dan Talaycoba3745c2010-07-21 21:51:08 -070040
Dan Talayco48370102010-03-03 15:17:33 -080041##@var DEBUG_LEVELS
42# Map from strings to debugging levels
43DEBUG_LEVELS = {
44 'debug' : logging.DEBUG,
45 'verbose' : logging.DEBUG,
46 'info' : logging.INFO,
47 'warning' : logging.WARNING,
48 'warn' : logging.WARNING,
49 'error' : logging.ERROR,
50 'critical' : logging.CRITICAL
51}
52
Dan Talayco48370102010-03-03 15:17:33 -080053##@var config_default
54# The default configuration dictionary for OFT
55config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -080056 # Miscellaneous options
57 "list" : False,
58 "list_test_names" : False,
59 "allow_user" : False,
60
61 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080062 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080063 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070064 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080065
66 # Switch connection options
67 "controller_host" : "0.0.0.0", # For passive bind
68 "controller_port" : 6633,
69 "switch_ip" : None, # If not none, actively connect to switch
Rich Lane15f26322013-01-08 11:23:24 -080070 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080071 "platform_args" : None,
72 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080073 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080074 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080075
76 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080077 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080078 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080079 "debug" : "verbose",
80
81 # Test behavior options
82 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070083 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070084 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070085 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000086 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070087 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080088
89 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080090 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080091}
92
Rich Lane95f078b2013-01-06 13:24:58 -080093def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080094 """
95 Set up the configuration including parsing the arguments
96
Dan Talayco48370102010-03-03 15:17:33 -080097 @return A pair (config, args) where config is an config
98 object and args is any additional arguments from the command line
99 """
100
Rich Lane4113a582013-01-03 10:13:02 -0800101 usage = "usage: %prog [options] (test|group)..."
102
Rich Lane95f078b2013-01-06 13:24:58 -0800103 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800104OFTest is a framework and set of tests for validating OpenFlow switches.
105
106The default configuration assumes that an OpenFlow 1.0 switch is attempting to
107connect to a controller on the machine running OFTest, port 6633. Additionally,
108the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
109dataplane.
110
111If no positional arguments are given then OFTest will run all tests that
112depend only on standard OpenFlow 1.0. Otherwise each positional argument
113is interpreted as either a test name or a test group name. The union of
114these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800115--list option. Tests and groups can be subtracted from the result by
116prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800117"""
118
Rich Lane2d6d4822013-01-08 10:49:16 -0800119 # Parse --interface
120 def check_interface(option, opt, value):
121 try:
122 ofport, interface = value.split('@', 1)
123 ofport = int(ofport)
124 except ValueError:
125 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
126 return (ofport, interface)
127
128 class Option(optparse.Option):
129 TYPES = optparse.Option.TYPES + ("interface",)
130 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
131 TYPE_CHECKER["interface"] = check_interface
132
Rich Lane95f078b2013-01-06 13:24:58 -0800133 parser = optparse.OptionParser(version="%prog 0.1",
134 usage=usage,
135 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800136 formatter=oftest.help_formatter.HelpFormatter(),
137 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800138
139 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800140 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800141
Dan Talayco2c0dba32010-03-06 22:47:06 -0800142 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700143 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800144 parser.add_option("--list-test-names", action='store_true',
145 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700146 parser.add_option("--allow-user", action="store_true",
147 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800148
149 group = optparse.OptionGroup(parser, "Test selection options")
150 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
151 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
152 group.add_option("--test-dir", type="string", help="Directory containing tests")
153 parser.add_option_group(group)
154
155 group = optparse.OptionGroup(parser, "Switch connection options")
156 group.add_option("-H", "--host", dest="controller_host",
157 help="IP address to listen on (default %default)")
158 group.add_option("-p", "--port", dest="controller_port",
159 type="int", help="Port number to listen on (default %default)")
160 group.add_option("-S", "--switch-ip", dest="switch_ip",
161 help="If set, actively connect to this switch by IP")
162 group.add_option("-P", "--platform", help="Platform module name (default %default)")
163 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
164 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800165 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
166 help="Specify a OpenFlow port number and the dataplane interface to use. May be given multiple times. Example: 1@eth1")
Rich Lane50cfa502013-04-25 13:45:08 -0700167 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800168 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800169 parser.add_option_group(group)
170
171 group = optparse.OptionGroup(parser, "Logging options")
172 group.add_option("--log-file",
173 help="Name of log file, empty string to log to console (default %default)")
174 group.add_option("--log-append", action="store_true",
175 help="Do not delete log file if specified")
176 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
177 group.add_option("--debug", choices=dbg_lvl_names,
178 help="Debug lvl: debug, info, warning, error, critical (default %default)")
179 group.add_option("-v", "--verbose", action="store_const", dest="debug",
180 const="verbose", help="Shortcut for --debug=verbose")
181 group.add_option("-q", "--quiet", action="store_const", dest="debug",
182 const="warning", help="Shortcut for --debug=warning")
183 parser.add_option_group(group)
184
185 group = optparse.OptionGroup(parser, "Test behavior options")
186 group.add_option("--relax", action="store_true",
187 help="Relax packet match checks allowing other packets")
188 test_params_help = """Set test parameters: key=val;... (see --list)
189 """
190 group.add_option("-t", "--test-params", help=test_params_help)
191 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700192 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800193 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700194 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800195 group.add_option("--minsize", type="int",
196 help="Minimum allowable packet size on the dataplane.")
197 group.add_option("--random-seed", type="int",
198 help="Random number generator seed")
199 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000200
Dan Talayco48370102010-03-03 15:17:33 -0800201 # Might need this if other parsers want command line
202 # parser.allow_interspersed_args = False
203 (options, args) = parser.parse_args()
204
Rich Lane74b13d12013-05-03 17:58:50 -0700205 # If --test-dir wasn't given, pick one based on the OpenFlow version
206 if options.test_dir == None:
207 if options.openflow_version == "1.0":
208 options.test_dir = os.path.join(root_dir, "tests")
209 else:
210 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
211
Rich Lane95f078b2013-01-06 13:24:58 -0800212 # Convert options from a Namespace to a plain dictionary
213 config = config_default.copy()
214 for key in config.keys():
215 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800216
217 return (config, args)
218
219def logging_setup(config):
220 """
221 Set up logging based on config
222 """
Rich Lane4a905e02013-08-23 15:00:29 -0700223 _format = "%(asctime)s.%(msecs)03d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800224 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800225 _mode = config["log_append"] and "a" or "w"
Jeffrey Townsendedb12d32013-02-06 14:52:30 -0800226 if config['log_file'] in [ "-", "stderr" ]:
227 config['log_file'] = None
228 logging.basicConfig(filename=config['log_file'],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800229 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800230 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800231 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800232
Rich Lane943be672012-10-04 19:20:16 -0700233def load_test_modules(config):
234 """
235 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800236
Rich Lane943be672012-10-04 19:20:16 -0700237 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800238
Rich Lanecc45b8e2013-01-02 15:55:02 -0800239 Also updates the _groups member to include "standard" and
240 module test groups if appropriate.
241
Dan Talayco2c0dba32010-03-06 22:47:06 -0800242 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700243 @returns A dictionary from test module names to tuples of
244 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800245 """
246
Rich Lane943be672012-10-04 19:20:16 -0700247 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800248
Rich Lane943be672012-10-04 19:20:16 -0700249 for root, dirs, filenames in os.walk(config["test_dir"]):
250 # Iterate over each python file
251 for filename in fnmatch.filter(filenames, '[!.]*.py'):
252 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800253
Rich Lane943be672012-10-04 19:20:16 -0700254 try:
255 if sys.modules.has_key(modname):
256 mod = sys.modules[modname]
257 else:
258 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
259 except:
260 logging.warning("Could not import file " + filename)
261 raise
Rich Lane520e4152012-07-09 16:18:16 -0700262
Rich Lane943be672012-10-04 19:20:16 -0700263 # Find all testcases defined in the module
264 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700265 issubclass(v, unittest.TestCase) and
266 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700267 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800268 for (testname, test) in tests.items():
269 # Set default annotation values
270 if not hasattr(test, "_groups"):
271 test._groups = []
272 if not hasattr(test, "_nonstandard"):
273 test._nonstandard = False
274 if not hasattr(test, "_disabled"):
275 test._disabled = False
276
277 # Put test in its module's test group
278 if not test._disabled:
279 test._groups.append(modname)
280
281 # Put test in the standard test group
282 if not test._disabled and not test._nonstandard:
283 test._groups.append("standard")
284 test._groups.append("all") # backwards compatibility
285
Rich Lane943be672012-10-04 19:20:16 -0700286 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700287
Rich Lane943be672012-10-04 19:20:16 -0700288 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800289
Rich Lane5a9a1922013-01-11 14:29:30 -0800290def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700291 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800292 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800293 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800294 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700295 @param test_modules Same format as the output of load_test_modules.
296 @returns Same format as the output of load_test_modules.
297 """
298 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800299 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700300 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800301
302 if e.startswith('^'):
303 negated = True
304 e = e[1:]
305 else:
306 negated = False
307
Rich Lane15f64de2012-10-04 21:25:57 -0700308 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800309 for (testname, test) in tests.items():
310 if e in test._groups or e == "%s.%s" % (modname, testname):
311 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800312 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800313 if not hasattr(test, "_versions") or version in test._versions:
314 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800315 else:
316 if modname in result and testname in result[modname][1]:
317 del result[modname][1][testname]
318 if not result[modname][1]:
319 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800320 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800321
Rich Lane15f64de2012-10-04 21:25:57 -0700322 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800323 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800324
Rich Lane15f64de2012-10-04 21:25:57 -0700325 return result
326
Dan Talayco2c0dba32010-03-06 22:47:06 -0800327def die(msg, exit_val=1):
328 print msg
329 logging.critical(msg)
330 sys.exit(exit_val)
331
Dan Talayco79f36082010-03-11 16:53:53 -0800332def _space_to(n, str):
333 """
334 Generate a string of spaces to achieve width n given string str
335 If length of str >= n, return one space
336 """
337 spaces = n - len(str)
338 if spaces > 0:
339 return " " * spaces
340 return " "
341
Dan Talayco48370102010-03-03 15:17:33 -0800342#
343# Main script
344#
345
Rich Lane477f4812012-10-04 22:49:00 -0700346# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800347(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700348oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800349
Rich Laned7a85c42012-09-28 15:38:45 -0700350logging_setup(config)
351logging.info("++++++++ " + time.asctime() + " ++++++++")
352
Rich Lane9fd05682013-01-10 15:30:38 -0800353# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700354name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
355sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800356
357# HACK: testutils.py imports controller.py, which needs the ofp module
358import oftest.testutils
359
Rich Lanee284b6b2012-10-03 09:19:58 -0700360# Allow tests to import each other
361sys.path.append(config["test_dir"])
362
Rich Lanec76b09a2013-01-02 16:53:22 -0800363test_specs = args
364if config["test_spec"] != "":
365 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
366 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800367if config["test_file"] != None:
368 with open(config["test_file"], 'r') as f:
369 for line in f:
370 line, _, _ = line.partition('#') # remove comments
371 line = line.strip()
372 if line:
373 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800374if test_specs == []:
375 test_specs = ["standard"]
376
Rich Laned8e45482013-01-02 17:36:21 -0800377test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800378
379# Check if test list is requested; display and exit if so
380if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700381 mod_count = 0
382 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800383 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800384 print """\
385Tests are shown grouped by module. If a test is in any groups beyond "standard"
386and its module's group then they are shown in parentheses."""
387 print
388 print """\
389Tests marked with '*' are non-standard and may require vendor extensions or
390special switch configuration. These are not part of the "standard" test group."""
391 print
392 print """\
393Tests marked with '!' are disabled because they are experimental, special-purpose,
394or are too long to be run normally. These are not part of the "standard" test
395group or their module's test group."""
396 print
397 print "Tests marked (TP1) after name take --test-params including:"
398 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800399 print
400 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700401 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700402 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800403 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800404 start_str = " Module " + mod.__name__ + ": "
405 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700406 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700407 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800408 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700409 desc = desc.split('\n')[0]
410 except:
411 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800412 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800413 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800414 if groups:
415 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800416 if hasattr(test, "_versions"):
417 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800418 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
419 test._disabled and "!" or " ",
420 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700421 if len(start_str) > 22:
422 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800423 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700424 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800425 print
Rich Lane15f64de2012-10-04 21:25:57 -0700426 print "%d modules shown with a total of %d tests" % \
427 (mod_count, test_count)
428 print
Rich Lane37f42112013-01-03 13:41:49 -0800429 print "Test groups: %s" % (', '.join(sorted(all_groups)))
430
Dan Talayco2c0dba32010-03-06 22:47:06 -0800431 sys.exit(0)
432
Rich Lane5a9a1922013-01-11 14:29:30 -0800433test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800434
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700435# Check if test list is requested; display and exit if so
436if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700437 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700438 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800439 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700440 sys.exit(0)
441
Dan Talayco2c0dba32010-03-06 22:47:06 -0800442# Generate the test suite
443#@todo Decide if multiple suites are ever needed
444suite = unittest.TestSuite()
445
Rich Lane15f64de2012-10-04 21:25:57 -0700446for (modname, (mod, tests)) in test_modules.items():
447 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800448 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800449
Rich Lane51590f62012-10-09 15:06:29 -0700450# Allow platforms to import each other
451sys.path.append(config["platform_dir"])
452
Rich Lane8aebc5e2012-09-25 17:57:53 -0700453# Load the platform module
454platform_name = config["platform"]
455logging.info("Importing platform: " + platform_name)
456platform_mod = None
457try:
Rich Lane483e1542012-10-05 09:29:39 -0700458 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700459except:
460 logging.warn("Failed to import " + platform_name + " platform module")
461 raise
Dan Talayco48370102010-03-03 15:17:33 -0800462
463try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700464 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800465except:
466 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700467 raise
Dan Talayco48370102010-03-03 15:17:33 -0800468
469if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700470 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800471
472logging.debug("Configuration: " + str(config))
473logging.info("OF port map: " + str(config["port_map"]))
474
Rich Lanee55abf72012-07-26 20:11:42 -0700475oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700476oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700477
Rich Laneee57ad02012-07-13 15:40:36 -0700478if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700479 print "ERROR: Super-user privileges required. Please re-run with " \
480 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700481 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700482
Rich Lane8592bec2012-09-03 09:06:59 -0700483if config["random_seed"] is not None:
484 logging.info("Random seed: %d" % config["random_seed"])
485 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700486else:
487 # Generate random seed and report to log file
488 seed = random.randrange(100000000)
489 logging.info("Autogen random seed: %d" % seed)
490 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700491
Rich Lane5bd6cf92012-10-04 17:57:24 -0700492# Remove python's signal handler which raises KeyboardError. Exiting from an
493# exception waits for all threads to terminate which might not happen.
494signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700495
496if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800497 # Set up the dataplane
498 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
499 for of_port, ifname in config["port_map"].items():
500 oftest.dataplane_instance.port_add(ifname, of_port)
501
Dan Talaycoac25cf32010-07-20 14:08:28 -0700502 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800503 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700504 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700505 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700506 if oftest.testutils.skipped_test_count == 1: ts = " test"
507 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
508 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700509 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800510
511 # Shutdown the dataplane
512 oftest.dataplane_instance.kill()
513 oftest.dataplane_instance = None
514
Rich Lane50d42eb2012-07-16 11:57:03 -0700515 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700516 # exit(1) hangs sometimes
517 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700518 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700519 os._exit(1)