blob: 5016a63f4cfcfb8b3e6bffaeef8cf975a7ae0da2 [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
Stephen Finucane7424a742014-05-15 19:46:06 +010010To add a new command line option, edit both the CONFIG_DEFAULT dictionary and
Rich Laneea5060d2013-01-06 13:59:00 -080011the 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 Talayco48370102010-03-03 15:17:33 -080017import logging
18import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080019import time
Brandon Heller446c1432010-04-01 12:43:27 -070020import os
Rich Lane6b452bc2012-07-09 16:52:21 -070021import imp
Rich Lane8592bec2012-09-03 09:06:59 -070022import random
Rich Lane5bd6cf92012-10-04 17:57:24 -070023import signal
Rich Lane943be672012-10-04 19:20:16 -070024import fnmatch
Rich Lane2d6d4822013-01-08 10:49:16 -080025import copy
Dan Talayco48370102010-03-03 15:17:33 -080026
Stephen Finucane7424a742014-05-15 19:46:06 +010027ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
Rich Lanefadf3452012-10-03 16:23:37 -070028
Stephen Finucane7424a742014-05-15 19:46:06 +010029PY_SRC_DIR = os.path.join(ROOT_DIR, 'src', 'python')
30if os.path.exists(os.path.join(PY_SRC_DIR, 'oftest')):
Rich Lane39878042012-07-09 14:45:35 -070031 # Running from source tree
Stephen Finucane7424a742014-05-15 19:46:06 +010032 sys.path.insert(0, PY_SRC_DIR)
Rich Lane39878042012-07-09 14:45:35 -070033
Rich Lane477f4812012-10-04 22:49:00 -070034import oftest
35from oftest import config
Rich Lanee55abf72012-07-26 20:11:42 -070036import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080037import oftest.help_formatter
Rich Lane3f7098c2013-03-12 10:28:32 -070038import loxi
Dan Talaycoba3745c2010-07-21 21:51:08 -070039
Dan Talayco48370102010-03-03 15:17:33 -080040##@var DEBUG_LEVELS
41# Map from strings to debugging levels
42DEBUG_LEVELS = {
43 'debug' : logging.DEBUG,
44 'verbose' : logging.DEBUG,
45 'info' : logging.INFO,
46 'warning' : logging.WARNING,
47 'warn' : logging.WARNING,
48 'error' : logging.ERROR,
49 'critical' : logging.CRITICAL
50}
51
Stephen Finucane7424a742014-05-15 19:46:06 +010052##@var CONFIG_DEFAULT
Dan Talayco48370102010-03-03 15:17:33 -080053# The default configuration dictionary for OFT
Stephen Finucane7424a742014-05-15 19:46:06 +010054CONFIG_DEFAULT = {
Rich Lane95f078b2013-01-06 13:24:58 -080055 # Miscellaneous options
56 "list" : False,
57 "list_test_names" : False,
58 "allow_user" : False,
59
60 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080061 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080062 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070063 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080064
65 # Switch connection options
66 "controller_host" : "0.0.0.0", # For passive bind
Rich Lane4d1f3eb2013-10-03 13:45:57 -070067 "controller_port" : 6653,
Rich Lane95f078b2013-01-06 13:24:58 -080068 "switch_ip" : None, # If not none, actively connect to switch
Rich Lane15f26322013-01-08 11:23:24 -080069 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080070 "platform_args" : None,
Stephen Finucane7424a742014-05-15 19:46:06 +010071 "platform_dir" : os.path.join(ROOT_DIR, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080072 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080073 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080074
75 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080076 "log_file" : "oft.log",
Rich Lane69fd8e02013-08-23 16:23:42 -070077 "log_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080078 "debug" : "verbose",
Rich Lane9631f002014-03-21 18:05:16 -070079 "profile" : False,
Stephen Finucane6219af72014-05-14 21:08:30 +010080 "profile_file" : "profile.out",
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
Stephen Finucane7424a742014-05-15 19:46:06 +0100145 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")
Stephen Finucane6219af72014-05-14 21:08:30 +0100186 group.add_option("--profile", action="store_true", help="Enable Python profiling")
187 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100188 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
189 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800190 parser.add_option_group(group)
191
192 group = optparse.OptionGroup(parser, "Test behavior options")
193 group.add_option("--relax", action="store_true",
194 help="Relax packet match checks allowing other packets")
195 test_params_help = """Set test parameters: key=val;... (see --list)
196 """
197 group.add_option("-t", "--test-params", help=test_params_help)
198 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700199 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700200 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700201 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700202 group.add_option("--default-negative-timeout", type=float,
203 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800204 group.add_option("--minsize", type="int",
205 help="Minimum allowable packet size on the dataplane.")
206 group.add_option("--random-seed", type="int",
207 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000208 group.add_option("--disable-ipv6", action="store_true",
209 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800210 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000211
Dan Talayco48370102010-03-03 15:17:33 -0800212 # Might need this if other parsers want command line
213 # parser.allow_interspersed_args = False
214 (options, args) = parser.parse_args()
215
Rich Lane74b13d12013-05-03 17:58:50 -0700216 # If --test-dir wasn't given, pick one based on the OpenFlow version
217 if options.test_dir == None:
218 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100219 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700220 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100221 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700222
Rich Lane95f078b2013-01-06 13:24:58 -0800223 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100224 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800225 for key in config.keys():
226 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800227
228 return (config, args)
229
230def logging_setup(config):
231 """
232 Set up logging based on config
233 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700234
235 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
236
237 if config["log_dir"] != None:
238 if os.path.exists(config["log_dir"]):
239 import shutil
240 shutil.rmtree(config["log_dir"])
241 os.makedirs(config["log_dir"])
242 else:
243 if os.path.exists(config["log_file"]):
244 os.remove(config["log_file"])
245
246 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800247
Stephen Finucanee016cf22014-04-16 22:04:11 +0100248def xunit_setup(config):
249 """
250 Set up xUnit output based on config
251 """
252
253 if not config["xunit"]:
254 return
255
Stephen Finucane6219af72014-05-14 21:08:30 +0100256 if os.path.exists(config["xunit_dir"]):
257 import shutil
258 shutil.rmtree(config["xunit_dir"])
259 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100260
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
Stephen Finucane6219af72014-05-14 21:08:30 +0100273def profiler_setup(config):
274 """
275 Set up profiler based on config
276 """
277
278 if not config["profile"]:
279 return
280
281 import cProfile
282 profiler = cProfile.Profile()
283 profiler.enable()
284
285 return profiler
286
287def profiler_teardown(profiler):
288 """
289 Tear down profiler based on config
290 """
291
292 if not config["profile"]:
293 return
294
295 profiler.disable()
296 profiler.dump_stats(config["profile_file"])
297
Rich Lane472aaea2013-08-27 09:27:38 -0700298
Rich Lane943be672012-10-04 19:20:16 -0700299def load_test_modules(config):
300 """
301 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800302
Rich Lane943be672012-10-04 19:20:16 -0700303 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800304
Rich Lanecc45b8e2013-01-02 15:55:02 -0800305 Also updates the _groups member to include "standard" and
306 module test groups if appropriate.
307
Dan Talayco2c0dba32010-03-06 22:47:06 -0800308 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700309 @returns A dictionary from test module names to tuples of
310 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800311 """
312
Rich Lane943be672012-10-04 19:20:16 -0700313 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800314
Rich Lane943be672012-10-04 19:20:16 -0700315 for root, dirs, filenames in os.walk(config["test_dir"]):
316 # Iterate over each python file
317 for filename in fnmatch.filter(filenames, '[!.]*.py'):
318 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800319
Rich Lane943be672012-10-04 19:20:16 -0700320 try:
321 if sys.modules.has_key(modname):
322 mod = sys.modules[modname]
323 else:
324 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
325 except:
326 logging.warning("Could not import file " + filename)
327 raise
Rich Lane520e4152012-07-09 16:18:16 -0700328
Rich Lane943be672012-10-04 19:20:16 -0700329 # Find all testcases defined in the module
330 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700331 issubclass(v, unittest.TestCase) and
332 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700333 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800334 for (testname, test) in tests.items():
335 # Set default annotation values
336 if not hasattr(test, "_groups"):
337 test._groups = []
338 if not hasattr(test, "_nonstandard"):
339 test._nonstandard = False
340 if not hasattr(test, "_disabled"):
341 test._disabled = False
342
343 # Put test in its module's test group
344 if not test._disabled:
345 test._groups.append(modname)
346
347 # Put test in the standard test group
348 if not test._disabled and not test._nonstandard:
349 test._groups.append("standard")
350 test._groups.append("all") # backwards compatibility
351
Rich Lane943be672012-10-04 19:20:16 -0700352 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700353
Rich Lane943be672012-10-04 19:20:16 -0700354 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800355
Rich Lane5a9a1922013-01-11 14:29:30 -0800356def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700357 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800358 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800359 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800360 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700361 @param test_modules Same format as the output of load_test_modules.
362 @returns Same format as the output of load_test_modules.
363 """
364 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800365 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700366 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800367
368 if e.startswith('^'):
369 negated = True
370 e = e[1:]
371 else:
372 negated = False
373
Rich Lane15f64de2012-10-04 21:25:57 -0700374 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800375 for (testname, test) in tests.items():
376 if e in test._groups or e == "%s.%s" % (modname, testname):
377 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800378 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800379 if not hasattr(test, "_versions") or version in test._versions:
380 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800381 else:
382 if modname in result and testname in result[modname][1]:
383 del result[modname][1][testname]
384 if not result[modname][1]:
385 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800386 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800387
Rich Lane15f64de2012-10-04 21:25:57 -0700388 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800389 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800390
Rich Lane15f64de2012-10-04 21:25:57 -0700391 return result
392
Dan Talayco2c0dba32010-03-06 22:47:06 -0800393def die(msg, exit_val=1):
394 print msg
395 logging.critical(msg)
396 sys.exit(exit_val)
397
Dan Talayco79f36082010-03-11 16:53:53 -0800398def _space_to(n, str):
399 """
400 Generate a string of spaces to achieve width n given string str
401 If length of str >= n, return one space
402 """
403 spaces = n - len(str)
404 if spaces > 0:
405 return " " * spaces
406 return " "
407
Dan Talayco48370102010-03-03 15:17:33 -0800408#
409# Main script
410#
411
Rich Lane477f4812012-10-04 22:49:00 -0700412# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800413(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700414oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800415
Rich Laned7a85c42012-09-28 15:38:45 -0700416logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100417xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700418logging.info("++++++++ " + time.asctime() + " ++++++++")
419
Rich Lane9fd05682013-01-10 15:30:38 -0800420# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700421name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
422sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800423
424# HACK: testutils.py imports controller.py, which needs the ofp module
425import oftest.testutils
426
Rich Lanee284b6b2012-10-03 09:19:58 -0700427# Allow tests to import each other
428sys.path.append(config["test_dir"])
429
Rich Lanec76b09a2013-01-02 16:53:22 -0800430test_specs = args
431if config["test_spec"] != "":
432 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
433 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800434if config["test_file"] != None:
435 with open(config["test_file"], 'r') as f:
436 for line in f:
437 line, _, _ = line.partition('#') # remove comments
438 line = line.strip()
439 if line:
440 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800441if test_specs == []:
442 test_specs = ["standard"]
443
Rich Laned8e45482013-01-02 17:36:21 -0800444test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800445
446# Check if test list is requested; display and exit if so
447if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700448 mod_count = 0
449 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800450 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800451 print """\
452Tests are shown grouped by module. If a test is in any groups beyond "standard"
453and its module's group then they are shown in parentheses."""
454 print
455 print """\
456Tests marked with '*' are non-standard and may require vendor extensions or
457special switch configuration. These are not part of the "standard" test group."""
458 print
459 print """\
460Tests marked with '!' are disabled because they are experimental, special-purpose,
461or are too long to be run normally. These are not part of the "standard" test
462group or their module's test group."""
463 print
464 print "Tests marked (TP1) after name take --test-params including:"
465 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800466 print
467 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700468 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700469 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800470 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800471 start_str = " Module " + mod.__name__ + ": "
472 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700473 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700474 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800475 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700476 desc = desc.split('\n')[0]
477 except:
478 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800479 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800480 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800481 if groups:
482 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800483 if hasattr(test, "_versions"):
484 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Rich Lanee811e7b2013-01-03 13:36:54 -0800485 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
486 test._disabled and "!" or " ",
487 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700488 if len(start_str) > 22:
489 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800490 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700491 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800492 print
Rich Lane15f64de2012-10-04 21:25:57 -0700493 print "%d modules shown with a total of %d tests" % \
494 (mod_count, test_count)
495 print
Rich Lane37f42112013-01-03 13:41:49 -0800496 print "Test groups: %s" % (', '.join(sorted(all_groups)))
497
Dan Talayco2c0dba32010-03-06 22:47:06 -0800498 sys.exit(0)
499
Rich Lane5a9a1922013-01-11 14:29:30 -0800500test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800501
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700502# Check if test list is requested; display and exit if so
503if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700504 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700505 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800506 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700507 sys.exit(0)
508
Dan Talayco2c0dba32010-03-06 22:47:06 -0800509# Generate the test suite
510#@todo Decide if multiple suites are ever needed
511suite = unittest.TestSuite()
512
Rich Lane15f64de2012-10-04 21:25:57 -0700513for (modname, (mod, tests)) in test_modules.items():
514 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800515 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800516
Rich Lane51590f62012-10-09 15:06:29 -0700517# Allow platforms to import each other
518sys.path.append(config["platform_dir"])
519
Rich Lane8aebc5e2012-09-25 17:57:53 -0700520# Load the platform module
521platform_name = config["platform"]
522logging.info("Importing platform: " + platform_name)
523platform_mod = None
524try:
Rich Lane483e1542012-10-05 09:29:39 -0700525 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700526except:
527 logging.warn("Failed to import " + platform_name + " platform module")
528 raise
Dan Talayco48370102010-03-03 15:17:33 -0800529
530try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700531 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800532except:
533 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700534 raise
Dan Talayco48370102010-03-03 15:17:33 -0800535
536if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700537 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800538
539logging.debug("Configuration: " + str(config))
540logging.info("OF port map: " + str(config["port_map"]))
541
Rich Lanee55abf72012-07-26 20:11:42 -0700542oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700543oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700544oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700545
Rich Laneee57ad02012-07-13 15:40:36 -0700546if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700547 print "ERROR: Super-user privileges required. Please re-run with " \
548 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700549 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700550
Rich Lane8592bec2012-09-03 09:06:59 -0700551if config["random_seed"] is not None:
552 logging.info("Random seed: %d" % config["random_seed"])
553 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700554else:
555 # Generate random seed and report to log file
556 seed = random.randrange(100000000)
557 logging.info("Autogen random seed: %d" % seed)
558 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700559
Rich Lane5bd6cf92012-10-04 17:57:24 -0700560# Remove python's signal handler which raises KeyboardError. Exiting from an
561# exception waits for all threads to terminate which might not happen.
562signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700563
564if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100565 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700566
Rich Lane2c7812c2012-12-27 17:52:23 -0800567 # Set up the dataplane
568 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700569 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800570 for of_port, ifname in config["port_map"].items():
571 oftest.dataplane_instance.port_add(ifname, of_port)
572
Dan Talaycoac25cf32010-07-20 14:08:28 -0700573 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100574 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100575 try:
576 import xmlrunner # fail-fast if module missing
577 except ImportError as ex:
578 oftest.dataplane_instance.kill()
579 profiler_teardown(profiler)
580 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100581 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
582 outsuffix="",
583 verbosity=2)
584 result = runner.run(suite)
585 else:
586 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700587 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700588 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700589 ts = " tests"
Stephen Finucane6219af72014-05-14 21:08:30 +0100590 if oftest.testutils.skipped_test_count == 1:
591 ts = " test"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700592 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
593 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700594 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800595
596 # Shutdown the dataplane
597 oftest.dataplane_instance.kill()
598 oftest.dataplane_instance = None
599
Stephen Finucane6219af72014-05-14 21:08:30 +0100600 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700601
Rich Lane50d42eb2012-07-16 11:57:03 -0700602 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700603 # exit(1) hangs sometimes
604 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700605 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700606 os._exit(1)