blob: 297c3f3e89b8a18fbaabec3396c6dabd00d7ef44 [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
macauleyc4540792015-08-03 09:40:05 +080036if os.path.exists(os.path.join(ROOT_DIR, 'accton')):
37 PY_ACCTON_DIR = os.path.join(ROOT_DIR, 'accton')
38 sys.path.insert(0, PY_ACCTON_DIR)
39
Rich Lane477f4812012-10-04 22:49:00 -070040import oftest
41from oftest import config
Rich Lanee55abf72012-07-26 20:11:42 -070042import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080043import oftest.help_formatter
Rich Lane3f7098c2013-03-12 10:28:32 -070044import loxi
Dan Talaycoba3745c2010-07-21 21:51:08 -070045
Dan Talayco48370102010-03-03 15:17:33 -080046##@var DEBUG_LEVELS
47# Map from strings to debugging levels
48DEBUG_LEVELS = {
49 'debug' : logging.DEBUG,
50 'verbose' : logging.DEBUG,
51 'info' : logging.INFO,
52 'warning' : logging.WARNING,
53 'warn' : logging.WARNING,
54 'error' : logging.ERROR,
55 'critical' : logging.CRITICAL
56}
57
Stephen Finucane7424a742014-05-15 19:46:06 +010058##@var CONFIG_DEFAULT
Dan Talayco48370102010-03-03 15:17:33 -080059# The default configuration dictionary for OFT
Stephen Finucane7424a742014-05-15 19:46:06 +010060CONFIG_DEFAULT = {
Rich Lane95f078b2013-01-06 13:24:58 -080061 # Miscellaneous options
62 "list" : False,
63 "list_test_names" : False,
64 "allow_user" : False,
65
66 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080067 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080068 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070069 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080070
71 # Switch connection options
72 "controller_host" : "0.0.0.0", # For passive bind
Rich Lane4d1f3eb2013-10-03 13:45:57 -070073 "controller_port" : 6653,
Rich Lane95f078b2013-01-06 13:24:58 -080074 "switch_ip" : None, # If not none, actively connect to switch
Saurav Dase94ba572017-04-28 17:47:21 -070075 "switch_type" : None, # If not none, adapt flows to pipeline differences for switch type
Rich Lane15f26322013-01-08 11:23:24 -080076 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080077 "platform_args" : None,
Stephen Finucane7424a742014-05-15 19:46:06 +010078 "platform_dir" : os.path.join(ROOT_DIR, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080079 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080080 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080081
82 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080083 "log_file" : "oft.log",
Rich Lane69fd8e02013-08-23 16:23:42 -070084 "log_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080085 "debug" : "verbose",
Rich Lane9631f002014-03-21 18:05:16 -070086 "profile" : False,
Stephen Finucane6219af72014-05-14 21:08:30 +010087 "profile_file" : "profile.out",
Stephen Finucanee016cf22014-04-16 22:04:11 +010088 "xunit" : False,
89 "xunit_dir" : "xunit",
Rich Lane95f078b2013-01-06 13:24:58 -080090
91 # Test behavior options
92 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070093 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070094 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070095 "default_timeout" : 2.0,
96 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000097 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070098 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000099 "disable_ipv6" : False,
Rich Lane62b4fb12015-04-02 18:04:10 -0700100 "random_order" : False,
macauleyc1b4bd72015-07-16 15:29:45 +0800101 "dump_packet" : True,
macauleyc2ad42c2015-07-17 15:59:15 +0800102 "cicada_poject" : False,
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700103 "force_ofdpa_restart": False,
Rich Lane95f078b2013-01-06 13:24:58 -0800104 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -0800105 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -0800106}
107
Rich Lane95f078b2013-01-06 13:24:58 -0800108def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800109 """
110 Set up the configuration including parsing the arguments
111
Dan Talayco48370102010-03-03 15:17:33 -0800112 @return A pair (config, args) where config is an config
113 object and args is any additional arguments from the command line
114 """
115
Rich Lane4113a582013-01-03 10:13:02 -0800116 usage = "usage: %prog [options] (test|group)..."
117
Rich Lane95f078b2013-01-06 13:24:58 -0800118 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800119OFTest is a framework and set of tests for validating OpenFlow switches.
120
121The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700122connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800123the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
124dataplane.
125
126If no positional arguments are given then OFTest will run all tests that
127depend only on standard OpenFlow 1.0. Otherwise each positional argument
128is interpreted as either a test name or a test group name. The union of
129these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800130--list option. Tests and groups can be subtracted from the result by
131prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800132"""
133
Rich Lane2d6d4822013-01-08 10:49:16 -0800134 # Parse --interface
135 def check_interface(option, opt, value):
136 try:
137 ofport, interface = value.split('@', 1)
138 ofport = int(ofport)
139 except ValueError:
140 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
141 return (ofport, interface)
142
143 class Option(optparse.Option):
144 TYPES = optparse.Option.TYPES + ("interface",)
145 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
146 TYPE_CHECKER["interface"] = check_interface
147
Rich Lane95f078b2013-01-06 13:24:58 -0800148 parser = optparse.OptionParser(version="%prog 0.1",
149 usage=usage,
150 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800151 formatter=oftest.help_formatter.HelpFormatter(),
152 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800153
154 # Set up default values
Stephen Finucane7424a742014-05-15 19:46:06 +0100155 parser.set_defaults(**CONFIG_DEFAULT)
Dan Talayco48370102010-03-03 15:17:33 -0800156
Dan Talayco2c0dba32010-03-06 22:47:06 -0800157 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700158 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800159 parser.add_option("--list-test-names", action='store_true',
160 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700161 parser.add_option("--allow-user", action="store_true",
162 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800163
164 group = optparse.OptionGroup(parser, "Test selection options")
165 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
166 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
167 group.add_option("--test-dir", type="string", help="Directory containing tests")
168 parser.add_option_group(group)
169
170 group = optparse.OptionGroup(parser, "Switch connection options")
171 group.add_option("-H", "--host", dest="controller_host",
172 help="IP address to listen on (default %default)")
173 group.add_option("-p", "--port", dest="controller_port",
174 type="int", help="Port number to listen on (default %default)")
175 group.add_option("-S", "--switch-ip", dest="switch_ip",
176 help="If set, actively connect to this switch by IP")
Saurav Dase94ba572017-04-28 17:47:21 -0700177 group.add_option("-Y", "--switch-type", dest="switch_type",
178 help="If set to qmx, flows will adapt to pipline differences")
Rich Lane95f078b2013-01-06 13:24:58 -0800179 group.add_option("-P", "--platform", help="Platform module name (default %default)")
180 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
181 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800182 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
183 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 -0700184 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800185 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800186 parser.add_option_group(group)
187
188 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700189 group.add_option("--log-file", help="Name of log file (default %default)")
190 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800191 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
192 group.add_option("--debug", choices=dbg_lvl_names,
193 help="Debug lvl: debug, info, warning, error, critical (default %default)")
194 group.add_option("-v", "--verbose", action="store_const", dest="debug",
195 const="verbose", help="Shortcut for --debug=verbose")
196 group.add_option("-q", "--quiet", action="store_const", dest="debug",
197 const="warning", help="Shortcut for --debug=warning")
Stephen Finucane6219af72014-05-14 21:08:30 +0100198 group.add_option("--profile", action="store_true", help="Enable Python profiling")
199 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100200 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
201 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800202 parser.add_option_group(group)
203
204 group = optparse.OptionGroup(parser, "Test behavior options")
205 group.add_option("--relax", action="store_true",
206 help="Relax packet match checks allowing other packets")
207 test_params_help = """Set test parameters: key=val;... (see --list)
208 """
209 group.add_option("-t", "--test-params", help=test_params_help)
210 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700211 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700212 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700213 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700214 group.add_option("--default-negative-timeout", type=float,
215 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800216 group.add_option("--minsize", type="int",
217 help="Minimum allowable packet size on the dataplane.")
218 group.add_option("--random-seed", type="int",
219 help="Random number generator seed")
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700220 group.add_option("--force-ofdpa-restart",
221 help="If set force ofdpa restart on user@switchIP")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000222 group.add_option("--disable-ipv6", action="store_true",
223 help="Disable IPv6 tests")
Rich Lane62b4fb12015-04-02 18:04:10 -0700224 group.add_option("--random-order", action="store_true",
225 help="Randomize order of tests")
macauleyc1b4bd72015-07-16 15:29:45 +0800226 group.add_option("--dump_packet", action="store_true",
macauleyc2ad42c2015-07-17 15:59:15 +0800227 help="Dump packet content on log when verify packet fail")
228 group.add_option("--cicada_poject", action="store_true",
229 help="True verify Cicada behavior, False verify AOS behaviro")
Rich Lane95f078b2013-01-06 13:24:58 -0800230 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000231
Dan Talayco48370102010-03-03 15:17:33 -0800232 # Might need this if other parsers want command line
233 # parser.allow_interspersed_args = False
234 (options, args) = parser.parse_args()
235
Rich Lane74b13d12013-05-03 17:58:50 -0700236 # If --test-dir wasn't given, pick one based on the OpenFlow version
237 if options.test_dir == None:
238 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100239 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700240 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100241 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700242
Rich Lane95f078b2013-01-06 13:24:58 -0800243 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100244 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800245 for key in config.keys():
246 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800247
248 return (config, args)
249
250def logging_setup(config):
251 """
252 Set up logging based on config
253 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700254
255 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
256
257 if config["log_dir"] != None:
258 if os.path.exists(config["log_dir"]):
259 import shutil
260 shutil.rmtree(config["log_dir"])
261 os.makedirs(config["log_dir"])
262 else:
263 if os.path.exists(config["log_file"]):
264 os.remove(config["log_file"])
265
266 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800267
Stephen Finucanee016cf22014-04-16 22:04:11 +0100268def xunit_setup(config):
269 """
270 Set up xUnit output based on config
271 """
272
273 if not config["xunit"]:
274 return
275
Stephen Finucane6219af72014-05-14 21:08:30 +0100276 if os.path.exists(config["xunit_dir"]):
277 import shutil
278 shutil.rmtree(config["xunit_dir"])
279 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100280
Rich Lane472aaea2013-08-27 09:27:38 -0700281def pcap_setup(config):
282 """
283 Set up dataplane packet capturing based on config
284 """
285
286 if config["log_dir"] == None:
287 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
288 oftest.dataplane_instance.start_pcap(filename)
289 else:
290 # start_pcap is called per-test in base_tests
291 pass
292
Stephen Finucane6219af72014-05-14 21:08:30 +0100293def profiler_setup(config):
294 """
295 Set up profiler based on config
296 """
297
298 if not config["profile"]:
299 return
300
301 import cProfile
302 profiler = cProfile.Profile()
303 profiler.enable()
304
305 return profiler
306
307def profiler_teardown(profiler):
308 """
309 Tear down profiler based on config
310 """
311
312 if not config["profile"]:
313 return
314
315 profiler.disable()
316 profiler.dump_stats(config["profile_file"])
317
Rich Lane472aaea2013-08-27 09:27:38 -0700318
Rich Lane943be672012-10-04 19:20:16 -0700319def load_test_modules(config):
320 """
321 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800322
Rich Lane943be672012-10-04 19:20:16 -0700323 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800324
Rich Lanecc45b8e2013-01-02 15:55:02 -0800325 Also updates the _groups member to include "standard" and
326 module test groups if appropriate.
327
Dan Talayco2c0dba32010-03-06 22:47:06 -0800328 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700329 @returns A dictionary from test module names to tuples of
330 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800331 """
332
Rich Lane943be672012-10-04 19:20:16 -0700333 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800334
Rich Lane943be672012-10-04 19:20:16 -0700335 for root, dirs, filenames in os.walk(config["test_dir"]):
336 # Iterate over each python file
337 for filename in fnmatch.filter(filenames, '[!.]*.py'):
338 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800339
Rich Lane943be672012-10-04 19:20:16 -0700340 try:
341 if sys.modules.has_key(modname):
342 mod = sys.modules[modname]
343 else:
344 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
345 except:
346 logging.warning("Could not import file " + filename)
347 raise
Rich Lane520e4152012-07-09 16:18:16 -0700348
Rich Lane943be672012-10-04 19:20:16 -0700349 # Find all testcases defined in the module
350 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700351 issubclass(v, unittest.TestCase) and
352 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700353 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800354 for (testname, test) in tests.items():
355 # Set default annotation values
356 if not hasattr(test, "_groups"):
357 test._groups = []
358 if not hasattr(test, "_nonstandard"):
359 test._nonstandard = False
360 if not hasattr(test, "_disabled"):
361 test._disabled = False
362
363 # Put test in its module's test group
364 if not test._disabled:
365 test._groups.append(modname)
366
367 # Put test in the standard test group
368 if not test._disabled and not test._nonstandard:
369 test._groups.append("standard")
370 test._groups.append("all") # backwards compatibility
371
Rich Lane943be672012-10-04 19:20:16 -0700372 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700373
Rich Lane943be672012-10-04 19:20:16 -0700374 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800375
Rich Lane5a9a1922013-01-11 14:29:30 -0800376def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700377 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800378 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800379 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800380 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700381 @param test_modules Same format as the output of load_test_modules.
382 @returns Same format as the output of load_test_modules.
383 """
384 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800385 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700386 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800387
388 if e.startswith('^'):
389 negated = True
390 e = e[1:]
391 else:
392 negated = False
393
Rich Lane15f64de2012-10-04 21:25:57 -0700394 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800395 for (testname, test) in tests.items():
396 if e in test._groups or e == "%s.%s" % (modname, testname):
397 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800398 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800399 if not hasattr(test, "_versions") or version in test._versions:
400 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800401 else:
402 if modname in result and testname in result[modname][1]:
403 del result[modname][1][testname]
404 if not result[modname][1]:
405 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800406 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800407
Rich Lane15f64de2012-10-04 21:25:57 -0700408 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800409 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800410
Rich Lane15f64de2012-10-04 21:25:57 -0700411 return result
412
Dan Talayco2c0dba32010-03-06 22:47:06 -0800413def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800414 logging.critical(msg)
415 sys.exit(exit_val)
416
Dan Talayco48370102010-03-03 15:17:33 -0800417#
418# Main script
419#
420
Rich Lane477f4812012-10-04 22:49:00 -0700421# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800422(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700423oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800424
Rich Laned7a85c42012-09-28 15:38:45 -0700425logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100426xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700427logging.info("++++++++ " + time.asctime() + " ++++++++")
428
Rich Lane9fd05682013-01-10 15:30:38 -0800429# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700430name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
431sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800432
433# HACK: testutils.py imports controller.py, which needs the ofp module
434import oftest.testutils
435
Rich Lanee284b6b2012-10-03 09:19:58 -0700436# Allow tests to import each other
437sys.path.append(config["test_dir"])
438
Rich Lanec76b09a2013-01-02 16:53:22 -0800439test_specs = args
440if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100441 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800442 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800443if config["test_file"] != None:
444 with open(config["test_file"], 'r') as f:
445 for line in f:
446 line, _, _ = line.partition('#') # remove comments
447 line = line.strip()
448 if line:
449 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800450if test_specs == []:
451 test_specs = ["standard"]
452
Rich Laned8e45482013-01-02 17:36:21 -0800453test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800454
455# Check if test list is requested; display and exit if so
456if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700457 mod_count = 0
458 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800459 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100460
461 print("""
462Tests are shown grouped by module. If a test is in any groups beyond
463"standard" and its module's group then they are shown in parentheses.
464
Rich Lanee811e7b2013-01-03 13:36:54 -0800465Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100466special switch configuration. These are not part of the "standard" test group.
467
468Tests marked with '!' are disabled because they are experimental,
469special-purpose, or are too long to be run normally. These are not part of
470the "standard" test group or their module's test group.
471
472Tests marked (TP1) after name take --test-params including:
473
474 'vid=N;strip_vlan=bool;add_vlan=bool'
475
476Test List:
477""")
Rich Lane943be672012-10-04 19:20:16 -0700478 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700479 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800480 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100481 print(" Module %13s: %s" % (mod.__name__, desc))
482
Rich Lane943be672012-10-04 19:20:16 -0700483 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100484 desc = (test.__doc__ or "No description").strip().split('\n')[0]
485
Rich Lanee811e7b2013-01-03 13:36:54 -0800486 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800487 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800488 if groups:
489 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800490 if hasattr(test, "_versions"):
491 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100492
Rich Lanee811e7b2013-01-03 13:36:54 -0800493 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
494 test._disabled and "!" or " ",
495 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100496 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700497 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800498 print
Stephen Finucane01588652014-05-15 20:03:12 +0100499 print("'%d' modules shown with a total of '%d' tests\n" %
500 (mod_count, test_count))
501 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800502
Dan Talayco2c0dba32010-03-06 22:47:06 -0800503 sys.exit(0)
504
Rich Lane5a9a1922013-01-11 14:29:30 -0800505test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800506
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700507# Check if test list is requested; display and exit if so
508if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700509 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700510 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100511 print("%s.%s" % (modname, testname))
512
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700513 sys.exit(0)
514
Dan Talayco2c0dba32010-03-06 22:47:06 -0800515# Generate the test suite
516#@todo Decide if multiple suites are ever needed
517suite = unittest.TestSuite()
518
Rich Lane62b4fb12015-04-02 18:04:10 -0700519sorted_tests = []
Rich Lanec72ef462015-04-02 17:52:44 -0700520for (modname, (mod, tests)) in sorted(test_modules.items()):
521 for (testname, test) in sorted(tests.items()):
Rich Lane62b4fb12015-04-02 18:04:10 -0700522 sorted_tests.append(test)
523
524if config["random_order"]:
525 random.shuffle(sorted_tests)
526
527for test in sorted_tests:
528 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800529
Rich Lane51590f62012-10-09 15:06:29 -0700530# Allow platforms to import each other
531sys.path.append(config["platform_dir"])
532
Rich Lane8aebc5e2012-09-25 17:57:53 -0700533# Load the platform module
534platform_name = config["platform"]
535logging.info("Importing platform: " + platform_name)
536platform_mod = None
537try:
Rich Lane483e1542012-10-05 09:29:39 -0700538 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700539except:
540 logging.warn("Failed to import " + platform_name + " platform module")
541 raise
Dan Talayco48370102010-03-03 15:17:33 -0800542
543try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700544 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800545except:
546 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700547 raise
Dan Talayco48370102010-03-03 15:17:33 -0800548
549if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700550 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800551
552logging.debug("Configuration: " + str(config))
553logging.info("OF port map: " + str(config["port_map"]))
554
Rich Lanee55abf72012-07-26 20:11:42 -0700555oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700556oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700557oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700558
Rich Laneee57ad02012-07-13 15:40:36 -0700559if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100560 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700561 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700562
Rich Lane8592bec2012-09-03 09:06:59 -0700563if config["random_seed"] is not None:
564 logging.info("Random seed: %d" % config["random_seed"])
565 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700566else:
567 # Generate random seed and report to log file
568 seed = random.randrange(100000000)
569 logging.info("Autogen random seed: %d" % seed)
570 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700571
Rich Lane5bd6cf92012-10-04 17:57:24 -0700572# Remove python's signal handler which raises KeyboardError. Exiting from an
573# exception waits for all threads to terminate which might not happen.
574signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700575
576if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100577 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700578
Rich Lane2c7812c2012-12-27 17:52:23 -0800579 # Set up the dataplane
580 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700581 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800582 for of_port, ifname in config["port_map"].items():
583 oftest.dataplane_instance.port_add(ifname, of_port)
584
Dan Talaycoac25cf32010-07-20 14:08:28 -0700585 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100586 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100587 try:
588 import xmlrunner # fail-fast if module missing
589 except ImportError as ex:
590 oftest.dataplane_instance.kill()
591 profiler_teardown(profiler)
592 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100593 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
594 outsuffix="",
595 verbosity=2)
596 result = runner.run(suite)
597 else:
598 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700599 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700600 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100601 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
602 logging.info(message)
603 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800604
605 # Shutdown the dataplane
606 oftest.dataplane_instance.kill()
607 oftest.dataplane_instance = None
608
Stephen Finucane6219af72014-05-14 21:08:30 +0100609 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700610
Rich Lane50d42eb2012-07-16 11:57:03 -0700611 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700612 # exit(1) hangs sometimes
613 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700614 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700615 os._exit(1)