blob: 7db8257f65eda89a02460fe5a2defa4674cf344d [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 Lanee55abf72012-07-26 20:11:42 -070086 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000087 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070088 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000089 "disable_ipv6" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080090
91 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080092 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080093}
94
Rich Lane95f078b2013-01-06 13:24:58 -080095def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080096 """
97 Set up the configuration including parsing the arguments
98
Dan Talayco48370102010-03-03 15:17:33 -080099 @return A pair (config, args) where config is an config
100 object and args is any additional arguments from the command line
101 """
102
Rich Lane4113a582013-01-03 10:13:02 -0800103 usage = "usage: %prog [options] (test|group)..."
104
Rich Lane95f078b2013-01-06 13:24:58 -0800105 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800106OFTest is a framework and set of tests for validating OpenFlow switches.
107
108The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700109connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800110the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
111dataplane.
112
113If no positional arguments are given then OFTest will run all tests that
114depend only on standard OpenFlow 1.0. Otherwise each positional argument
115is interpreted as either a test name or a test group name. The union of
116these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800117--list option. Tests and groups can be subtracted from the result by
118prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800119"""
120
Rich Lane2d6d4822013-01-08 10:49:16 -0800121 # Parse --interface
122 def check_interface(option, opt, value):
123 try:
124 ofport, interface = value.split('@', 1)
125 ofport = int(ofport)
126 except ValueError:
127 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
128 return (ofport, interface)
129
130 class Option(optparse.Option):
131 TYPES = optparse.Option.TYPES + ("interface",)
132 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
133 TYPE_CHECKER["interface"] = check_interface
134
Rich Lane95f078b2013-01-06 13:24:58 -0800135 parser = optparse.OptionParser(version="%prog 0.1",
136 usage=usage,
137 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800138 formatter=oftest.help_formatter.HelpFormatter(),
139 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800140
141 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800142 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800143
Dan Talayco2c0dba32010-03-06 22:47:06 -0800144 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700145 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800146 parser.add_option("--list-test-names", action='store_true',
147 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700148 parser.add_option("--allow-user", action="store_true",
149 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800150
151 group = optparse.OptionGroup(parser, "Test selection options")
152 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
153 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
154 group.add_option("--test-dir", type="string", help="Directory containing tests")
155 parser.add_option_group(group)
156
157 group = optparse.OptionGroup(parser, "Switch connection options")
158 group.add_option("-H", "--host", dest="controller_host",
159 help="IP address to listen on (default %default)")
160 group.add_option("-p", "--port", dest="controller_port",
161 type="int", help="Port number to listen on (default %default)")
162 group.add_option("-S", "--switch-ip", dest="switch_ip",
163 help="If set, actively connect to this switch by IP")
164 group.add_option("-P", "--platform", help="Platform module name (default %default)")
165 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
166 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800167 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
168 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 -0700169 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800170 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800171 parser.add_option_group(group)
172
173 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700174 group.add_option("--log-file", help="Name of log file (default %default)")
175 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800176 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
177 group.add_option("--debug", choices=dbg_lvl_names,
178 help="Debug lvl: debug, info, warning, error, critical (default %default)")
179 group.add_option("-v", "--verbose", action="store_const", dest="debug",
180 const="verbose", help="Shortcut for --debug=verbose")
181 group.add_option("-q", "--quiet", action="store_const", dest="debug",
182 const="warning", help="Shortcut for --debug=warning")
Rich Lane9631f002014-03-21 18:05:16 -0700183 group.add_option("--profile", action="store_true", help="Write Python profile to profile.out")
Rich Lane95f078b2013-01-06 13:24:58 -0800184 parser.add_option_group(group)
185
186 group = optparse.OptionGroup(parser, "Test behavior options")
187 group.add_option("--relax", action="store_true",
188 help="Relax packet match checks allowing other packets")
189 test_params_help = """Set test parameters: key=val;... (see --list)
190 """
191 group.add_option("-t", "--test-params", help=test_params_help)
192 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700193 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800194 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700195 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800196 group.add_option("--minsize", type="int",
197 help="Minimum allowable packet size on the dataplane.")
198 group.add_option("--random-seed", type="int",
199 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000200 group.add_option("--disable-ipv6", action="store_true",
201 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800202 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000203
Dan Talayco48370102010-03-03 15:17:33 -0800204 # Might need this if other parsers want command line
205 # parser.allow_interspersed_args = False
206 (options, args) = parser.parse_args()
207
Rich Lane74b13d12013-05-03 17:58:50 -0700208 # If --test-dir wasn't given, pick one based on the OpenFlow version
209 if options.test_dir == None:
210 if options.openflow_version == "1.0":
211 options.test_dir = os.path.join(root_dir, "tests")
212 else:
213 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
214
Rich Lane95f078b2013-01-06 13:24:58 -0800215 # Convert options from a Namespace to a plain dictionary
216 config = config_default.copy()
217 for key in config.keys():
218 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800219
220 return (config, args)
221
222def logging_setup(config):
223 """
224 Set up logging based on config
225 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700226
227 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
228
229 if config["log_dir"] != None:
230 if os.path.exists(config["log_dir"]):
231 import shutil
232 shutil.rmtree(config["log_dir"])
233 os.makedirs(config["log_dir"])
234 else:
235 if os.path.exists(config["log_file"]):
236 os.remove(config["log_file"])
237
238 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800239
Rich Lane472aaea2013-08-27 09:27:38 -0700240def pcap_setup(config):
241 """
242 Set up dataplane packet capturing based on config
243 """
244
245 if config["log_dir"] == None:
246 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
247 oftest.dataplane_instance.start_pcap(filename)
248 else:
249 # start_pcap is called per-test in base_tests
250 pass
251
252
Rich Lane943be672012-10-04 19:20:16 -0700253def load_test_modules(config):
254 """
255 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800256
Rich Lane943be672012-10-04 19:20:16 -0700257 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800258
Rich Lanecc45b8e2013-01-02 15:55:02 -0800259 Also updates the _groups member to include "standard" and
260 module test groups if appropriate.
261
Dan Talayco2c0dba32010-03-06 22:47:06 -0800262 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700263 @returns A dictionary from test module names to tuples of
264 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800265 """
266
Rich Lane943be672012-10-04 19:20:16 -0700267 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800268
Rich Lane943be672012-10-04 19:20:16 -0700269 for root, dirs, filenames in os.walk(config["test_dir"]):
270 # Iterate over each python file
271 for filename in fnmatch.filter(filenames, '[!.]*.py'):
272 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800273
Rich Lane943be672012-10-04 19:20:16 -0700274 try:
275 if sys.modules.has_key(modname):
276 mod = sys.modules[modname]
277 else:
278 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
279 except:
280 logging.warning("Could not import file " + filename)
281 raise
Rich Lane520e4152012-07-09 16:18:16 -0700282
Rich Lane943be672012-10-04 19:20:16 -0700283 # Find all testcases defined in the module
284 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700285 issubclass(v, unittest.TestCase) and
286 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700287 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800288 for (testname, test) in tests.items():
289 # Set default annotation values
290 if not hasattr(test, "_groups"):
291 test._groups = []
292 if not hasattr(test, "_nonstandard"):
293 test._nonstandard = False
294 if not hasattr(test, "_disabled"):
295 test._disabled = False
296
297 # Put test in its module's test group
298 if not test._disabled:
299 test._groups.append(modname)
300
301 # Put test in the standard test group
302 if not test._disabled and not test._nonstandard:
303 test._groups.append("standard")
304 test._groups.append("all") # backwards compatibility
305
Rich Lane943be672012-10-04 19:20:16 -0700306 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700307
Rich Lane943be672012-10-04 19:20:16 -0700308 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800309
Rich Lane5a9a1922013-01-11 14:29:30 -0800310def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700311 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800312 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800313 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800314 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700315 @param test_modules Same format as the output of load_test_modules.
316 @returns Same format as the output of load_test_modules.
317 """
318 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800319 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700320 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800321
322 if e.startswith('^'):
323 negated = True
324 e = e[1:]
325 else:
326 negated = False
327
Rich Lane15f64de2012-10-04 21:25:57 -0700328 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800329 for (testname, test) in tests.items():
330 if e in test._groups or e == "%s.%s" % (modname, testname):
331 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800332 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800333 if not hasattr(test, "_versions") or version in test._versions:
334 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800335 else:
336 if modname in result and testname in result[modname][1]:
337 del result[modname][1][testname]
338 if not result[modname][1]:
339 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800340 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800341
Rich Lane15f64de2012-10-04 21:25:57 -0700342 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800343 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800344
Rich Lane15f64de2012-10-04 21:25:57 -0700345 return result
346
Dan Talayco2c0dba32010-03-06 22:47:06 -0800347def die(msg, exit_val=1):
348 print msg
349 logging.critical(msg)
350 sys.exit(exit_val)
351
Dan Talayco79f36082010-03-11 16:53:53 -0800352def _space_to(n, str):
353 """
354 Generate a string of spaces to achieve width n given string str
355 If length of str >= n, return one space
356 """
357 spaces = n - len(str)
358 if spaces > 0:
359 return " " * spaces
360 return " "
361
Dan Talayco48370102010-03-03 15:17:33 -0800362#
363# Main script
364#
365
Rich Lane477f4812012-10-04 22:49:00 -0700366# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800367(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700368oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800369
Rich Laned7a85c42012-09-28 15:38:45 -0700370logging_setup(config)
371logging.info("++++++++ " + time.asctime() + " ++++++++")
372
Rich Lane9fd05682013-01-10 15:30:38 -0800373# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700374name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
375sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800376
377# HACK: testutils.py imports controller.py, which needs the ofp module
378import oftest.testutils
379
Rich Lanee284b6b2012-10-03 09:19:58 -0700380# Allow tests to import each other
381sys.path.append(config["test_dir"])
382
Rich Lanec76b09a2013-01-02 16:53:22 -0800383test_specs = args
384if config["test_spec"] != "":
385 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
386 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800387if config["test_file"] != None:
388 with open(config["test_file"], 'r') as f:
389 for line in f:
390 line, _, _ = line.partition('#') # remove comments
391 line = line.strip()
392 if line:
393 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800394if test_specs == []:
395 test_specs = ["standard"]
396
Rich Laned8e45482013-01-02 17:36:21 -0800397test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800398
399# Check if test list is requested; display and exit if so
400if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700401 mod_count = 0
402 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800403 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800404 print """\
405Tests are shown grouped by module. If a test is in any groups beyond "standard"
406and its module's group then they are shown in parentheses."""
407 print
408 print """\
409Tests marked with '*' are non-standard and may require vendor extensions or
410special switch configuration. These are not part of the "standard" test group."""
411 print
412 print """\
413Tests marked with '!' are disabled because they are experimental, special-purpose,
414or are too long to be run normally. These are not part of the "standard" test
415group or their module's test group."""
416 print
417 print "Tests marked (TP1) after name take --test-params including:"
418 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800419 print
420 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700421 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700422 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800423 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800424 start_str = " Module " + mod.__name__ + ": "
425 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700426 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700427 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800428 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700429 desc = desc.split('\n')[0]
430 except:
431 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800432 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800433 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800434 if groups:
435 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800436 if hasattr(test, "_versions"):
437 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800438 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
439 test._disabled and "!" or " ",
440 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700441 if len(start_str) > 22:
442 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800443 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700444 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800445 print
Rich Lane15f64de2012-10-04 21:25:57 -0700446 print "%d modules shown with a total of %d tests" % \
447 (mod_count, test_count)
448 print
Rich Lane37f42112013-01-03 13:41:49 -0800449 print "Test groups: %s" % (', '.join(sorted(all_groups)))
450
Dan Talayco2c0dba32010-03-06 22:47:06 -0800451 sys.exit(0)
452
Rich Lane5a9a1922013-01-11 14:29:30 -0800453test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800454
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700455# Check if test list is requested; display and exit if so
456if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700457 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700458 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800459 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700460 sys.exit(0)
461
Dan Talayco2c0dba32010-03-06 22:47:06 -0800462# Generate the test suite
463#@todo Decide if multiple suites are ever needed
464suite = unittest.TestSuite()
465
Rich Lane15f64de2012-10-04 21:25:57 -0700466for (modname, (mod, tests)) in test_modules.items():
467 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800468 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800469
Rich Lane51590f62012-10-09 15:06:29 -0700470# Allow platforms to import each other
471sys.path.append(config["platform_dir"])
472
Rich Lane8aebc5e2012-09-25 17:57:53 -0700473# Load the platform module
474platform_name = config["platform"]
475logging.info("Importing platform: " + platform_name)
476platform_mod = None
477try:
Rich Lane483e1542012-10-05 09:29:39 -0700478 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700479except:
480 logging.warn("Failed to import " + platform_name + " platform module")
481 raise
Dan Talayco48370102010-03-03 15:17:33 -0800482
483try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700484 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800485except:
486 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700487 raise
Dan Talayco48370102010-03-03 15:17:33 -0800488
489if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700490 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800491
492logging.debug("Configuration: " + str(config))
493logging.info("OF port map: " + str(config["port_map"]))
494
Rich Lanee55abf72012-07-26 20:11:42 -0700495oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700496oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700497
Rich Laneee57ad02012-07-13 15:40:36 -0700498if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700499 print "ERROR: Super-user privileges required. Please re-run with " \
500 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700501 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700502
Rich Lane8592bec2012-09-03 09:06:59 -0700503if config["random_seed"] is not None:
504 logging.info("Random seed: %d" % config["random_seed"])
505 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700506else:
507 # Generate random seed and report to log file
508 seed = random.randrange(100000000)
509 logging.info("Autogen random seed: %d" % seed)
510 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700511
Rich Lane5bd6cf92012-10-04 17:57:24 -0700512# Remove python's signal handler which raises KeyboardError. Exiting from an
513# exception waits for all threads to terminate which might not happen.
514signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700515
516if __name__ == "__main__":
Rich Lane9631f002014-03-21 18:05:16 -0700517 if config["profile"]:
518 import cProfile
519 profiler = cProfile.Profile()
520 profiler.enable()
521
Rich Lane2c7812c2012-12-27 17:52:23 -0800522 # Set up the dataplane
523 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700524 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800525 for of_port, ifname in config["port_map"].items():
526 oftest.dataplane_instance.port_add(ifname, of_port)
527
Dan Talaycoac25cf32010-07-20 14:08:28 -0700528 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800529 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700530 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700531 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700532 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700533 if oftest.testutils.skipped_test_count == 1: ts = " test"
534 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
535 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700536 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800537
538 # Shutdown the dataplane
539 oftest.dataplane_instance.kill()
540 oftest.dataplane_instance = None
541
Rich Lane9631f002014-03-21 18:05:16 -0700542 if config["profile"]:
543 profiler.disable()
544 profiler.dump_stats("profile.out")
545
Rich Lane50d42eb2012-07-16 11:57:03 -0700546 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700547 # exit(1) hangs sometimes
548 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700549 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700550 os._exit(1)