blob: 4ef82ebe0e10be2087f8c70bcb39f222eb6cdd65 [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
Rich Lane4d1f3eb2013-10-03 13:45:57 -070068 "controller_port" : 6653,
Rich Lane95f078b2013-01-06 13:24:58 -080069 "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,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000088 "disable_ipv6" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080089
90 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080091 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080092}
93
Rich Lane95f078b2013-01-06 13:24:58 -080094def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080095 """
96 Set up the configuration including parsing the arguments
97
Dan Talayco48370102010-03-03 15:17:33 -080098 @return A pair (config, args) where config is an config
99 object and args is any additional arguments from the command line
100 """
101
Rich Lane4113a582013-01-03 10:13:02 -0800102 usage = "usage: %prog [options] (test|group)..."
103
Rich Lane95f078b2013-01-06 13:24:58 -0800104 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800105OFTest is a framework and set of tests for validating OpenFlow switches.
106
107The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700108connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800109the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
110dataplane.
111
112If no positional arguments are given then OFTest will run all tests that
113depend only on standard OpenFlow 1.0. Otherwise each positional argument
114is interpreted as either a test name or a test group name. The union of
115these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800116--list option. Tests and groups can be subtracted from the result by
117prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800118"""
119
Rich Lane2d6d4822013-01-08 10:49:16 -0800120 # Parse --interface
121 def check_interface(option, opt, value):
122 try:
123 ofport, interface = value.split('@', 1)
124 ofport = int(ofport)
125 except ValueError:
126 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
127 return (ofport, interface)
128
129 class Option(optparse.Option):
130 TYPES = optparse.Option.TYPES + ("interface",)
131 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
132 TYPE_CHECKER["interface"] = check_interface
133
Rich Lane95f078b2013-01-06 13:24:58 -0800134 parser = optparse.OptionParser(version="%prog 0.1",
135 usage=usage,
136 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800137 formatter=oftest.help_formatter.HelpFormatter(),
138 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800139
140 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800141 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800142
Dan Talayco2c0dba32010-03-06 22:47:06 -0800143 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700144 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800145 parser.add_option("--list-test-names", action='store_true',
146 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700147 parser.add_option("--allow-user", action="store_true",
148 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800149
150 group = optparse.OptionGroup(parser, "Test selection options")
151 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
152 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
153 group.add_option("--test-dir", type="string", help="Directory containing tests")
154 parser.add_option_group(group)
155
156 group = optparse.OptionGroup(parser, "Switch connection options")
157 group.add_option("-H", "--host", dest="controller_host",
158 help="IP address to listen on (default %default)")
159 group.add_option("-p", "--port", dest="controller_port",
160 type="int", help="Port number to listen on (default %default)")
161 group.add_option("-S", "--switch-ip", dest="switch_ip",
162 help="If set, actively connect to this switch by IP")
163 group.add_option("-P", "--platform", help="Platform module name (default %default)")
164 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
165 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800166 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
167 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 -0700168 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800169 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800170 parser.add_option_group(group)
171
172 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700173 group.add_option("--log-file", help="Name of log file (default %default)")
174 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800175 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
176 group.add_option("--debug", choices=dbg_lvl_names,
177 help="Debug lvl: debug, info, warning, error, critical (default %default)")
178 group.add_option("-v", "--verbose", action="store_const", dest="debug",
179 const="verbose", help="Shortcut for --debug=verbose")
180 group.add_option("-q", "--quiet", action="store_const", dest="debug",
181 const="warning", help="Shortcut for --debug=warning")
182 parser.add_option_group(group)
183
184 group = optparse.OptionGroup(parser, "Test behavior options")
185 group.add_option("--relax", action="store_true",
186 help="Relax packet match checks allowing other packets")
187 test_params_help = """Set test parameters: key=val;... (see --list)
188 """
189 group.add_option("-t", "--test-params", help=test_params_help)
190 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700191 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800192 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700193 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800194 group.add_option("--minsize", type="int",
195 help="Minimum allowable packet size on the dataplane.")
196 group.add_option("--random-seed", type="int",
197 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000198 group.add_option("--disable-ipv6", action="store_true",
199 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800200 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000201
Dan Talayco48370102010-03-03 15:17:33 -0800202 # Might need this if other parsers want command line
203 # parser.allow_interspersed_args = False
204 (options, args) = parser.parse_args()
205
Rich Lane74b13d12013-05-03 17:58:50 -0700206 # If --test-dir wasn't given, pick one based on the OpenFlow version
207 if options.test_dir == None:
208 if options.openflow_version == "1.0":
209 options.test_dir = os.path.join(root_dir, "tests")
210 else:
211 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
212
Rich Lane95f078b2013-01-06 13:24:58 -0800213 # Convert options from a Namespace to a plain dictionary
214 config = config_default.copy()
215 for key in config.keys():
216 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800217
218 return (config, args)
219
220def logging_setup(config):
221 """
222 Set up logging based on config
223 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700224
225 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
226
227 if config["log_dir"] != None:
228 if os.path.exists(config["log_dir"]):
229 import shutil
230 shutil.rmtree(config["log_dir"])
231 os.makedirs(config["log_dir"])
232 else:
233 if os.path.exists(config["log_file"]):
234 os.remove(config["log_file"])
235
236 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800237
Rich Lane472aaea2013-08-27 09:27:38 -0700238def pcap_setup(config):
239 """
240 Set up dataplane packet capturing based on config
241 """
242
243 if config["log_dir"] == None:
244 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
245 oftest.dataplane_instance.start_pcap(filename)
246 else:
247 # start_pcap is called per-test in base_tests
248 pass
249
250
Rich Lane943be672012-10-04 19:20:16 -0700251def load_test_modules(config):
252 """
253 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800254
Rich Lane943be672012-10-04 19:20:16 -0700255 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800256
Rich Lanecc45b8e2013-01-02 15:55:02 -0800257 Also updates the _groups member to include "standard" and
258 module test groups if appropriate.
259
Dan Talayco2c0dba32010-03-06 22:47:06 -0800260 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700261 @returns A dictionary from test module names to tuples of
262 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800263 """
264
Rich Lane943be672012-10-04 19:20:16 -0700265 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800266
Rich Lane943be672012-10-04 19:20:16 -0700267 for root, dirs, filenames in os.walk(config["test_dir"]):
268 # Iterate over each python file
269 for filename in fnmatch.filter(filenames, '[!.]*.py'):
270 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800271
Rich Lane943be672012-10-04 19:20:16 -0700272 try:
273 if sys.modules.has_key(modname):
274 mod = sys.modules[modname]
275 else:
276 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
277 except:
278 logging.warning("Could not import file " + filename)
279 raise
Rich Lane520e4152012-07-09 16:18:16 -0700280
Rich Lane943be672012-10-04 19:20:16 -0700281 # Find all testcases defined in the module
282 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700283 issubclass(v, unittest.TestCase) and
284 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700285 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800286 for (testname, test) in tests.items():
287 # Set default annotation values
288 if not hasattr(test, "_groups"):
289 test._groups = []
290 if not hasattr(test, "_nonstandard"):
291 test._nonstandard = False
292 if not hasattr(test, "_disabled"):
293 test._disabled = False
294
295 # Put test in its module's test group
296 if not test._disabled:
297 test._groups.append(modname)
298
299 # Put test in the standard test group
300 if not test._disabled and not test._nonstandard:
301 test._groups.append("standard")
302 test._groups.append("all") # backwards compatibility
303
Rich Lane943be672012-10-04 19:20:16 -0700304 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700305
Rich Lane943be672012-10-04 19:20:16 -0700306 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800307
Rich Lane5a9a1922013-01-11 14:29:30 -0800308def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700309 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800310 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800311 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800312 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700313 @param test_modules Same format as the output of load_test_modules.
314 @returns Same format as the output of load_test_modules.
315 """
316 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800317 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700318 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800319
320 if e.startswith('^'):
321 negated = True
322 e = e[1:]
323 else:
324 negated = False
325
Rich Lane15f64de2012-10-04 21:25:57 -0700326 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800327 for (testname, test) in tests.items():
328 if e in test._groups or e == "%s.%s" % (modname, testname):
329 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800330 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800331 if not hasattr(test, "_versions") or version in test._versions:
332 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800333 else:
334 if modname in result and testname in result[modname][1]:
335 del result[modname][1][testname]
336 if not result[modname][1]:
337 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800338 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800339
Rich Lane15f64de2012-10-04 21:25:57 -0700340 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800341 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800342
Rich Lane15f64de2012-10-04 21:25:57 -0700343 return result
344
Dan Talayco2c0dba32010-03-06 22:47:06 -0800345def die(msg, exit_val=1):
346 print msg
347 logging.critical(msg)
348 sys.exit(exit_val)
349
Dan Talayco79f36082010-03-11 16:53:53 -0800350def _space_to(n, str):
351 """
352 Generate a string of spaces to achieve width n given string str
353 If length of str >= n, return one space
354 """
355 spaces = n - len(str)
356 if spaces > 0:
357 return " " * spaces
358 return " "
359
Dan Talayco48370102010-03-03 15:17:33 -0800360#
361# Main script
362#
363
Rich Lane477f4812012-10-04 22:49:00 -0700364# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800365(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700366oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800367
Rich Laned7a85c42012-09-28 15:38:45 -0700368logging_setup(config)
369logging.info("++++++++ " + time.asctime() + " ++++++++")
370
Rich Lane9fd05682013-01-10 15:30:38 -0800371# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700372name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
373sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800374
375# HACK: testutils.py imports controller.py, which needs the ofp module
376import oftest.testutils
377
Rich Lanee284b6b2012-10-03 09:19:58 -0700378# Allow tests to import each other
379sys.path.append(config["test_dir"])
380
Rich Lanec76b09a2013-01-02 16:53:22 -0800381test_specs = args
382if config["test_spec"] != "":
383 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
384 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800385if config["test_file"] != None:
386 with open(config["test_file"], 'r') as f:
387 for line in f:
388 line, _, _ = line.partition('#') # remove comments
389 line = line.strip()
390 if line:
391 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800392if test_specs == []:
393 test_specs = ["standard"]
394
Rich Laned8e45482013-01-02 17:36:21 -0800395test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800396
397# Check if test list is requested; display and exit if so
398if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700399 mod_count = 0
400 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800401 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800402 print """\
403Tests are shown grouped by module. If a test is in any groups beyond "standard"
404and its module's group then they are shown in parentheses."""
405 print
406 print """\
407Tests marked with '*' are non-standard and may require vendor extensions or
408special switch configuration. These are not part of the "standard" test group."""
409 print
410 print """\
411Tests marked with '!' are disabled because they are experimental, special-purpose,
412or are too long to be run normally. These are not part of the "standard" test
413group or their module's test group."""
414 print
415 print "Tests marked (TP1) after name take --test-params including:"
416 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800417 print
418 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700419 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700420 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800421 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800422 start_str = " Module " + mod.__name__ + ": "
423 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700424 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700425 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800426 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700427 desc = desc.split('\n')[0]
428 except:
429 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800430 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800431 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800432 if groups:
433 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800434 if hasattr(test, "_versions"):
435 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800436 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
437 test._disabled and "!" or " ",
438 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700439 if len(start_str) > 22:
440 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800441 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700442 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800443 print
Rich Lane15f64de2012-10-04 21:25:57 -0700444 print "%d modules shown with a total of %d tests" % \
445 (mod_count, test_count)
446 print
Rich Lane37f42112013-01-03 13:41:49 -0800447 print "Test groups: %s" % (', '.join(sorted(all_groups)))
448
Dan Talayco2c0dba32010-03-06 22:47:06 -0800449 sys.exit(0)
450
Rich Lane5a9a1922013-01-11 14:29:30 -0800451test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800452
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700453# Check if test list is requested; display and exit if so
454if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700455 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700456 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800457 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700458 sys.exit(0)
459
Dan Talayco2c0dba32010-03-06 22:47:06 -0800460# Generate the test suite
461#@todo Decide if multiple suites are ever needed
462suite = unittest.TestSuite()
463
Rich Lane15f64de2012-10-04 21:25:57 -0700464for (modname, (mod, tests)) in test_modules.items():
465 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800466 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800467
Rich Lane51590f62012-10-09 15:06:29 -0700468# Allow platforms to import each other
469sys.path.append(config["platform_dir"])
470
Rich Lane8aebc5e2012-09-25 17:57:53 -0700471# Load the platform module
472platform_name = config["platform"]
473logging.info("Importing platform: " + platform_name)
474platform_mod = None
475try:
Rich Lane483e1542012-10-05 09:29:39 -0700476 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700477except:
478 logging.warn("Failed to import " + platform_name + " platform module")
479 raise
Dan Talayco48370102010-03-03 15:17:33 -0800480
481try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700482 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800483except:
484 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700485 raise
Dan Talayco48370102010-03-03 15:17:33 -0800486
487if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700488 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800489
490logging.debug("Configuration: " + str(config))
491logging.info("OF port map: " + str(config["port_map"]))
492
Rich Lanee55abf72012-07-26 20:11:42 -0700493oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700494oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700495
Rich Laneee57ad02012-07-13 15:40:36 -0700496if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700497 print "ERROR: Super-user privileges required. Please re-run with " \
498 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700499 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700500
Rich Lane8592bec2012-09-03 09:06:59 -0700501if config["random_seed"] is not None:
502 logging.info("Random seed: %d" % config["random_seed"])
503 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700504else:
505 # Generate random seed and report to log file
506 seed = random.randrange(100000000)
507 logging.info("Autogen random seed: %d" % seed)
508 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700509
Rich Lane5bd6cf92012-10-04 17:57:24 -0700510# Remove python's signal handler which raises KeyboardError. Exiting from an
511# exception waits for all threads to terminate which might not happen.
512signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700513
514if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800515 # Set up the dataplane
516 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700517 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800518 for of_port, ifname in config["port_map"].items():
519 oftest.dataplane_instance.port_add(ifname, of_port)
520
Dan Talaycoac25cf32010-07-20 14:08:28 -0700521 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800522 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700523 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700524 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700525 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700526 if oftest.testutils.skipped_test_count == 1: ts = " test"
527 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
528 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700529 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800530
531 # Shutdown the dataplane
532 oftest.dataplane_instance.kill()
533 oftest.dataplane_instance = None
534
Rich Lane50d42eb2012-07-16 11:57:03 -0700535 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700536 # exit(1) hangs sometimes
537 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700538 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700539 os._exit(1)