blob: a4aa5b493bcf5724b597167e5a69763207a22d24 [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 Lane472aaea2013-08-27 09:27:38 -0700235def pcap_setup(config):
236 """
237 Set up dataplane packet capturing based on config
238 """
239
240 if config["log_dir"] == None:
241 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
242 oftest.dataplane_instance.start_pcap(filename)
243 else:
244 # start_pcap is called per-test in base_tests
245 pass
246
247
Rich Lane943be672012-10-04 19:20:16 -0700248def load_test_modules(config):
249 """
250 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800251
Rich Lane943be672012-10-04 19:20:16 -0700252 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800253
Rich Lanecc45b8e2013-01-02 15:55:02 -0800254 Also updates the _groups member to include "standard" and
255 module test groups if appropriate.
256
Dan Talayco2c0dba32010-03-06 22:47:06 -0800257 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700258 @returns A dictionary from test module names to tuples of
259 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800260 """
261
Rich Lane943be672012-10-04 19:20:16 -0700262 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800263
Rich Lane943be672012-10-04 19:20:16 -0700264 for root, dirs, filenames in os.walk(config["test_dir"]):
265 # Iterate over each python file
266 for filename in fnmatch.filter(filenames, '[!.]*.py'):
267 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800268
Rich Lane943be672012-10-04 19:20:16 -0700269 try:
270 if sys.modules.has_key(modname):
271 mod = sys.modules[modname]
272 else:
273 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
274 except:
275 logging.warning("Could not import file " + filename)
276 raise
Rich Lane520e4152012-07-09 16:18:16 -0700277
Rich Lane943be672012-10-04 19:20:16 -0700278 # Find all testcases defined in the module
279 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700280 issubclass(v, unittest.TestCase) and
281 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700282 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800283 for (testname, test) in tests.items():
284 # Set default annotation values
285 if not hasattr(test, "_groups"):
286 test._groups = []
287 if not hasattr(test, "_nonstandard"):
288 test._nonstandard = False
289 if not hasattr(test, "_disabled"):
290 test._disabled = False
291
292 # Put test in its module's test group
293 if not test._disabled:
294 test._groups.append(modname)
295
296 # Put test in the standard test group
297 if not test._disabled and not test._nonstandard:
298 test._groups.append("standard")
299 test._groups.append("all") # backwards compatibility
300
Rich Lane943be672012-10-04 19:20:16 -0700301 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700302
Rich Lane943be672012-10-04 19:20:16 -0700303 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800304
Rich Lane5a9a1922013-01-11 14:29:30 -0800305def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700306 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800307 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800308 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800309 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700310 @param test_modules Same format as the output of load_test_modules.
311 @returns Same format as the output of load_test_modules.
312 """
313 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800314 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700315 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800316
317 if e.startswith('^'):
318 negated = True
319 e = e[1:]
320 else:
321 negated = False
322
Rich Lane15f64de2012-10-04 21:25:57 -0700323 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800324 for (testname, test) in tests.items():
325 if e in test._groups or e == "%s.%s" % (modname, testname):
326 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800327 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800328 if not hasattr(test, "_versions") or version in test._versions:
329 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800330 else:
331 if modname in result and testname in result[modname][1]:
332 del result[modname][1][testname]
333 if not result[modname][1]:
334 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800335 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800336
Rich Lane15f64de2012-10-04 21:25:57 -0700337 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800338 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800339
Rich Lane15f64de2012-10-04 21:25:57 -0700340 return result
341
Dan Talayco2c0dba32010-03-06 22:47:06 -0800342def die(msg, exit_val=1):
343 print msg
344 logging.critical(msg)
345 sys.exit(exit_val)
346
Dan Talayco79f36082010-03-11 16:53:53 -0800347def _space_to(n, str):
348 """
349 Generate a string of spaces to achieve width n given string str
350 If length of str >= n, return one space
351 """
352 spaces = n - len(str)
353 if spaces > 0:
354 return " " * spaces
355 return " "
356
Dan Talayco48370102010-03-03 15:17:33 -0800357#
358# Main script
359#
360
Rich Lane477f4812012-10-04 22:49:00 -0700361# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800362(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700363oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800364
Rich Laned7a85c42012-09-28 15:38:45 -0700365logging_setup(config)
366logging.info("++++++++ " + time.asctime() + " ++++++++")
367
Rich Lane9fd05682013-01-10 15:30:38 -0800368# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700369name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
370sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800371
372# HACK: testutils.py imports controller.py, which needs the ofp module
373import oftest.testutils
374
Rich Lanee284b6b2012-10-03 09:19:58 -0700375# Allow tests to import each other
376sys.path.append(config["test_dir"])
377
Rich Lanec76b09a2013-01-02 16:53:22 -0800378test_specs = args
379if config["test_spec"] != "":
380 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
381 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800382if config["test_file"] != None:
383 with open(config["test_file"], 'r') as f:
384 for line in f:
385 line, _, _ = line.partition('#') # remove comments
386 line = line.strip()
387 if line:
388 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800389if test_specs == []:
390 test_specs = ["standard"]
391
Rich Laned8e45482013-01-02 17:36:21 -0800392test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800393
394# Check if test list is requested; display and exit if so
395if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700396 mod_count = 0
397 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800398 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800399 print """\
400Tests are shown grouped by module. If a test is in any groups beyond "standard"
401and its module's group then they are shown in parentheses."""
402 print
403 print """\
404Tests marked with '*' are non-standard and may require vendor extensions or
405special switch configuration. These are not part of the "standard" test group."""
406 print
407 print """\
408Tests marked with '!' are disabled because they are experimental, special-purpose,
409or are too long to be run normally. These are not part of the "standard" test
410group or their module's test group."""
411 print
412 print "Tests marked (TP1) after name take --test-params including:"
413 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800414 print
415 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700416 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700417 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800418 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800419 start_str = " Module " + mod.__name__ + ": "
420 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700421 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700422 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800423 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700424 desc = desc.split('\n')[0]
425 except:
426 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800427 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800428 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800429 if groups:
430 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800431 if hasattr(test, "_versions"):
432 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800433 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
434 test._disabled and "!" or " ",
435 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700436 if len(start_str) > 22:
437 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800438 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700439 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800440 print
Rich Lane15f64de2012-10-04 21:25:57 -0700441 print "%d modules shown with a total of %d tests" % \
442 (mod_count, test_count)
443 print
Rich Lane37f42112013-01-03 13:41:49 -0800444 print "Test groups: %s" % (', '.join(sorted(all_groups)))
445
Dan Talayco2c0dba32010-03-06 22:47:06 -0800446 sys.exit(0)
447
Rich Lane5a9a1922013-01-11 14:29:30 -0800448test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800449
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700450# Check if test list is requested; display and exit if so
451if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700452 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700453 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800454 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700455 sys.exit(0)
456
Dan Talayco2c0dba32010-03-06 22:47:06 -0800457# Generate the test suite
458#@todo Decide if multiple suites are ever needed
459suite = unittest.TestSuite()
460
Rich Lane15f64de2012-10-04 21:25:57 -0700461for (modname, (mod, tests)) in test_modules.items():
462 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800463 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800464
Rich Lane51590f62012-10-09 15:06:29 -0700465# Allow platforms to import each other
466sys.path.append(config["platform_dir"])
467
Rich Lane8aebc5e2012-09-25 17:57:53 -0700468# Load the platform module
469platform_name = config["platform"]
470logging.info("Importing platform: " + platform_name)
471platform_mod = None
472try:
Rich Lane483e1542012-10-05 09:29:39 -0700473 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700474except:
475 logging.warn("Failed to import " + platform_name + " platform module")
476 raise
Dan Talayco48370102010-03-03 15:17:33 -0800477
478try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700479 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800480except:
481 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700482 raise
Dan Talayco48370102010-03-03 15:17:33 -0800483
484if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700485 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800486
487logging.debug("Configuration: " + str(config))
488logging.info("OF port map: " + str(config["port_map"]))
489
Rich Lanee55abf72012-07-26 20:11:42 -0700490oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700491oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700492
Rich Laneee57ad02012-07-13 15:40:36 -0700493if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700494 print "ERROR: Super-user privileges required. Please re-run with " \
495 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700496 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700497
Rich Lane8592bec2012-09-03 09:06:59 -0700498if config["random_seed"] is not None:
499 logging.info("Random seed: %d" % config["random_seed"])
500 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700501else:
502 # Generate random seed and report to log file
503 seed = random.randrange(100000000)
504 logging.info("Autogen random seed: %d" % seed)
505 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700506
Rich Lane5bd6cf92012-10-04 17:57:24 -0700507# Remove python's signal handler which raises KeyboardError. Exiting from an
508# exception waits for all threads to terminate which might not happen.
509signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700510
511if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800512 # Set up the dataplane
513 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700514 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800515 for of_port, ifname in config["port_map"].items():
516 oftest.dataplane_instance.port_add(ifname, of_port)
517
Dan Talaycoac25cf32010-07-20 14:08:28 -0700518 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800519 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700520 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700521 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700522 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700523 if oftest.testutils.skipped_test_count == 1: ts = " test"
524 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
525 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700526 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800527
528 # Shutdown the dataplane
529 oftest.dataplane_instance.kill()
530 oftest.dataplane_instance = None
531
Rich Lane50d42eb2012-07-16 11:57:03 -0700532 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700533 # exit(1) hangs sometimes
534 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700535 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700536 os._exit(1)