blob: 4ca4d6418cf57b3d1f9cfe26e3470e9ad311dbdd [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 Talayco02eca0b2010-04-15 16:09:43 -070041try:
42 import scapy.all as scapy
43except:
44 try:
45 import scapy as scapy
46 except:
47 sys.exit("Need to install scapy for packet parsing")
48
Dan Talayco48370102010-03-03 15:17:33 -080049##@var DEBUG_LEVELS
50# Map from strings to debugging levels
51DEBUG_LEVELS = {
52 'debug' : logging.DEBUG,
53 'verbose' : logging.DEBUG,
54 'info' : logging.INFO,
55 'warning' : logging.WARNING,
56 'warn' : logging.WARNING,
57 'error' : logging.ERROR,
58 'critical' : logging.CRITICAL
59}
60
Dan Talayco48370102010-03-03 15:17:33 -080061##@var config_default
62# The default configuration dictionary for OFT
63config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -080064 # Miscellaneous options
65 "list" : False,
66 "list_test_names" : False,
67 "allow_user" : False,
68
69 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080070 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080071 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070072 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080073
74 # Switch connection options
75 "controller_host" : "0.0.0.0", # For passive bind
76 "controller_port" : 6633,
77 "switch_ip" : None, # If not none, actively connect to switch
Rich Lane15f26322013-01-08 11:23:24 -080078 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080079 "platform_args" : None,
80 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080081 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080082 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080083
84 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080085 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080086 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080087 "debug" : "verbose",
88
89 # Test behavior options
90 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070091 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070092 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070093 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000094 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070095 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080096
97 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080098 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080099}
100
Rich Lane95f078b2013-01-06 13:24:58 -0800101def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800102 """
103 Set up the configuration including parsing the arguments
104
Dan Talayco48370102010-03-03 15:17:33 -0800105 @return A pair (config, args) where config is an config
106 object and args is any additional arguments from the command line
107 """
108
Rich Lane4113a582013-01-03 10:13:02 -0800109 usage = "usage: %prog [options] (test|group)..."
110
Rich Lane95f078b2013-01-06 13:24:58 -0800111 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800112OFTest is a framework and set of tests for validating OpenFlow switches.
113
114The default configuration assumes that an OpenFlow 1.0 switch is attempting to
115connect to a controller on the machine running OFTest, port 6633. Additionally,
116the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
117dataplane.
118
119If no positional arguments are given then OFTest will run all tests that
120depend only on standard OpenFlow 1.0. Otherwise each positional argument
121is interpreted as either a test name or a test group name. The union of
122these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800123--list option. Tests and groups can be subtracted from the result by
124prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800125"""
126
Rich Lane2d6d4822013-01-08 10:49:16 -0800127 # Parse --interface
128 def check_interface(option, opt, value):
129 try:
130 ofport, interface = value.split('@', 1)
131 ofport = int(ofport)
132 except ValueError:
133 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
134 return (ofport, interface)
135
136 class Option(optparse.Option):
137 TYPES = optparse.Option.TYPES + ("interface",)
138 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
139 TYPE_CHECKER["interface"] = check_interface
140
Rich Lane95f078b2013-01-06 13:24:58 -0800141 parser = optparse.OptionParser(version="%prog 0.1",
142 usage=usage,
143 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800144 formatter=oftest.help_formatter.HelpFormatter(),
145 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800146
147 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800148 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800149
Dan Talayco2c0dba32010-03-06 22:47:06 -0800150 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700151 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800152 parser.add_option("--list-test-names", action='store_true',
153 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700154 parser.add_option("--allow-user", action="store_true",
155 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800156
157 group = optparse.OptionGroup(parser, "Test selection options")
158 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
159 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
160 group.add_option("--test-dir", type="string", help="Directory containing tests")
161 parser.add_option_group(group)
162
163 group = optparse.OptionGroup(parser, "Switch connection options")
164 group.add_option("-H", "--host", dest="controller_host",
165 help="IP address to listen on (default %default)")
166 group.add_option("-p", "--port", dest="controller_port",
167 type="int", help="Port number to listen on (default %default)")
168 group.add_option("-S", "--switch-ip", dest="switch_ip",
169 help="If set, actively connect to this switch by IP")
170 group.add_option("-P", "--platform", help="Platform module name (default %default)")
171 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
172 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800173 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
174 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 -0700175 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800176 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800177 parser.add_option_group(group)
178
179 group = optparse.OptionGroup(parser, "Logging options")
180 group.add_option("--log-file",
181 help="Name of log file, empty string to log to console (default %default)")
182 group.add_option("--log-append", action="store_true",
183 help="Do not delete log file if specified")
184 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
185 group.add_option("--debug", choices=dbg_lvl_names,
186 help="Debug lvl: debug, info, warning, error, critical (default %default)")
187 group.add_option("-v", "--verbose", action="store_const", dest="debug",
188 const="verbose", help="Shortcut for --debug=verbose")
189 group.add_option("-q", "--quiet", action="store_const", dest="debug",
190 const="warning", help="Shortcut for --debug=warning")
191 parser.add_option_group(group)
192
193 group = optparse.OptionGroup(parser, "Test behavior options")
194 group.add_option("--relax", action="store_true",
195 help="Relax packet match checks allowing other packets")
196 test_params_help = """Set test parameters: key=val;... (see --list)
197 """
198 group.add_option("-t", "--test-params", help=test_params_help)
199 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700200 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800201 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700202 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800203 group.add_option("--minsize", type="int",
204 help="Minimum allowable packet size on the dataplane.")
205 group.add_option("--random-seed", type="int",
206 help="Random number generator seed")
207 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000208
Dan Talayco48370102010-03-03 15:17:33 -0800209 # Might need this if other parsers want command line
210 # parser.allow_interspersed_args = False
211 (options, args) = parser.parse_args()
212
Rich Lane74b13d12013-05-03 17:58:50 -0700213 # If --test-dir wasn't given, pick one based on the OpenFlow version
214 if options.test_dir == None:
215 if options.openflow_version == "1.0":
216 options.test_dir = os.path.join(root_dir, "tests")
217 else:
218 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
219
Rich Lane95f078b2013-01-06 13:24:58 -0800220 # Convert options from a Namespace to a plain dictionary
221 config = config_default.copy()
222 for key in config.keys():
223 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800224
225 return (config, args)
226
227def logging_setup(config):
228 """
229 Set up logging based on config
230 """
Rich Lane71d887d2012-12-22 17:05:13 -0800231 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800232 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800233 _mode = config["log_append"] and "a" or "w"
Jeffrey Townsendedb12d32013-02-06 14:52:30 -0800234 if config['log_file'] in [ "-", "stderr" ]:
235 config['log_file'] = None
236 logging.basicConfig(filename=config['log_file'],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800237 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800238 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800239 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800240
Rich Lane943be672012-10-04 19:20:16 -0700241def load_test_modules(config):
242 """
243 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800244
Rich Lane943be672012-10-04 19:20:16 -0700245 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800246
Rich Lanecc45b8e2013-01-02 15:55:02 -0800247 Also updates the _groups member to include "standard" and
248 module test groups if appropriate.
249
Dan Talayco2c0dba32010-03-06 22:47:06 -0800250 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700251 @returns A dictionary from test module names to tuples of
252 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800253 """
254
Rich Lane943be672012-10-04 19:20:16 -0700255 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800256
Rich Lane943be672012-10-04 19:20:16 -0700257 for root, dirs, filenames in os.walk(config["test_dir"]):
258 # Iterate over each python file
259 for filename in fnmatch.filter(filenames, '[!.]*.py'):
260 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800261
Rich Lane943be672012-10-04 19:20:16 -0700262 try:
263 if sys.modules.has_key(modname):
264 mod = sys.modules[modname]
265 else:
266 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
267 except:
268 logging.warning("Could not import file " + filename)
269 raise
Rich Lane520e4152012-07-09 16:18:16 -0700270
Rich Lane943be672012-10-04 19:20:16 -0700271 # Find all testcases defined in the module
272 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
273 issubclass(v, unittest.TestCase))
274 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800275 for (testname, test) in tests.items():
276 # Set default annotation values
277 if not hasattr(test, "_groups"):
278 test._groups = []
279 if not hasattr(test, "_nonstandard"):
280 test._nonstandard = False
281 if not hasattr(test, "_disabled"):
282 test._disabled = False
283
284 # Put test in its module's test group
285 if not test._disabled:
286 test._groups.append(modname)
287
288 # Put test in the standard test group
289 if not test._disabled and not test._nonstandard:
290 test._groups.append("standard")
291 test._groups.append("all") # backwards compatibility
292
Rich Lane943be672012-10-04 19:20:16 -0700293 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700294
Rich Lane943be672012-10-04 19:20:16 -0700295 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800296
Rich Lane5a9a1922013-01-11 14:29:30 -0800297def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700298 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800299 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800300 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800301 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700302 @param test_modules Same format as the output of load_test_modules.
303 @returns Same format as the output of load_test_modules.
304 """
305 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800306 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700307 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800308
309 if e.startswith('^'):
310 negated = True
311 e = e[1:]
312 else:
313 negated = False
314
Rich Lane15f64de2012-10-04 21:25:57 -0700315 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800316 for (testname, test) in tests.items():
317 if e in test._groups or e == "%s.%s" % (modname, testname):
318 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800319 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800320 if not hasattr(test, "_versions") or version in test._versions:
321 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800322 else:
323 if modname in result and testname in result[modname][1]:
324 del result[modname][1][testname]
325 if not result[modname][1]:
326 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800327 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800328
Rich Lane15f64de2012-10-04 21:25:57 -0700329 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800330 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800331
Rich Lane15f64de2012-10-04 21:25:57 -0700332 return result
333
Dan Talayco2c0dba32010-03-06 22:47:06 -0800334def die(msg, exit_val=1):
335 print msg
336 logging.critical(msg)
337 sys.exit(exit_val)
338
Dan Talayco79f36082010-03-11 16:53:53 -0800339def _space_to(n, str):
340 """
341 Generate a string of spaces to achieve width n given string str
342 If length of str >= n, return one space
343 """
344 spaces = n - len(str)
345 if spaces > 0:
346 return " " * spaces
347 return " "
348
Dan Talayco48370102010-03-03 15:17:33 -0800349#
350# Main script
351#
352
Rich Lane477f4812012-10-04 22:49:00 -0700353# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800354(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700355oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800356
Rich Laned7a85c42012-09-28 15:38:45 -0700357logging_setup(config)
358logging.info("++++++++ " + time.asctime() + " ++++++++")
359
Rich Lane9fd05682013-01-10 15:30:38 -0800360# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700361name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
362sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800363
364# HACK: testutils.py imports controller.py, which needs the ofp module
365import oftest.testutils
366
Rich Lanee284b6b2012-10-03 09:19:58 -0700367# Allow tests to import each other
368sys.path.append(config["test_dir"])
369
Rich Lanec76b09a2013-01-02 16:53:22 -0800370test_specs = args
371if config["test_spec"] != "":
372 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
373 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800374if config["test_file"] != None:
375 with open(config["test_file"], 'r') as f:
376 for line in f:
377 line, _, _ = line.partition('#') # remove comments
378 line = line.strip()
379 if line:
380 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800381if test_specs == []:
382 test_specs = ["standard"]
383
Rich Laned8e45482013-01-02 17:36:21 -0800384test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800385
386# Check if test list is requested; display and exit if so
387if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700388 mod_count = 0
389 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800390 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800391 print """\
392Tests are shown grouped by module. If a test is in any groups beyond "standard"
393and its module's group then they are shown in parentheses."""
394 print
395 print """\
396Tests marked with '*' are non-standard and may require vendor extensions or
397special switch configuration. These are not part of the "standard" test group."""
398 print
399 print """\
400Tests marked with '!' are disabled because they are experimental, special-purpose,
401or are too long to be run normally. These are not part of the "standard" test
402group or their module's test group."""
403 print
404 print "Tests marked (TP1) after name take --test-params including:"
405 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800406 print
407 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700408 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700409 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800410 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800411 start_str = " Module " + mod.__name__ + ": "
412 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700413 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700414 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800415 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700416 desc = desc.split('\n')[0]
417 except:
418 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800419 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800420 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800421 if groups:
422 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800423 if hasattr(test, "_versions"):
424 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800425 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
426 test._disabled and "!" or " ",
427 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700428 if len(start_str) > 22:
429 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800430 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700431 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800432 print
Rich Lane15f64de2012-10-04 21:25:57 -0700433 print "%d modules shown with a total of %d tests" % \
434 (mod_count, test_count)
435 print
Rich Lane37f42112013-01-03 13:41:49 -0800436 print "Test groups: %s" % (', '.join(sorted(all_groups)))
437
Dan Talayco2c0dba32010-03-06 22:47:06 -0800438 sys.exit(0)
439
Rich Lane5a9a1922013-01-11 14:29:30 -0800440test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800441
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700442# Check if test list is requested; display and exit if so
443if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700444 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700445 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800446 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700447 sys.exit(0)
448
Dan Talayco2c0dba32010-03-06 22:47:06 -0800449# Generate the test suite
450#@todo Decide if multiple suites are ever needed
451suite = unittest.TestSuite()
452
Rich Lane15f64de2012-10-04 21:25:57 -0700453for (modname, (mod, tests)) in test_modules.items():
454 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800455 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800456
Rich Lane51590f62012-10-09 15:06:29 -0700457# Allow platforms to import each other
458sys.path.append(config["platform_dir"])
459
Rich Lane8aebc5e2012-09-25 17:57:53 -0700460# Load the platform module
461platform_name = config["platform"]
462logging.info("Importing platform: " + platform_name)
463platform_mod = None
464try:
Rich Lane483e1542012-10-05 09:29:39 -0700465 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700466except:
467 logging.warn("Failed to import " + platform_name + " platform module")
468 raise
Dan Talayco48370102010-03-03 15:17:33 -0800469
470try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700471 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800472except:
473 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700474 raise
Dan Talayco48370102010-03-03 15:17:33 -0800475
476if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700477 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800478
479logging.debug("Configuration: " + str(config))
480logging.info("OF port map: " + str(config["port_map"]))
481
Rich Lanee55abf72012-07-26 20:11:42 -0700482oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700483oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700484
Rich Laneee57ad02012-07-13 15:40:36 -0700485if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700486 print "ERROR: Super-user privileges required. Please re-run with " \
487 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700488 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700489
Rich Lane8592bec2012-09-03 09:06:59 -0700490if config["random_seed"] is not None:
491 logging.info("Random seed: %d" % config["random_seed"])
492 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700493else:
494 # Generate random seed and report to log file
495 seed = random.randrange(100000000)
496 logging.info("Autogen random seed: %d" % seed)
497 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700498
Rich Lane5bd6cf92012-10-04 17:57:24 -0700499# Remove python's signal handler which raises KeyboardError. Exiting from an
500# exception waits for all threads to terminate which might not happen.
501signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700502
503if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800504 # Set up the dataplane
505 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
506 for of_port, ifname in config["port_map"].items():
507 oftest.dataplane_instance.port_add(ifname, of_port)
508
Dan Talaycoac25cf32010-07-20 14:08:28 -0700509 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800510 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700511 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700512 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700513 if oftest.testutils.skipped_test_count == 1: ts = " test"
514 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
515 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700516 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800517
518 # Shutdown the dataplane
519 oftest.dataplane_instance.kill()
520 oftest.dataplane_instance = None
521
Rich Lane50d42eb2012-07-16 11:57:03 -0700522 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700523 # exit(1) hangs sometimes
524 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700525 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700526 os._exit(1)