blob: b79d46c5d0ec98b4ed302360fd79df41c9da2bd6 [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",
Rich Lane69fd8e02013-08-23 16:23:42 -070078 "log_dir" : None,
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")
Rich Lane69fd8e02013-08-23 16:23:42 -0700172 group.add_option("--log-file", help="Name of log file (default %default)")
173 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800174 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
175 group.add_option("--debug", choices=dbg_lvl_names,
176 help="Debug lvl: debug, info, warning, error, critical (default %default)")
177 group.add_option("-v", "--verbose", action="store_const", dest="debug",
178 const="verbose", help="Shortcut for --debug=verbose")
179 group.add_option("-q", "--quiet", action="store_const", dest="debug",
180 const="warning", help="Shortcut for --debug=warning")
181 parser.add_option_group(group)
182
183 group = optparse.OptionGroup(parser, "Test behavior options")
184 group.add_option("--relax", action="store_true",
185 help="Relax packet match checks allowing other packets")
186 test_params_help = """Set test parameters: key=val;... (see --list)
187 """
188 group.add_option("-t", "--test-params", help=test_params_help)
189 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700190 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800191 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700192 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800193 group.add_option("--minsize", type="int",
194 help="Minimum allowable packet size on the dataplane.")
195 group.add_option("--random-seed", type="int",
196 help="Random number generator seed")
197 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000198
Dan Talayco48370102010-03-03 15:17:33 -0800199 # Might need this if other parsers want command line
200 # parser.allow_interspersed_args = False
201 (options, args) = parser.parse_args()
202
Rich Lane74b13d12013-05-03 17:58:50 -0700203 # If --test-dir wasn't given, pick one based on the OpenFlow version
204 if options.test_dir == None:
205 if options.openflow_version == "1.0":
206 options.test_dir = os.path.join(root_dir, "tests")
207 else:
208 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
209
Rich Lane95f078b2013-01-06 13:24:58 -0800210 # Convert options from a Namespace to a plain dictionary
211 config = config_default.copy()
212 for key in config.keys():
213 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800214
215 return (config, args)
216
217def logging_setup(config):
218 """
219 Set up logging based on config
220 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700221
222 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
223
224 if config["log_dir"] != None:
225 if os.path.exists(config["log_dir"]):
226 import shutil
227 shutil.rmtree(config["log_dir"])
228 os.makedirs(config["log_dir"])
229 else:
230 if os.path.exists(config["log_file"]):
231 os.remove(config["log_file"])
232
233 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800234
Rich Lane943be672012-10-04 19:20:16 -0700235def load_test_modules(config):
236 """
237 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800238
Rich Lane943be672012-10-04 19:20:16 -0700239 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800240
Rich Lanecc45b8e2013-01-02 15:55:02 -0800241 Also updates the _groups member to include "standard" and
242 module test groups if appropriate.
243
Dan Talayco2c0dba32010-03-06 22:47:06 -0800244 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700245 @returns A dictionary from test module names to tuples of
246 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800247 """
248
Rich Lane943be672012-10-04 19:20:16 -0700249 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800250
Rich Lane943be672012-10-04 19:20:16 -0700251 for root, dirs, filenames in os.walk(config["test_dir"]):
252 # Iterate over each python file
253 for filename in fnmatch.filter(filenames, '[!.]*.py'):
254 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800255
Rich Lane943be672012-10-04 19:20:16 -0700256 try:
257 if sys.modules.has_key(modname):
258 mod = sys.modules[modname]
259 else:
260 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
261 except:
262 logging.warning("Could not import file " + filename)
263 raise
Rich Lane520e4152012-07-09 16:18:16 -0700264
Rich Lane943be672012-10-04 19:20:16 -0700265 # Find all testcases defined in the module
266 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700267 issubclass(v, unittest.TestCase) and
268 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700269 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800270 for (testname, test) in tests.items():
271 # Set default annotation values
272 if not hasattr(test, "_groups"):
273 test._groups = []
274 if not hasattr(test, "_nonstandard"):
275 test._nonstandard = False
276 if not hasattr(test, "_disabled"):
277 test._disabled = False
278
279 # Put test in its module's test group
280 if not test._disabled:
281 test._groups.append(modname)
282
283 # Put test in the standard test group
284 if not test._disabled and not test._nonstandard:
285 test._groups.append("standard")
286 test._groups.append("all") # backwards compatibility
287
Rich Lane943be672012-10-04 19:20:16 -0700288 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700289
Rich Lane943be672012-10-04 19:20:16 -0700290 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800291
Rich Lane5a9a1922013-01-11 14:29:30 -0800292def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700293 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800294 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800295 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800296 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700297 @param test_modules Same format as the output of load_test_modules.
298 @returns Same format as the output of load_test_modules.
299 """
300 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800301 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700302 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800303
304 if e.startswith('^'):
305 negated = True
306 e = e[1:]
307 else:
308 negated = False
309
Rich Lane15f64de2012-10-04 21:25:57 -0700310 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800311 for (testname, test) in tests.items():
312 if e in test._groups or e == "%s.%s" % (modname, testname):
313 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800314 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800315 if not hasattr(test, "_versions") or version in test._versions:
316 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800317 else:
318 if modname in result and testname in result[modname][1]:
319 del result[modname][1][testname]
320 if not result[modname][1]:
321 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800322 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800323
Rich Lane15f64de2012-10-04 21:25:57 -0700324 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800325 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800326
Rich Lane15f64de2012-10-04 21:25:57 -0700327 return result
328
Dan Talayco2c0dba32010-03-06 22:47:06 -0800329def die(msg, exit_val=1):
330 print msg
331 logging.critical(msg)
332 sys.exit(exit_val)
333
Dan Talayco79f36082010-03-11 16:53:53 -0800334def _space_to(n, str):
335 """
336 Generate a string of spaces to achieve width n given string str
337 If length of str >= n, return one space
338 """
339 spaces = n - len(str)
340 if spaces > 0:
341 return " " * spaces
342 return " "
343
Dan Talayco48370102010-03-03 15:17:33 -0800344#
345# Main script
346#
347
Rich Lane477f4812012-10-04 22:49:00 -0700348# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800349(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700350oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800351
Rich Laned7a85c42012-09-28 15:38:45 -0700352logging_setup(config)
353logging.info("++++++++ " + time.asctime() + " ++++++++")
354
Rich Lane9fd05682013-01-10 15:30:38 -0800355# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700356name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
357sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800358
359# HACK: testutils.py imports controller.py, which needs the ofp module
360import oftest.testutils
361
Rich Lanee284b6b2012-10-03 09:19:58 -0700362# Allow tests to import each other
363sys.path.append(config["test_dir"])
364
Rich Lanec76b09a2013-01-02 16:53:22 -0800365test_specs = args
366if config["test_spec"] != "":
367 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
368 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800369if config["test_file"] != None:
370 with open(config["test_file"], 'r') as f:
371 for line in f:
372 line, _, _ = line.partition('#') # remove comments
373 line = line.strip()
374 if line:
375 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800376if test_specs == []:
377 test_specs = ["standard"]
378
Rich Laned8e45482013-01-02 17:36:21 -0800379test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800380
381# Check if test list is requested; display and exit if so
382if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700383 mod_count = 0
384 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800385 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800386 print """\
387Tests are shown grouped by module. If a test is in any groups beyond "standard"
388and its module's group then they are shown in parentheses."""
389 print
390 print """\
391Tests marked with '*' are non-standard and may require vendor extensions or
392special switch configuration. These are not part of the "standard" test group."""
393 print
394 print """\
395Tests marked with '!' are disabled because they are experimental, special-purpose,
396or are too long to be run normally. These are not part of the "standard" test
397group or their module's test group."""
398 print
399 print "Tests marked (TP1) after name take --test-params including:"
400 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800401 print
402 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700403 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700404 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800405 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800406 start_str = " Module " + mod.__name__ + ": "
407 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700408 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700409 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800410 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700411 desc = desc.split('\n')[0]
412 except:
413 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800414 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800415 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800416 if groups:
417 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800418 if hasattr(test, "_versions"):
419 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800420 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
421 test._disabled and "!" or " ",
422 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700423 if len(start_str) > 22:
424 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800425 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700426 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800427 print
Rich Lane15f64de2012-10-04 21:25:57 -0700428 print "%d modules shown with a total of %d tests" % \
429 (mod_count, test_count)
430 print
Rich Lane37f42112013-01-03 13:41:49 -0800431 print "Test groups: %s" % (', '.join(sorted(all_groups)))
432
Dan Talayco2c0dba32010-03-06 22:47:06 -0800433 sys.exit(0)
434
Rich Lane5a9a1922013-01-11 14:29:30 -0800435test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800436
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700437# Check if test list is requested; display and exit if so
438if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700439 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700440 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800441 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700442 sys.exit(0)
443
Dan Talayco2c0dba32010-03-06 22:47:06 -0800444# Generate the test suite
445#@todo Decide if multiple suites are ever needed
446suite = unittest.TestSuite()
447
Rich Lane15f64de2012-10-04 21:25:57 -0700448for (modname, (mod, tests)) in test_modules.items():
449 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800450 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800451
Rich Lane51590f62012-10-09 15:06:29 -0700452# Allow platforms to import each other
453sys.path.append(config["platform_dir"])
454
Rich Lane8aebc5e2012-09-25 17:57:53 -0700455# Load the platform module
456platform_name = config["platform"]
457logging.info("Importing platform: " + platform_name)
458platform_mod = None
459try:
Rich Lane483e1542012-10-05 09:29:39 -0700460 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700461except:
462 logging.warn("Failed to import " + platform_name + " platform module")
463 raise
Dan Talayco48370102010-03-03 15:17:33 -0800464
465try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700466 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800467except:
468 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700469 raise
Dan Talayco48370102010-03-03 15:17:33 -0800470
471if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700472 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800473
474logging.debug("Configuration: " + str(config))
475logging.info("OF port map: " + str(config["port_map"]))
476
Rich Lanee55abf72012-07-26 20:11:42 -0700477oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700478oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700479
Rich Laneee57ad02012-07-13 15:40:36 -0700480if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700481 print "ERROR: Super-user privileges required. Please re-run with " \
482 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700483 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700484
Rich Lane8592bec2012-09-03 09:06:59 -0700485if config["random_seed"] is not None:
486 logging.info("Random seed: %d" % config["random_seed"])
487 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700488else:
489 # Generate random seed and report to log file
490 seed = random.randrange(100000000)
491 logging.info("Autogen random seed: %d" % seed)
492 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700493
Rich Lane5bd6cf92012-10-04 17:57:24 -0700494# Remove python's signal handler which raises KeyboardError. Exiting from an
495# exception waits for all threads to terminate which might not happen.
496signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700497
498if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800499 # Set up the dataplane
500 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
501 for of_port, ifname in config["port_map"].items():
502 oftest.dataplane_instance.port_add(ifname, of_port)
503
Dan Talaycoac25cf32010-07-20 14:08:28 -0700504 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800505 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700506 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700507 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700508 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700509 if oftest.testutils.skipped_test_count == 1: ts = " test"
510 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
511 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700512 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800513
514 # Shutdown the dataplane
515 oftest.dataplane_instance.kill()
516 oftest.dataplane_instance = None
517
Rich Lane50d42eb2012-07-16 11:57:03 -0700518 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700519 # exit(1) hangs sometimes
520 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700521 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700522 os._exit(1)