blob: a3756daff7581139c6156bed62550e0dee2a4e3d [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,
Stephen Finucanee016cf22014-04-16 22:04:11 +010081 "xunit" : False,
82 "xunit_dir" : "xunit",
Rich Lane95f078b2013-01-06 13:24:58 -080083
84 # Test behavior options
85 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070086 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070087 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070088 "default_timeout" : 2.0,
89 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000090 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070091 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000092 "disable_ipv6" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080093
94 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080095 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080096}
97
Rich Lane95f078b2013-01-06 13:24:58 -080098def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -080099 """
100 Set up the configuration including parsing the arguments
101
Dan Talayco48370102010-03-03 15:17:33 -0800102 @return A pair (config, args) where config is an config
103 object and args is any additional arguments from the command line
104 """
105
Rich Lane4113a582013-01-03 10:13:02 -0800106 usage = "usage: %prog [options] (test|group)..."
107
Rich Lane95f078b2013-01-06 13:24:58 -0800108 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800109OFTest is a framework and set of tests for validating OpenFlow switches.
110
111The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700112connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800113the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
114dataplane.
115
116If no positional arguments are given then OFTest will run all tests that
117depend only on standard OpenFlow 1.0. Otherwise each positional argument
118is interpreted as either a test name or a test group name. The union of
119these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800120--list option. Tests and groups can be subtracted from the result by
121prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800122"""
123
Rich Lane2d6d4822013-01-08 10:49:16 -0800124 # Parse --interface
125 def check_interface(option, opt, value):
126 try:
127 ofport, interface = value.split('@', 1)
128 ofport = int(ofport)
129 except ValueError:
130 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
131 return (ofport, interface)
132
133 class Option(optparse.Option):
134 TYPES = optparse.Option.TYPES + ("interface",)
135 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
136 TYPE_CHECKER["interface"] = check_interface
137
Rich Lane95f078b2013-01-06 13:24:58 -0800138 parser = optparse.OptionParser(version="%prog 0.1",
139 usage=usage,
140 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800141 formatter=oftest.help_formatter.HelpFormatter(),
142 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800143
144 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800145 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800146
Dan Talayco2c0dba32010-03-06 22:47:06 -0800147 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700148 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800149 parser.add_option("--list-test-names", action='store_true',
150 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700151 parser.add_option("--allow-user", action="store_true",
152 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800153
154 group = optparse.OptionGroup(parser, "Test selection options")
155 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
156 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
157 group.add_option("--test-dir", type="string", help="Directory containing tests")
158 parser.add_option_group(group)
159
160 group = optparse.OptionGroup(parser, "Switch connection options")
161 group.add_option("-H", "--host", dest="controller_host",
162 help="IP address to listen on (default %default)")
163 group.add_option("-p", "--port", dest="controller_port",
164 type="int", help="Port number to listen on (default %default)")
165 group.add_option("-S", "--switch-ip", dest="switch_ip",
166 help="If set, actively connect to this switch by IP")
167 group.add_option("-P", "--platform", help="Platform module name (default %default)")
168 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
169 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800170 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
171 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 -0700172 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800173 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800174 parser.add_option_group(group)
175
176 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700177 group.add_option("--log-file", help="Name of log file (default %default)")
178 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800179 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
180 group.add_option("--debug", choices=dbg_lvl_names,
181 help="Debug lvl: debug, info, warning, error, critical (default %default)")
182 group.add_option("-v", "--verbose", action="store_const", dest="debug",
183 const="verbose", help="Shortcut for --debug=verbose")
184 group.add_option("-q", "--quiet", action="store_const", dest="debug",
185 const="warning", help="Shortcut for --debug=warning")
Rich Lane9631f002014-03-21 18:05:16 -0700186 group.add_option("--profile", action="store_true", help="Write Python profile to profile.out")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100187 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
188 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800189 parser.add_option_group(group)
190
191 group = optparse.OptionGroup(parser, "Test behavior options")
192 group.add_option("--relax", action="store_true",
193 help="Relax packet match checks allowing other packets")
194 test_params_help = """Set test parameters: key=val;... (see --list)
195 """
196 group.add_option("-t", "--test-params", help=test_params_help)
197 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700198 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700199 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700200 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700201 group.add_option("--default-negative-timeout", type=float,
202 help="Timeout in seconds for negative checks")
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")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000207 group.add_option("--disable-ipv6", action="store_true",
208 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800209 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000210
Dan Talayco48370102010-03-03 15:17:33 -0800211 # Might need this if other parsers want command line
212 # parser.allow_interspersed_args = False
213 (options, args) = parser.parse_args()
214
Rich Lane74b13d12013-05-03 17:58:50 -0700215 # If --test-dir wasn't given, pick one based on the OpenFlow version
216 if options.test_dir == None:
217 if options.openflow_version == "1.0":
218 options.test_dir = os.path.join(root_dir, "tests")
219 else:
220 options.test_dir = os.path.join(root_dir, "tests-" + options.openflow_version)
221
Rich Lane95f078b2013-01-06 13:24:58 -0800222 # Convert options from a Namespace to a plain dictionary
223 config = config_default.copy()
224 for key in config.keys():
225 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800226
227 return (config, args)
228
229def logging_setup(config):
230 """
231 Set up logging based on config
232 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700233
234 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
235
236 if config["log_dir"] != None:
237 if os.path.exists(config["log_dir"]):
238 import shutil
239 shutil.rmtree(config["log_dir"])
240 os.makedirs(config["log_dir"])
241 else:
242 if os.path.exists(config["log_file"]):
243 os.remove(config["log_file"])
244
245 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800246
Stephen Finucanee016cf22014-04-16 22:04:11 +0100247def xunit_setup(config):
248 """
249 Set up xUnit output based on config
250 """
251
252 if not config["xunit"]:
253 return
254
255 if config["xunit"] != None:
256 if os.path.exists(config["xunit_dir"]):
257 import shutil
258 shutil.rmtree(config["xunit_dir"])
259 os.makedirs(config["xunit_dir"])
260
Rich Lane472aaea2013-08-27 09:27:38 -0700261def pcap_setup(config):
262 """
263 Set up dataplane packet capturing based on config
264 """
265
266 if config["log_dir"] == None:
267 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
268 oftest.dataplane_instance.start_pcap(filename)
269 else:
270 # start_pcap is called per-test in base_tests
271 pass
272
273
Rich Lane943be672012-10-04 19:20:16 -0700274def load_test_modules(config):
275 """
276 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800277
Rich Lane943be672012-10-04 19:20:16 -0700278 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800279
Rich Lanecc45b8e2013-01-02 15:55:02 -0800280 Also updates the _groups member to include "standard" and
281 module test groups if appropriate.
282
Dan Talayco2c0dba32010-03-06 22:47:06 -0800283 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700284 @returns A dictionary from test module names to tuples of
285 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800286 """
287
Rich Lane943be672012-10-04 19:20:16 -0700288 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800289
Rich Lane943be672012-10-04 19:20:16 -0700290 for root, dirs, filenames in os.walk(config["test_dir"]):
291 # Iterate over each python file
292 for filename in fnmatch.filter(filenames, '[!.]*.py'):
293 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800294
Rich Lane943be672012-10-04 19:20:16 -0700295 try:
296 if sys.modules.has_key(modname):
297 mod = sys.modules[modname]
298 else:
299 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
300 except:
301 logging.warning("Could not import file " + filename)
302 raise
Rich Lane520e4152012-07-09 16:18:16 -0700303
Rich Lane943be672012-10-04 19:20:16 -0700304 # Find all testcases defined in the module
305 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700306 issubclass(v, unittest.TestCase) and
307 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700308 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800309 for (testname, test) in tests.items():
310 # Set default annotation values
311 if not hasattr(test, "_groups"):
312 test._groups = []
313 if not hasattr(test, "_nonstandard"):
314 test._nonstandard = False
315 if not hasattr(test, "_disabled"):
316 test._disabled = False
317
318 # Put test in its module's test group
319 if not test._disabled:
320 test._groups.append(modname)
321
322 # Put test in the standard test group
323 if not test._disabled and not test._nonstandard:
324 test._groups.append("standard")
325 test._groups.append("all") # backwards compatibility
326
Rich Lane943be672012-10-04 19:20:16 -0700327 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700328
Rich Lane943be672012-10-04 19:20:16 -0700329 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800330
Rich Lane5a9a1922013-01-11 14:29:30 -0800331def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700332 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800333 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800334 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800335 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700336 @param test_modules Same format as the output of load_test_modules.
337 @returns Same format as the output of load_test_modules.
338 """
339 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800340 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700341 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800342
343 if e.startswith('^'):
344 negated = True
345 e = e[1:]
346 else:
347 negated = False
348
Rich Lane15f64de2012-10-04 21:25:57 -0700349 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800350 for (testname, test) in tests.items():
351 if e in test._groups or e == "%s.%s" % (modname, testname):
352 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800353 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800354 if not hasattr(test, "_versions") or version in test._versions:
355 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800356 else:
357 if modname in result and testname in result[modname][1]:
358 del result[modname][1][testname]
359 if not result[modname][1]:
360 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800361 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800362
Rich Lane15f64de2012-10-04 21:25:57 -0700363 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800364 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800365
Rich Lane15f64de2012-10-04 21:25:57 -0700366 return result
367
Dan Talayco2c0dba32010-03-06 22:47:06 -0800368def die(msg, exit_val=1):
369 print msg
370 logging.critical(msg)
371 sys.exit(exit_val)
372
Dan Talayco79f36082010-03-11 16:53:53 -0800373def _space_to(n, str):
374 """
375 Generate a string of spaces to achieve width n given string str
376 If length of str >= n, return one space
377 """
378 spaces = n - len(str)
379 if spaces > 0:
380 return " " * spaces
381 return " "
382
Dan Talayco48370102010-03-03 15:17:33 -0800383#
384# Main script
385#
386
Rich Lane477f4812012-10-04 22:49:00 -0700387# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800388(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700389oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800390
Rich Laned7a85c42012-09-28 15:38:45 -0700391logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100392xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700393logging.info("++++++++ " + time.asctime() + " ++++++++")
394
Rich Lane9fd05682013-01-10 15:30:38 -0800395# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700396name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
397sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800398
399# HACK: testutils.py imports controller.py, which needs the ofp module
400import oftest.testutils
401
Rich Lanee284b6b2012-10-03 09:19:58 -0700402# Allow tests to import each other
403sys.path.append(config["test_dir"])
404
Rich Lanec76b09a2013-01-02 16:53:22 -0800405test_specs = args
406if config["test_spec"] != "":
407 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
408 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800409if config["test_file"] != None:
410 with open(config["test_file"], 'r') as f:
411 for line in f:
412 line, _, _ = line.partition('#') # remove comments
413 line = line.strip()
414 if line:
415 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800416if test_specs == []:
417 test_specs = ["standard"]
418
Rich Laned8e45482013-01-02 17:36:21 -0800419test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800420
421# Check if test list is requested; display and exit if so
422if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700423 mod_count = 0
424 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800425 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800426 print """\
427Tests are shown grouped by module. If a test is in any groups beyond "standard"
428and its module's group then they are shown in parentheses."""
429 print
430 print """\
431Tests marked with '*' are non-standard and may require vendor extensions or
432special switch configuration. These are not part of the "standard" test group."""
433 print
434 print """\
435Tests marked with '!' are disabled because they are experimental, special-purpose,
436or are too long to be run normally. These are not part of the "standard" test
437group or their module's test group."""
438 print
439 print "Tests marked (TP1) after name take --test-params including:"
440 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800441 print
442 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700443 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700444 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800445 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800446 start_str = " Module " + mod.__name__ + ": "
447 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700448 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700449 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800450 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700451 desc = desc.split('\n')[0]
452 except:
453 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800454 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800455 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800456 if groups:
457 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800458 if hasattr(test, "_versions"):
459 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800460 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
461 test._disabled and "!" or " ",
462 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700463 if len(start_str) > 22:
464 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800465 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700466 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800467 print
Rich Lane15f64de2012-10-04 21:25:57 -0700468 print "%d modules shown with a total of %d tests" % \
469 (mod_count, test_count)
470 print
Rich Lane37f42112013-01-03 13:41:49 -0800471 print "Test groups: %s" % (', '.join(sorted(all_groups)))
472
Dan Talayco2c0dba32010-03-06 22:47:06 -0800473 sys.exit(0)
474
Rich Lane5a9a1922013-01-11 14:29:30 -0800475test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800476
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700477# Check if test list is requested; display and exit if so
478if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700479 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700480 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800481 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700482 sys.exit(0)
483
Dan Talayco2c0dba32010-03-06 22:47:06 -0800484# Generate the test suite
485#@todo Decide if multiple suites are ever needed
486suite = unittest.TestSuite()
487
Rich Lane15f64de2012-10-04 21:25:57 -0700488for (modname, (mod, tests)) in test_modules.items():
489 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800490 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800491
Rich Lane51590f62012-10-09 15:06:29 -0700492# Allow platforms to import each other
493sys.path.append(config["platform_dir"])
494
Rich Lane8aebc5e2012-09-25 17:57:53 -0700495# Load the platform module
496platform_name = config["platform"]
497logging.info("Importing platform: " + platform_name)
498platform_mod = None
499try:
Rich Lane483e1542012-10-05 09:29:39 -0700500 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700501except:
502 logging.warn("Failed to import " + platform_name + " platform module")
503 raise
Dan Talayco48370102010-03-03 15:17:33 -0800504
505try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700506 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800507except:
508 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700509 raise
Dan Talayco48370102010-03-03 15:17:33 -0800510
511if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700512 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800513
514logging.debug("Configuration: " + str(config))
515logging.info("OF port map: " + str(config["port_map"]))
516
Rich Lanee55abf72012-07-26 20:11:42 -0700517oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700518oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700519oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700520
Rich Laneee57ad02012-07-13 15:40:36 -0700521if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700522 print "ERROR: Super-user privileges required. Please re-run with " \
523 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700524 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700525
Rich Lane8592bec2012-09-03 09:06:59 -0700526if config["random_seed"] is not None:
527 logging.info("Random seed: %d" % config["random_seed"])
528 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700529else:
530 # Generate random seed and report to log file
531 seed = random.randrange(100000000)
532 logging.info("Autogen random seed: %d" % seed)
533 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700534
Rich Lane5bd6cf92012-10-04 17:57:24 -0700535# Remove python's signal handler which raises KeyboardError. Exiting from an
536# exception waits for all threads to terminate which might not happen.
537signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700538
539if __name__ == "__main__":
Rich Lane9631f002014-03-21 18:05:16 -0700540 if config["profile"]:
541 import cProfile
542 profiler = cProfile.Profile()
543 profiler.enable()
544
Rich Lane2c7812c2012-12-27 17:52:23 -0800545 # Set up the dataplane
546 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700547 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800548 for of_port, ifname in config["port_map"].items():
549 oftest.dataplane_instance.port_add(ifname, of_port)
550
Dan Talaycoac25cf32010-07-20 14:08:28 -0700551 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100552 if config["xunit"]:
553 import xmlrunner # fail-fast if module missing
554
555 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
556 outsuffix="",
557 verbosity=2)
558 result = runner.run(suite)
559 else:
560 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700561 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700562 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700563 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700564 if oftest.testutils.skipped_test_count == 1: ts = " test"
565 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
566 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700567 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800568
569 # Shutdown the dataplane
570 oftest.dataplane_instance.kill()
571 oftest.dataplane_instance = None
572
Rich Lane9631f002014-03-21 18:05:16 -0700573 if config["profile"]:
574 profiler.disable()
575 profiler.dump_stats("profile.out")
576
Rich Lane50d42eb2012-07-16 11:57:03 -0700577 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700578 # exit(1) hangs sometimes
579 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700580 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700581 os._exit(1)