blob: d0db9c301ca73f80ba71d7d4988e961d897e2f67 [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
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
Rich Lane15f26322013-01-08 11:23:24 -080077 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080078 "platform_args" : None,
79 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080080 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080081 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080082
83 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080084 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080085 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080086 "debug" : "verbose",
87
88 # Test behavior options
89 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070090 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070091 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070092 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000093 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070094 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080095
96 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080097 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080098}
99
Rich Lane95f078b2013-01-06 13:24:58 -0800100def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800101 """
102 Set up the configuration including parsing the arguments
103
Dan Talayco48370102010-03-03 15:17:33 -0800104 @return A pair (config, args) where config is an config
105 object and args is any additional arguments from the command line
106 """
107
Rich Lane4113a582013-01-03 10:13:02 -0800108 usage = "usage: %prog [options] (test|group)..."
109
Rich Lane95f078b2013-01-06 13:24:58 -0800110 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800111OFTest is a framework and set of tests for validating OpenFlow switches.
112
113The default configuration assumes that an OpenFlow 1.0 switch is attempting to
114connect to a controller on the machine running OFTest, port 6633. Additionally,
115the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
116dataplane.
117
118If no positional arguments are given then OFTest will run all tests that
119depend only on standard OpenFlow 1.0. Otherwise each positional argument
120is interpreted as either a test name or a test group name. The union of
121these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800122--list option. Tests and groups can be subtracted from the result by
123prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800124"""
125
Rich Lane2d6d4822013-01-08 10:49:16 -0800126 # Parse --interface
127 def check_interface(option, opt, value):
128 try:
129 ofport, interface = value.split('@', 1)
130 ofport = int(ofport)
131 except ValueError:
132 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
133 return (ofport, interface)
134
135 class Option(optparse.Option):
136 TYPES = optparse.Option.TYPES + ("interface",)
137 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
138 TYPE_CHECKER["interface"] = check_interface
139
Rich Lane95f078b2013-01-06 13:24:58 -0800140 parser = optparse.OptionParser(version="%prog 0.1",
141 usage=usage,
142 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800143 formatter=oftest.help_formatter.HelpFormatter(),
144 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800145
146 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800147 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800148
Dan Talayco2c0dba32010-03-06 22:47:06 -0800149 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700150 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800151 parser.add_option("--list-test-names", action='store_true',
152 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700153 parser.add_option("--allow-user", action="store_true",
154 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800155
156 group = optparse.OptionGroup(parser, "Test selection options")
157 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
158 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
159 group.add_option("--test-dir", type="string", help="Directory containing tests")
160 parser.add_option_group(group)
161
162 group = optparse.OptionGroup(parser, "Switch connection options")
163 group.add_option("-H", "--host", dest="controller_host",
164 help="IP address to listen on (default %default)")
165 group.add_option("-p", "--port", dest="controller_port",
166 type="int", help="Port number to listen on (default %default)")
167 group.add_option("-S", "--switch-ip", dest="switch_ip",
168 help="If set, actively connect to this switch by IP")
169 group.add_option("-P", "--platform", help="Platform module name (default %default)")
170 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
171 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800172 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
173 help="Specify a OpenFlow port number and the dataplane interface to use. May be given multiple times. Example: 1@eth1")
Rich Lane9fd05682013-01-10 15:30:38 -0800174 group.add_option("--of-version", "-V", dest="openflow_version", choices=["1.0", "1.1", "1.2"],
175 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800176 parser.add_option_group(group)
177
178 group = optparse.OptionGroup(parser, "Logging options")
179 group.add_option("--log-file",
180 help="Name of log file, empty string to log to console (default %default)")
181 group.add_option("--log-append", action="store_true",
182 help="Do not delete log file if specified")
183 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
184 group.add_option("--debug", choices=dbg_lvl_names,
185 help="Debug lvl: debug, info, warning, error, critical (default %default)")
186 group.add_option("-v", "--verbose", action="store_const", dest="debug",
187 const="verbose", help="Shortcut for --debug=verbose")
188 group.add_option("-q", "--quiet", action="store_const", dest="debug",
189 const="warning", help="Shortcut for --debug=warning")
190 parser.add_option_group(group)
191
192 group = optparse.OptionGroup(parser, "Test behavior options")
193 group.add_option("--relax", action="store_true",
194 help="Relax packet match checks allowing other packets")
195 test_params_help = """Set test parameters: key=val;... (see --list)
196 """
197 group.add_option("-t", "--test-params", help=test_params_help)
198 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700199 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800200 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700201 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800202 group.add_option("--minsize", type="int",
203 help="Minimum allowable packet size on the dataplane.")
204 group.add_option("--random-seed", type="int",
205 help="Random number generator seed")
206 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000207
Dan Talayco48370102010-03-03 15:17:33 -0800208 # Might need this if other parsers want command line
209 # parser.allow_interspersed_args = False
210 (options, args) = parser.parse_args()
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 Lane71d887d2012-12-22 17:05:13 -0800223 _format = "%(asctime)s.%(msecs)d %(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
265 issubclass(v, unittest.TestCase))
266 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800267 for (testname, test) in tests.items():
268 # Set default annotation values
269 if not hasattr(test, "_groups"):
270 test._groups = []
271 if not hasattr(test, "_nonstandard"):
272 test._nonstandard = False
273 if not hasattr(test, "_disabled"):
274 test._disabled = False
275
276 # Put test in its module's test group
277 if not test._disabled:
278 test._groups.append(modname)
279
280 # Put test in the standard test group
281 if not test._disabled and not test._nonstandard:
282 test._groups.append("standard")
283 test._groups.append("all") # backwards compatibility
284
Rich Lane943be672012-10-04 19:20:16 -0700285 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700286
Rich Lane943be672012-10-04 19:20:16 -0700287 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800288
Rich Lane5a9a1922013-01-11 14:29:30 -0800289def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700290 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800291 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800292 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800293 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700294 @param test_modules Same format as the output of load_test_modules.
295 @returns Same format as the output of load_test_modules.
296 """
297 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800298 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700299 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800300
301 if e.startswith('^'):
302 negated = True
303 e = e[1:]
304 else:
305 negated = False
306
Rich Lane15f64de2012-10-04 21:25:57 -0700307 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800308 for (testname, test) in tests.items():
309 if e in test._groups or e == "%s.%s" % (modname, testname):
310 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800311 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800312 if not hasattr(test, "_versions") or version in test._versions:
313 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800314 else:
315 if modname in result and testname in result[modname][1]:
316 del result[modname][1][testname]
317 if not result[modname][1]:
318 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800319 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800320
Rich Lane15f64de2012-10-04 21:25:57 -0700321 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800322 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800323
Rich Lane15f64de2012-10-04 21:25:57 -0700324 return result
325
Dan Talayco2c0dba32010-03-06 22:47:06 -0800326def die(msg, exit_val=1):
327 print msg
328 logging.critical(msg)
329 sys.exit(exit_val)
330
Dan Talayco79f36082010-03-11 16:53:53 -0800331def _space_to(n, str):
332 """
333 Generate a string of spaces to achieve width n given string str
334 If length of str >= n, return one space
335 """
336 spaces = n - len(str)
337 if spaces > 0:
338 return " " * spaces
339 return " "
340
Dan Talayco48370102010-03-03 15:17:33 -0800341#
342# Main script
343#
344
Rich Lane477f4812012-10-04 22:49:00 -0700345# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800346(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700347oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800348
Rich Laned7a85c42012-09-28 15:38:45 -0700349logging_setup(config)
350logging.info("++++++++ " + time.asctime() + " ++++++++")
351
Rich Lane9fd05682013-01-10 15:30:38 -0800352# Pick an OpenFlow protocol module based on the configured version
353# This will go away once we have a single protocol module that
354# can handle all OpenFlow versions.
355if config["openflow_version"] == "1.0":
356 import of10
357 sys.modules["ofp"] = of10
358elif config["openflow_version"] == "1.1":
359 import of11
360 sys.modules["ofp"] = of11
361elif config["openflow_version"] == "1.2":
362 import of12
363 sys.modules["ofp"] = of12
364else:
365 assert(False)
366
367# HACK: testutils.py imports controller.py, which needs the ofp module
368import oftest.testutils
369
Rich Lanee284b6b2012-10-03 09:19:58 -0700370# Allow tests to import each other
371sys.path.append(config["test_dir"])
372
Rich Lanec76b09a2013-01-02 16:53:22 -0800373test_specs = args
374if config["test_spec"] != "":
375 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
376 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800377if config["test_file"] != None:
378 with open(config["test_file"], 'r') as f:
379 for line in f:
380 line, _, _ = line.partition('#') # remove comments
381 line = line.strip()
382 if line:
383 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800384if test_specs == []:
385 test_specs = ["standard"]
386
Rich Laned8e45482013-01-02 17:36:21 -0800387test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800388
389# Check if test list is requested; display and exit if so
390if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700391 mod_count = 0
392 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800393 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800394 print """\
395Tests are shown grouped by module. If a test is in any groups beyond "standard"
396and its module's group then they are shown in parentheses."""
397 print
398 print """\
399Tests marked with '*' are non-standard and may require vendor extensions or
400special switch configuration. These are not part of the "standard" test group."""
401 print
402 print """\
403Tests marked with '!' are disabled because they are experimental, special-purpose,
404or are too long to be run normally. These are not part of the "standard" test
405group or their module's test group."""
406 print
407 print "Tests marked (TP1) after name take --test-params including:"
408 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800409 print
410 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700411 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700412 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800413 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800414 start_str = " Module " + mod.__name__ + ": "
415 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700416 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700417 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800418 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700419 desc = desc.split('\n')[0]
420 except:
421 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800422 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800423 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800424 if groups:
425 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800426 if hasattr(test, "_versions"):
427 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800428 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
429 test._disabled and "!" or " ",
430 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700431 if len(start_str) > 22:
432 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800433 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700434 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800435 print
Rich Lane15f64de2012-10-04 21:25:57 -0700436 print "%d modules shown with a total of %d tests" % \
437 (mod_count, test_count)
438 print
Rich Lane37f42112013-01-03 13:41:49 -0800439 print "Test groups: %s" % (', '.join(sorted(all_groups)))
440
Dan Talayco2c0dba32010-03-06 22:47:06 -0800441 sys.exit(0)
442
Rich Lane5a9a1922013-01-11 14:29:30 -0800443test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800444
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700445# Check if test list is requested; display and exit if so
446if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700447 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700448 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800449 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700450 sys.exit(0)
451
Dan Talayco2c0dba32010-03-06 22:47:06 -0800452# Generate the test suite
453#@todo Decide if multiple suites are ever needed
454suite = unittest.TestSuite()
455
Rich Lane15f64de2012-10-04 21:25:57 -0700456for (modname, (mod, tests)) in test_modules.items():
457 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800458 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800459
Rich Lane51590f62012-10-09 15:06:29 -0700460# Allow platforms to import each other
461sys.path.append(config["platform_dir"])
462
Rich Lane8aebc5e2012-09-25 17:57:53 -0700463# Load the platform module
464platform_name = config["platform"]
465logging.info("Importing platform: " + platform_name)
466platform_mod = None
467try:
Rich Lane483e1542012-10-05 09:29:39 -0700468 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700469except:
470 logging.warn("Failed to import " + platform_name + " platform module")
471 raise
Dan Talayco48370102010-03-03 15:17:33 -0800472
473try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700474 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800475except:
476 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700477 raise
Dan Talayco48370102010-03-03 15:17:33 -0800478
479if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700480 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800481
482logging.debug("Configuration: " + str(config))
483logging.info("OF port map: " + str(config["port_map"]))
484
Rich Lanee55abf72012-07-26 20:11:42 -0700485oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700486oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700487
Rich Laneee57ad02012-07-13 15:40:36 -0700488if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700489 print "ERROR: Super-user privileges required. Please re-run with " \
490 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700491 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700492
Rich Lane8592bec2012-09-03 09:06:59 -0700493if config["random_seed"] is not None:
494 logging.info("Random seed: %d" % config["random_seed"])
495 random.seed(config["random_seed"])
496
Rich Lane5bd6cf92012-10-04 17:57:24 -0700497# Remove python's signal handler which raises KeyboardError. Exiting from an
498# exception waits for all threads to terminate which might not happen.
499signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700500
501if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800502 # Set up the dataplane
503 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
504 for of_port, ifname in config["port_map"].items():
505 oftest.dataplane_instance.port_add(ifname, of_port)
506
Dan Talaycoac25cf32010-07-20 14:08:28 -0700507 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800508 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700509 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700510 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700511 if oftest.testutils.skipped_test_count == 1: ts = " test"
512 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
513 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700514 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800515
516 # Shutdown the dataplane
517 oftest.dataplane_instance.kill()
518 oftest.dataplane_instance = None
519
Rich Lane50d42eb2012-07-16 11:57:03 -0700520 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700521 # exit(1) hangs sometimes
522 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700523 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700524 os._exit(1)