blob: 9d5df6aab2cdcb2123212db7c2fd9cdf30591572 [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",
Rich Lane9631f002014-03-21 18:05:16 -070080 "profile" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080081
82 # Test behavior options
83 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070084 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070085 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070086 "default_timeout" : 2.0,
87 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000088 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070089 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000090 "disable_ipv6" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080091
92 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080093 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080094}
95
Rich Lane95f078b2013-01-06 13:24:58 -080096def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080097 """
98 Set up the configuration including parsing the arguments
99
Dan Talayco48370102010-03-03 15:17:33 -0800100 @return A pair (config, args) where config is an config
101 object and args is any additional arguments from the command line
102 """
103
Rich Lane4113a582013-01-03 10:13:02 -0800104 usage = "usage: %prog [options] (test|group)..."
105
Rich Lane95f078b2013-01-06 13:24:58 -0800106 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800107OFTest is a framework and set of tests for validating OpenFlow switches.
108
109The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700110connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800111the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
112dataplane.
113
114If no positional arguments are given then OFTest will run all tests that
115depend only on standard OpenFlow 1.0. Otherwise each positional argument
116is interpreted as either a test name or a test group name. The union of
117these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800118--list option. Tests and groups can be subtracted from the result by
119prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800120"""
121
Rich Lane2d6d4822013-01-08 10:49:16 -0800122 # Parse --interface
123 def check_interface(option, opt, value):
124 try:
125 ofport, interface = value.split('@', 1)
126 ofport = int(ofport)
127 except ValueError:
128 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
129 return (ofport, interface)
130
131 class Option(optparse.Option):
132 TYPES = optparse.Option.TYPES + ("interface",)
133 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
134 TYPE_CHECKER["interface"] = check_interface
135
Rich Lane95f078b2013-01-06 13:24:58 -0800136 parser = optparse.OptionParser(version="%prog 0.1",
137 usage=usage,
138 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800139 formatter=oftest.help_formatter.HelpFormatter(),
140 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800141
142 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800143 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800144
Dan Talayco2c0dba32010-03-06 22:47:06 -0800145 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700146 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800147 parser.add_option("--list-test-names", action='store_true',
148 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700149 parser.add_option("--allow-user", action="store_true",
150 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800151
152 group = optparse.OptionGroup(parser, "Test selection options")
153 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
154 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
155 group.add_option("--test-dir", type="string", help="Directory containing tests")
156 parser.add_option_group(group)
157
158 group = optparse.OptionGroup(parser, "Switch connection options")
159 group.add_option("-H", "--host", dest="controller_host",
160 help="IP address to listen on (default %default)")
161 group.add_option("-p", "--port", dest="controller_port",
162 type="int", help="Port number to listen on (default %default)")
163 group.add_option("-S", "--switch-ip", dest="switch_ip",
164 help="If set, actively connect to this switch by IP")
165 group.add_option("-P", "--platform", help="Platform module name (default %default)")
166 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
167 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800168 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
169 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 -0700170 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800171 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800172 parser.add_option_group(group)
173
174 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700175 group.add_option("--log-file", help="Name of log file (default %default)")
176 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800177 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
178 group.add_option("--debug", choices=dbg_lvl_names,
179 help="Debug lvl: debug, info, warning, error, critical (default %default)")
180 group.add_option("-v", "--verbose", action="store_const", dest="debug",
181 const="verbose", help="Shortcut for --debug=verbose")
182 group.add_option("-q", "--quiet", action="store_const", dest="debug",
183 const="warning", help="Shortcut for --debug=warning")
Rich Lane9631f002014-03-21 18:05:16 -0700184 group.add_option("--profile", action="store_true", help="Write Python profile to profile.out")
Rich Lane95f078b2013-01-06 13:24:58 -0800185 parser.add_option_group(group)
186
187 group = optparse.OptionGroup(parser, "Test behavior options")
188 group.add_option("--relax", action="store_true",
189 help="Relax packet match checks allowing other packets")
190 test_params_help = """Set test parameters: key=val;... (see --list)
191 """
192 group.add_option("-t", "--test-params", help=test_params_help)
193 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700194 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700195 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700196 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700197 group.add_option("--default-negative-timeout", type=float,
198 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800199 group.add_option("--minsize", type="int",
200 help="Minimum allowable packet size on the dataplane.")
201 group.add_option("--random-seed", type="int",
202 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000203 group.add_option("--disable-ipv6", action="store_true",
204 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800205 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000206
Dan Talayco48370102010-03-03 15:17:33 -0800207 # Might need this if other parsers want command line
208 # parser.allow_interspersed_args = False
209 (options, args) = parser.parse_args()
210
Rich Lane74b13d12013-05-03 17:58:50 -0700211 # If --test-dir wasn't given, pick one based on the OpenFlow version
212 if options.test_dir == None:
213 if options.openflow_version == "1.0":
214 options.test_dir = os.path.join(root_dir, "tests")
215 else:
216 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
217
Rich Lane95f078b2013-01-06 13:24:58 -0800218 # Convert options from a Namespace to a plain dictionary
219 config = config_default.copy()
220 for key in config.keys():
221 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800222
223 return (config, args)
224
225def logging_setup(config):
226 """
227 Set up logging based on config
228 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700229
230 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
231
232 if config["log_dir"] != None:
233 if os.path.exists(config["log_dir"]):
234 import shutil
235 shutil.rmtree(config["log_dir"])
236 os.makedirs(config["log_dir"])
237 else:
238 if os.path.exists(config["log_file"]):
239 os.remove(config["log_file"])
240
241 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800242
Rich Lane472aaea2013-08-27 09:27:38 -0700243def pcap_setup(config):
244 """
245 Set up dataplane packet capturing based on config
246 """
247
248 if config["log_dir"] == None:
249 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
250 oftest.dataplane_instance.start_pcap(filename)
251 else:
252 # start_pcap is called per-test in base_tests
253 pass
254
255
Rich Lane943be672012-10-04 19:20:16 -0700256def load_test_modules(config):
257 """
258 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800259
Rich Lane943be672012-10-04 19:20:16 -0700260 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800261
Rich Lanecc45b8e2013-01-02 15:55:02 -0800262 Also updates the _groups member to include "standard" and
263 module test groups if appropriate.
264
Dan Talayco2c0dba32010-03-06 22:47:06 -0800265 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700266 @returns A dictionary from test module names to tuples of
267 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800268 """
269
Rich Lane943be672012-10-04 19:20:16 -0700270 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800271
Rich Lane943be672012-10-04 19:20:16 -0700272 for root, dirs, filenames in os.walk(config["test_dir"]):
273 # Iterate over each python file
274 for filename in fnmatch.filter(filenames, '[!.]*.py'):
275 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800276
Rich Lane943be672012-10-04 19:20:16 -0700277 try:
278 if sys.modules.has_key(modname):
279 mod = sys.modules[modname]
280 else:
281 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
282 except:
283 logging.warning("Could not import file " + filename)
284 raise
Rich Lane520e4152012-07-09 16:18:16 -0700285
Rich Lane943be672012-10-04 19:20:16 -0700286 # Find all testcases defined in the module
287 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700288 issubclass(v, unittest.TestCase) and
289 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700290 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800291 for (testname, test) in tests.items():
292 # Set default annotation values
293 if not hasattr(test, "_groups"):
294 test._groups = []
295 if not hasattr(test, "_nonstandard"):
296 test._nonstandard = False
297 if not hasattr(test, "_disabled"):
298 test._disabled = False
299
300 # Put test in its module's test group
301 if not test._disabled:
302 test._groups.append(modname)
303
304 # Put test in the standard test group
305 if not test._disabled and not test._nonstandard:
306 test._groups.append("standard")
307 test._groups.append("all") # backwards compatibility
308
Rich Lane943be672012-10-04 19:20:16 -0700309 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700310
Rich Lane943be672012-10-04 19:20:16 -0700311 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800312
Rich Lane5a9a1922013-01-11 14:29:30 -0800313def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700314 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800315 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800316 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800317 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700318 @param test_modules Same format as the output of load_test_modules.
319 @returns Same format as the output of load_test_modules.
320 """
321 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800322 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700323 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800324
325 if e.startswith('^'):
326 negated = True
327 e = e[1:]
328 else:
329 negated = False
330
Rich Lane15f64de2012-10-04 21:25:57 -0700331 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800332 for (testname, test) in tests.items():
333 if e in test._groups or e == "%s.%s" % (modname, testname):
334 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800335 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800336 if not hasattr(test, "_versions") or version in test._versions:
337 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800338 else:
339 if modname in result and testname in result[modname][1]:
340 del result[modname][1][testname]
341 if not result[modname][1]:
342 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800343 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800344
Rich Lane15f64de2012-10-04 21:25:57 -0700345 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800346 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800347
Rich Lane15f64de2012-10-04 21:25:57 -0700348 return result
349
Dan Talayco2c0dba32010-03-06 22:47:06 -0800350def die(msg, exit_val=1):
351 print msg
352 logging.critical(msg)
353 sys.exit(exit_val)
354
Dan Talayco79f36082010-03-11 16:53:53 -0800355def _space_to(n, str):
356 """
357 Generate a string of spaces to achieve width n given string str
358 If length of str >= n, return one space
359 """
360 spaces = n - len(str)
361 if spaces > 0:
362 return " " * spaces
363 return " "
364
Dan Talayco48370102010-03-03 15:17:33 -0800365#
366# Main script
367#
368
Rich Lane477f4812012-10-04 22:49:00 -0700369# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800370(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700371oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800372
Rich Laned7a85c42012-09-28 15:38:45 -0700373logging_setup(config)
374logging.info("++++++++ " + time.asctime() + " ++++++++")
375
Rich Lane9fd05682013-01-10 15:30:38 -0800376# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700377name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
378sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800379
380# HACK: testutils.py imports controller.py, which needs the ofp module
381import oftest.testutils
382
Rich Lanee284b6b2012-10-03 09:19:58 -0700383# Allow tests to import each other
384sys.path.append(config["test_dir"])
385
Rich Lanec76b09a2013-01-02 16:53:22 -0800386test_specs = args
387if config["test_spec"] != "":
388 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
389 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800390if config["test_file"] != None:
391 with open(config["test_file"], 'r') as f:
392 for line in f:
393 line, _, _ = line.partition('#') # remove comments
394 line = line.strip()
395 if line:
396 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800397if test_specs == []:
398 test_specs = ["standard"]
399
Rich Laned8e45482013-01-02 17:36:21 -0800400test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800401
402# Check if test list is requested; display and exit if so
403if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700404 mod_count = 0
405 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800406 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800407 print """\
408Tests are shown grouped by module. If a test is in any groups beyond "standard"
409and its module's group then they are shown in parentheses."""
410 print
411 print """\
412Tests marked with '*' are non-standard and may require vendor extensions or
413special switch configuration. These are not part of the "standard" test group."""
414 print
415 print """\
416Tests marked with '!' are disabled because they are experimental, special-purpose,
417or are too long to be run normally. These are not part of the "standard" test
418group or their module's test group."""
419 print
420 print "Tests marked (TP1) after name take --test-params including:"
421 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800422 print
423 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700424 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700425 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800426 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800427 start_str = " Module " + mod.__name__ + ": "
428 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700429 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700430 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800431 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700432 desc = desc.split('\n')[0]
433 except:
434 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800435 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800436 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800437 if groups:
438 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800439 if hasattr(test, "_versions"):
440 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800441 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
442 test._disabled and "!" or " ",
443 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700444 if len(start_str) > 22:
445 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800446 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700447 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800448 print
Rich Lane15f64de2012-10-04 21:25:57 -0700449 print "%d modules shown with a total of %d tests" % \
450 (mod_count, test_count)
451 print
Rich Lane37f42112013-01-03 13:41:49 -0800452 print "Test groups: %s" % (', '.join(sorted(all_groups)))
453
Dan Talayco2c0dba32010-03-06 22:47:06 -0800454 sys.exit(0)
455
Rich Lane5a9a1922013-01-11 14:29:30 -0800456test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800457
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700458# Check if test list is requested; display and exit if so
459if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700460 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700461 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800462 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700463 sys.exit(0)
464
Dan Talayco2c0dba32010-03-06 22:47:06 -0800465# Generate the test suite
466#@todo Decide if multiple suites are ever needed
467suite = unittest.TestSuite()
468
Rich Lane15f64de2012-10-04 21:25:57 -0700469for (modname, (mod, tests)) in test_modules.items():
470 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800471 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800472
Rich Lane51590f62012-10-09 15:06:29 -0700473# Allow platforms to import each other
474sys.path.append(config["platform_dir"])
475
Rich Lane8aebc5e2012-09-25 17:57:53 -0700476# Load the platform module
477platform_name = config["platform"]
478logging.info("Importing platform: " + platform_name)
479platform_mod = None
480try:
Rich Lane483e1542012-10-05 09:29:39 -0700481 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700482except:
483 logging.warn("Failed to import " + platform_name + " platform module")
484 raise
Dan Talayco48370102010-03-03 15:17:33 -0800485
486try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700487 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800488except:
489 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700490 raise
Dan Talayco48370102010-03-03 15:17:33 -0800491
492if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700493 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800494
495logging.debug("Configuration: " + str(config))
496logging.info("OF port map: " + str(config["port_map"]))
497
Rich Lanee55abf72012-07-26 20:11:42 -0700498oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700499oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700500oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700501
Rich Laneee57ad02012-07-13 15:40:36 -0700502if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700503 print "ERROR: Super-user privileges required. Please re-run with " \
504 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700505 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700506
Rich Lane8592bec2012-09-03 09:06:59 -0700507if config["random_seed"] is not None:
508 logging.info("Random seed: %d" % config["random_seed"])
509 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700510else:
511 # Generate random seed and report to log file
512 seed = random.randrange(100000000)
513 logging.info("Autogen random seed: %d" % seed)
514 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700515
Rich Lane5bd6cf92012-10-04 17:57:24 -0700516# Remove python's signal handler which raises KeyboardError. Exiting from an
517# exception waits for all threads to terminate which might not happen.
518signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700519
520if __name__ == "__main__":
Rich Lane9631f002014-03-21 18:05:16 -0700521 if config["profile"]:
522 import cProfile
523 profiler = cProfile.Profile()
524 profiler.enable()
525
Rich Lane2c7812c2012-12-27 17:52:23 -0800526 # Set up the dataplane
527 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700528 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800529 for of_port, ifname in config["port_map"].items():
530 oftest.dataplane_instance.port_add(ifname, of_port)
531
Dan Talaycoac25cf32010-07-20 14:08:28 -0700532 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800533 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700534 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700535 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700536 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700537 if oftest.testutils.skipped_test_count == 1: ts = " test"
538 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
539 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700540 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800541
542 # Shutdown the dataplane
543 oftest.dataplane_instance.kill()
544 oftest.dataplane_instance = None
545
Rich Lane9631f002014-03-21 18:05:16 -0700546 if config["profile"]:
547 profiler.disable()
548 profiler.dump_stats("profile.out")
549
Rich Lane50d42eb2012-07-16 11:57:03 -0700550 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700551 # exit(1) hangs sometimes
552 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700553 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700554 os._exit(1)