blob: c34d4e0049f3ced5881d292ec91b6184c6107325 [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
Stephen Finucane01588652014-05-15 20:03:12 +010015from __future__ import print_function
16
Dan Talayco48370102010-03-03 15:17:33 -080017import sys
Rich Lane95f078b2013-01-06 13:24:58 -080018import optparse
Dan Talayco48370102010-03-03 15:17:33 -080019import logging
20import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080021import time
Brandon Heller446c1432010-04-01 12:43:27 -070022import os
Rich Lane6b452bc2012-07-09 16:52:21 -070023import imp
Rich Lane8592bec2012-09-03 09:06:59 -070024import random
Rich Lane5bd6cf92012-10-04 17:57:24 -070025import signal
Rich Lane943be672012-10-04 19:20:16 -070026import fnmatch
Rich Lane2d6d4822013-01-08 10:49:16 -080027import copy
Dan Talayco48370102010-03-03 15:17:33 -080028
Stephen Finucane7424a742014-05-15 19:46:06 +010029ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
Rich Lanefadf3452012-10-03 16:23:37 -070030
Stephen Finucane7424a742014-05-15 19:46:06 +010031PY_SRC_DIR = os.path.join(ROOT_DIR, 'src', 'python')
32if os.path.exists(os.path.join(PY_SRC_DIR, 'oftest')):
Rich Lane39878042012-07-09 14:45:35 -070033 # Running from source tree
Stephen Finucane7424a742014-05-15 19:46:06 +010034 sys.path.insert(0, PY_SRC_DIR)
Rich Lane39878042012-07-09 14:45:35 -070035
Rich Lane477f4812012-10-04 22:49:00 -070036import oftest
37from oftest import config
Rich Lanee55abf72012-07-26 20:11:42 -070038import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080039import oftest.help_formatter
Rich Lane3f7098c2013-03-12 10:28:32 -070040import loxi
Dan Talaycoba3745c2010-07-21 21:51:08 -070041
Dan Talayco48370102010-03-03 15:17:33 -080042##@var DEBUG_LEVELS
43# Map from strings to debugging levels
44DEBUG_LEVELS = {
45 'debug' : logging.DEBUG,
46 'verbose' : logging.DEBUG,
47 'info' : logging.INFO,
48 'warning' : logging.WARNING,
49 'warn' : logging.WARNING,
50 'error' : logging.ERROR,
51 'critical' : logging.CRITICAL
52}
53
Stephen Finucane7424a742014-05-15 19:46:06 +010054##@var CONFIG_DEFAULT
Dan Talayco48370102010-03-03 15:17:33 -080055# The default configuration dictionary for OFT
Stephen Finucane7424a742014-05-15 19:46:06 +010056CONFIG_DEFAULT = {
Rich Lane95f078b2013-01-06 13:24:58 -080057 # Miscellaneous options
58 "list" : False,
59 "list_test_names" : False,
60 "allow_user" : False,
61
62 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080063 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080064 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070065 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080066
67 # Switch connection options
68 "controller_host" : "0.0.0.0", # For passive bind
Rich Lane4d1f3eb2013-10-03 13:45:57 -070069 "controller_port" : 6653,
Rich Lane95f078b2013-01-06 13:24:58 -080070 "switch_ip" : None, # If not none, actively connect to switch
Rich Lane15f26322013-01-08 11:23:24 -080071 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080072 "platform_args" : None,
Stephen Finucane7424a742014-05-15 19:46:06 +010073 "platform_dir" : os.path.join(ROOT_DIR, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080074 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080075 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080076
77 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080078 "log_file" : "oft.log",
Rich Lane69fd8e02013-08-23 16:23:42 -070079 "log_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080080 "debug" : "verbose",
Rich Lane9631f002014-03-21 18:05:16 -070081 "profile" : False,
Stephen Finucane6219af72014-05-14 21:08:30 +010082 "profile_file" : "profile.out",
Stephen Finucanee016cf22014-04-16 22:04:11 +010083 "xunit" : False,
84 "xunit_dir" : "xunit",
Rich Lane95f078b2013-01-06 13:24:58 -080085
86 # Test behavior options
87 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070088 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070089 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070090 "default_timeout" : 2.0,
91 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000092 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070093 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000094 "disable_ipv6" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080095
96 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080097 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -080098}
99
Rich Lane95f078b2013-01-06 13:24:58 -0800100def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800101 """
102 Set up the configuration including parsing the arguments
103
Dan Talayco48370102010-03-03 15:17:33 -0800104 @return A pair (config, args) where config is an config
105 object and args is any additional arguments from the command line
106 """
107
Rich Lane4113a582013-01-03 10:13:02 -0800108 usage = "usage: %prog [options] (test|group)..."
109
Rich Lane95f078b2013-01-06 13:24:58 -0800110 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800111OFTest is a framework and set of tests for validating OpenFlow switches.
112
113The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700114connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800115the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
116dataplane.
117
118If no positional arguments are given then OFTest will run all tests that
119depend only on standard OpenFlow 1.0. Otherwise each positional argument
120is interpreted as either a test name or a test group name. The union of
121these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800122--list option. Tests and groups can be subtracted from the result by
123prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800124"""
125
Rich Lane2d6d4822013-01-08 10:49:16 -0800126 # Parse --interface
127 def check_interface(option, opt, value):
128 try:
129 ofport, interface = value.split('@', 1)
130 ofport = int(ofport)
131 except ValueError:
132 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
133 return (ofport, interface)
134
135 class Option(optparse.Option):
136 TYPES = optparse.Option.TYPES + ("interface",)
137 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
138 TYPE_CHECKER["interface"] = check_interface
139
Rich Lane95f078b2013-01-06 13:24:58 -0800140 parser = optparse.OptionParser(version="%prog 0.1",
141 usage=usage,
142 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800143 formatter=oftest.help_formatter.HelpFormatter(),
144 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800145
146 # Set up default values
Stephen Finucane7424a742014-05-15 19:46:06 +0100147 parser.set_defaults(**CONFIG_DEFAULT)
Dan Talayco48370102010-03-03 15:17:33 -0800148
Dan Talayco2c0dba32010-03-06 22:47:06 -0800149 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700150 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800151 parser.add_option("--list-test-names", action='store_true',
152 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700153 parser.add_option("--allow-user", action="store_true",
154 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800155
156 group = optparse.OptionGroup(parser, "Test selection options")
157 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
158 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
159 group.add_option("--test-dir", type="string", help="Directory containing tests")
160 parser.add_option_group(group)
161
162 group = optparse.OptionGroup(parser, "Switch connection options")
163 group.add_option("-H", "--host", dest="controller_host",
164 help="IP address to listen on (default %default)")
165 group.add_option("-p", "--port", dest="controller_port",
166 type="int", help="Port number to listen on (default %default)")
167 group.add_option("-S", "--switch-ip", dest="switch_ip",
168 help="If set, actively connect to this switch by IP")
169 group.add_option("-P", "--platform", help="Platform module name (default %default)")
170 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
171 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800172 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
173 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 -0700174 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800175 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800176 parser.add_option_group(group)
177
178 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700179 group.add_option("--log-file", help="Name of log file (default %default)")
180 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800181 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
182 group.add_option("--debug", choices=dbg_lvl_names,
183 help="Debug lvl: debug, info, warning, error, critical (default %default)")
184 group.add_option("-v", "--verbose", action="store_const", dest="debug",
185 const="verbose", help="Shortcut for --debug=verbose")
186 group.add_option("-q", "--quiet", action="store_const", dest="debug",
187 const="warning", help="Shortcut for --debug=warning")
Stephen Finucane6219af72014-05-14 21:08:30 +0100188 group.add_option("--profile", action="store_true", help="Enable Python profiling")
189 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100190 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
191 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800192 parser.add_option_group(group)
193
194 group = optparse.OptionGroup(parser, "Test behavior options")
195 group.add_option("--relax", action="store_true",
196 help="Relax packet match checks allowing other packets")
197 test_params_help = """Set test parameters: key=val;... (see --list)
198 """
199 group.add_option("-t", "--test-params", help=test_params_help)
200 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700201 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700202 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700203 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700204 group.add_option("--default-negative-timeout", type=float,
205 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800206 group.add_option("--minsize", type="int",
207 help="Minimum allowable packet size on the dataplane.")
208 group.add_option("--random-seed", type="int",
209 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000210 group.add_option("--disable-ipv6", action="store_true",
211 help="Disable IPv6 tests")
Rich Lane95f078b2013-01-06 13:24:58 -0800212 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000213
Dan Talayco48370102010-03-03 15:17:33 -0800214 # Might need this if other parsers want command line
215 # parser.allow_interspersed_args = False
216 (options, args) = parser.parse_args()
217
Rich Lane74b13d12013-05-03 17:58:50 -0700218 # If --test-dir wasn't given, pick one based on the OpenFlow version
219 if options.test_dir == None:
220 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100221 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700222 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100223 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700224
Rich Lane95f078b2013-01-06 13:24:58 -0800225 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100226 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800227 for key in config.keys():
228 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800229
230 return (config, args)
231
232def logging_setup(config):
233 """
234 Set up logging based on config
235 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700236
237 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
238
239 if config["log_dir"] != None:
240 if os.path.exists(config["log_dir"]):
241 import shutil
242 shutil.rmtree(config["log_dir"])
243 os.makedirs(config["log_dir"])
244 else:
245 if os.path.exists(config["log_file"]):
246 os.remove(config["log_file"])
247
248 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800249
Stephen Finucanee016cf22014-04-16 22:04:11 +0100250def xunit_setup(config):
251 """
252 Set up xUnit output based on config
253 """
254
255 if not config["xunit"]:
256 return
257
Stephen Finucane6219af72014-05-14 21:08:30 +0100258 if os.path.exists(config["xunit_dir"]):
259 import shutil
260 shutil.rmtree(config["xunit_dir"])
261 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100262
Rich Lane472aaea2013-08-27 09:27:38 -0700263def pcap_setup(config):
264 """
265 Set up dataplane packet capturing based on config
266 """
267
268 if config["log_dir"] == None:
269 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
270 oftest.dataplane_instance.start_pcap(filename)
271 else:
272 # start_pcap is called per-test in base_tests
273 pass
274
Stephen Finucane6219af72014-05-14 21:08:30 +0100275def profiler_setup(config):
276 """
277 Set up profiler based on config
278 """
279
280 if not config["profile"]:
281 return
282
283 import cProfile
284 profiler = cProfile.Profile()
285 profiler.enable()
286
287 return profiler
288
289def profiler_teardown(profiler):
290 """
291 Tear down profiler based on config
292 """
293
294 if not config["profile"]:
295 return
296
297 profiler.disable()
298 profiler.dump_stats(config["profile_file"])
299
Rich Lane472aaea2013-08-27 09:27:38 -0700300
Rich Lane943be672012-10-04 19:20:16 -0700301def load_test_modules(config):
302 """
303 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800304
Rich Lane943be672012-10-04 19:20:16 -0700305 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800306
Rich Lanecc45b8e2013-01-02 15:55:02 -0800307 Also updates the _groups member to include "standard" and
308 module test groups if appropriate.
309
Dan Talayco2c0dba32010-03-06 22:47:06 -0800310 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700311 @returns A dictionary from test module names to tuples of
312 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800313 """
314
Rich Lane943be672012-10-04 19:20:16 -0700315 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800316
Rich Lane943be672012-10-04 19:20:16 -0700317 for root, dirs, filenames in os.walk(config["test_dir"]):
318 # Iterate over each python file
319 for filename in fnmatch.filter(filenames, '[!.]*.py'):
320 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800321
Rich Lane943be672012-10-04 19:20:16 -0700322 try:
323 if sys.modules.has_key(modname):
324 mod = sys.modules[modname]
325 else:
326 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
327 except:
328 logging.warning("Could not import file " + filename)
329 raise
Rich Lane520e4152012-07-09 16:18:16 -0700330
Rich Lane943be672012-10-04 19:20:16 -0700331 # Find all testcases defined in the module
332 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700333 issubclass(v, unittest.TestCase) and
334 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700335 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800336 for (testname, test) in tests.items():
337 # Set default annotation values
338 if not hasattr(test, "_groups"):
339 test._groups = []
340 if not hasattr(test, "_nonstandard"):
341 test._nonstandard = False
342 if not hasattr(test, "_disabled"):
343 test._disabled = False
344
345 # Put test in its module's test group
346 if not test._disabled:
347 test._groups.append(modname)
348
349 # Put test in the standard test group
350 if not test._disabled and not test._nonstandard:
351 test._groups.append("standard")
352 test._groups.append("all") # backwards compatibility
353
Rich Lane943be672012-10-04 19:20:16 -0700354 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700355
Rich Lane943be672012-10-04 19:20:16 -0700356 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800357
Rich Lane5a9a1922013-01-11 14:29:30 -0800358def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700359 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800360 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800361 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800362 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700363 @param test_modules Same format as the output of load_test_modules.
364 @returns Same format as the output of load_test_modules.
365 """
366 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800367 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700368 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800369
370 if e.startswith('^'):
371 negated = True
372 e = e[1:]
373 else:
374 negated = False
375
Rich Lane15f64de2012-10-04 21:25:57 -0700376 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800377 for (testname, test) in tests.items():
378 if e in test._groups or e == "%s.%s" % (modname, testname):
379 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800380 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800381 if not hasattr(test, "_versions") or version in test._versions:
382 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800383 else:
384 if modname in result and testname in result[modname][1]:
385 del result[modname][1][testname]
386 if not result[modname][1]:
387 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800388 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800389
Rich Lane15f64de2012-10-04 21:25:57 -0700390 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800391 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800392
Rich Lane15f64de2012-10-04 21:25:57 -0700393 return result
394
Dan Talayco2c0dba32010-03-06 22:47:06 -0800395def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800396 logging.critical(msg)
397 sys.exit(exit_val)
398
Dan Talayco48370102010-03-03 15:17:33 -0800399#
400# Main script
401#
402
Rich Lane477f4812012-10-04 22:49:00 -0700403# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800404(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700405oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800406
Rich Laned7a85c42012-09-28 15:38:45 -0700407logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100408xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700409logging.info("++++++++ " + time.asctime() + " ++++++++")
410
Rich Lane9fd05682013-01-10 15:30:38 -0800411# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700412name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
413sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800414
415# HACK: testutils.py imports controller.py, which needs the ofp module
416import oftest.testutils
417
Rich Lanee284b6b2012-10-03 09:19:58 -0700418# Allow tests to import each other
419sys.path.append(config["test_dir"])
420
Rich Lanec76b09a2013-01-02 16:53:22 -0800421test_specs = args
422if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100423 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800424 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800425if config["test_file"] != None:
426 with open(config["test_file"], 'r') as f:
427 for line in f:
428 line, _, _ = line.partition('#') # remove comments
429 line = line.strip()
430 if line:
431 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800432if test_specs == []:
433 test_specs = ["standard"]
434
Rich Laned8e45482013-01-02 17:36:21 -0800435test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800436
437# Check if test list is requested; display and exit if so
438if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700439 mod_count = 0
440 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800441 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100442
443 print("""
444Tests are shown grouped by module. If a test is in any groups beyond
445"standard" and its module's group then they are shown in parentheses.
446
Rich Lanee811e7b2013-01-03 13:36:54 -0800447Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100448special switch configuration. These are not part of the "standard" test group.
449
450Tests marked with '!' are disabled because they are experimental,
451special-purpose, or are too long to be run normally. These are not part of
452the "standard" test group or their module's test group.
453
454Tests marked (TP1) after name take --test-params including:
455
456 'vid=N;strip_vlan=bool;add_vlan=bool'
457
458Test List:
459""")
Rich Lane943be672012-10-04 19:20:16 -0700460 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700461 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800462 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100463 print(" Module %13s: %s" % (mod.__name__, desc))
464
Rich Lane943be672012-10-04 19:20:16 -0700465 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100466 desc = (test.__doc__ or "No description").strip().split('\n')[0]
467
Rich Lanee811e7b2013-01-03 13:36:54 -0800468 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800469 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800470 if groups:
471 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800472 if hasattr(test, "_versions"):
473 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100474
Rich Lanee811e7b2013-01-03 13:36:54 -0800475 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
476 test._disabled and "!" or " ",
477 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100478 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700479 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800480 print
Stephen Finucane01588652014-05-15 20:03:12 +0100481 print("'%d' modules shown with a total of '%d' tests\n" %
482 (mod_count, test_count))
483 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800484
Dan Talayco2c0dba32010-03-06 22:47:06 -0800485 sys.exit(0)
486
Rich Lane5a9a1922013-01-11 14:29:30 -0800487test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800488
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700489# Check if test list is requested; display and exit if so
490if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700491 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700492 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100493 print("%s.%s" % (modname, testname))
494
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700495 sys.exit(0)
496
Dan Talayco2c0dba32010-03-06 22:47:06 -0800497# Generate the test suite
498#@todo Decide if multiple suites are ever needed
499suite = unittest.TestSuite()
500
Rich Lanec72ef462015-04-02 17:52:44 -0700501for (modname, (mod, tests)) in sorted(test_modules.items()):
502 for (testname, test) in sorted(tests.items()):
Rich Lanecc45b8e2013-01-02 15:55:02 -0800503 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800504
Rich Lane51590f62012-10-09 15:06:29 -0700505# Allow platforms to import each other
506sys.path.append(config["platform_dir"])
507
Rich Lane8aebc5e2012-09-25 17:57:53 -0700508# Load the platform module
509platform_name = config["platform"]
510logging.info("Importing platform: " + platform_name)
511platform_mod = None
512try:
Rich Lane483e1542012-10-05 09:29:39 -0700513 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700514except:
515 logging.warn("Failed to import " + platform_name + " platform module")
516 raise
Dan Talayco48370102010-03-03 15:17:33 -0800517
518try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700519 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800520except:
521 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700522 raise
Dan Talayco48370102010-03-03 15:17:33 -0800523
524if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700525 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800526
527logging.debug("Configuration: " + str(config))
528logging.info("OF port map: " + str(config["port_map"]))
529
Rich Lanee55abf72012-07-26 20:11:42 -0700530oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700531oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700532oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700533
Rich Laneee57ad02012-07-13 15:40:36 -0700534if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100535 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700536 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700537
Rich Lane8592bec2012-09-03 09:06:59 -0700538if config["random_seed"] is not None:
539 logging.info("Random seed: %d" % config["random_seed"])
540 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700541else:
542 # Generate random seed and report to log file
543 seed = random.randrange(100000000)
544 logging.info("Autogen random seed: %d" % seed)
545 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700546
Rich Lane5bd6cf92012-10-04 17:57:24 -0700547# Remove python's signal handler which raises KeyboardError. Exiting from an
548# exception waits for all threads to terminate which might not happen.
549signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700550
551if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100552 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700553
Rich Lane2c7812c2012-12-27 17:52:23 -0800554 # Set up the dataplane
555 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700556 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800557 for of_port, ifname in config["port_map"].items():
558 oftest.dataplane_instance.port_add(ifname, of_port)
559
Dan Talaycoac25cf32010-07-20 14:08:28 -0700560 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100561 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100562 try:
563 import xmlrunner # fail-fast if module missing
564 except ImportError as ex:
565 oftest.dataplane_instance.kill()
566 profiler_teardown(profiler)
567 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100568 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
569 outsuffix="",
570 verbosity=2)
571 result = runner.run(suite)
572 else:
573 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700574 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700575 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100576 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
577 logging.info(message)
578 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800579
580 # Shutdown the dataplane
581 oftest.dataplane_instance.kill()
582 oftest.dataplane_instance = None
583
Stephen Finucane6219af72014-05-14 21:08:30 +0100584 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700585
Rich Lane50d42eb2012-07-16 11:57:03 -0700586 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700587 # exit(1) hangs sometimes
588 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700589 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700590 os._exit(1)