blob: 275e3d325bdf0779919575476508d20e2f62a3c0 [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
Rich Lane15f26322013-01-08 11:23:24 -080075 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080076 "platform_args" : None,
Stephen Finucane7424a742014-05-15 19:46:06 +010077 "platform_dir" : os.path.join(ROOT_DIR, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080078 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080079 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080080
81 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080082 "log_file" : "oft.log",
Rich Lane69fd8e02013-08-23 16:23:42 -070083 "log_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080084 "debug" : "verbose",
Rich Lane9631f002014-03-21 18:05:16 -070085 "profile" : False,
Stephen Finucane6219af72014-05-14 21:08:30 +010086 "profile_file" : "profile.out",
Stephen Finucanee016cf22014-04-16 22:04:11 +010087 "xunit" : False,
88 "xunit_dir" : "xunit",
Rich Lane95f078b2013-01-06 13:24:58 -080089
90 # Test behavior options
91 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070092 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070093 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070094 "default_timeout" : 2.0,
95 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000096 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070097 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000098 "disable_ipv6" : False,
Rich Lane62b4fb12015-04-02 18:04:10 -070099 "random_order" : False,
macauleyc1b4bd72015-07-16 15:29:45 +0800100 "dump_packet" : True,
macauleyc2ad42c2015-07-17 15:59:15 +0800101 "cicada_poject" : False,
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700102 "force_ofdpa_restart": False,
Rich Lane95f078b2013-01-06 13:24:58 -0800103 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -0800104 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -0800105}
106
Rich Lane95f078b2013-01-06 13:24:58 -0800107def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800108 """
109 Set up the configuration including parsing the arguments
110
Dan Talayco48370102010-03-03 15:17:33 -0800111 @return A pair (config, args) where config is an config
112 object and args is any additional arguments from the command line
113 """
114
Rich Lane4113a582013-01-03 10:13:02 -0800115 usage = "usage: %prog [options] (test|group)..."
116
Rich Lane95f078b2013-01-06 13:24:58 -0800117 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800118OFTest is a framework and set of tests for validating OpenFlow switches.
119
120The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700121connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800122the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
123dataplane.
124
125If no positional arguments are given then OFTest will run all tests that
126depend only on standard OpenFlow 1.0. Otherwise each positional argument
127is interpreted as either a test name or a test group name. The union of
128these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800129--list option. Tests and groups can be subtracted from the result by
130prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800131"""
132
Rich Lane2d6d4822013-01-08 10:49:16 -0800133 # Parse --interface
134 def check_interface(option, opt, value):
135 try:
136 ofport, interface = value.split('@', 1)
137 ofport = int(ofport)
138 except ValueError:
139 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
140 return (ofport, interface)
141
142 class Option(optparse.Option):
143 TYPES = optparse.Option.TYPES + ("interface",)
144 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
145 TYPE_CHECKER["interface"] = check_interface
146
Rich Lane95f078b2013-01-06 13:24:58 -0800147 parser = optparse.OptionParser(version="%prog 0.1",
148 usage=usage,
149 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800150 formatter=oftest.help_formatter.HelpFormatter(),
151 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800152
153 # Set up default values
Stephen Finucane7424a742014-05-15 19:46:06 +0100154 parser.set_defaults(**CONFIG_DEFAULT)
Dan Talayco48370102010-03-03 15:17:33 -0800155
Dan Talayco2c0dba32010-03-06 22:47:06 -0800156 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700157 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800158 parser.add_option("--list-test-names", action='store_true',
159 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700160 parser.add_option("--allow-user", action="store_true",
161 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800162
163 group = optparse.OptionGroup(parser, "Test selection options")
164 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
165 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
166 group.add_option("--test-dir", type="string", help="Directory containing tests")
167 parser.add_option_group(group)
168
169 group = optparse.OptionGroup(parser, "Switch connection options")
170 group.add_option("-H", "--host", dest="controller_host",
171 help="IP address to listen on (default %default)")
172 group.add_option("-p", "--port", dest="controller_port",
173 type="int", help="Port number to listen on (default %default)")
174 group.add_option("-S", "--switch-ip", dest="switch_ip",
175 help="If set, actively connect to this switch by IP")
176 group.add_option("-P", "--platform", help="Platform module name (default %default)")
177 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
178 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800179 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
180 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 -0700181 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800182 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800183 parser.add_option_group(group)
184
185 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700186 group.add_option("--log-file", help="Name of log file (default %default)")
187 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800188 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
189 group.add_option("--debug", choices=dbg_lvl_names,
190 help="Debug lvl: debug, info, warning, error, critical (default %default)")
191 group.add_option("-v", "--verbose", action="store_const", dest="debug",
192 const="verbose", help="Shortcut for --debug=verbose")
193 group.add_option("-q", "--quiet", action="store_const", dest="debug",
194 const="warning", help="Shortcut for --debug=warning")
Stephen Finucane6219af72014-05-14 21:08:30 +0100195 group.add_option("--profile", action="store_true", help="Enable Python profiling")
196 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100197 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
198 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800199 parser.add_option_group(group)
200
201 group = optparse.OptionGroup(parser, "Test behavior options")
202 group.add_option("--relax", action="store_true",
203 help="Relax packet match checks allowing other packets")
204 test_params_help = """Set test parameters: key=val;... (see --list)
205 """
206 group.add_option("-t", "--test-params", help=test_params_help)
207 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700208 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700209 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700210 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700211 group.add_option("--default-negative-timeout", type=float,
212 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800213 group.add_option("--minsize", type="int",
214 help="Minimum allowable packet size on the dataplane.")
215 group.add_option("--random-seed", type="int",
216 help="Random number generator seed")
Flavio Castrob01d0aa2016-07-20 16:14:48 -0700217 group.add_option("--force-ofdpa-restart",
218 help="If set force ofdpa restart on user@switchIP")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000219 group.add_option("--disable-ipv6", action="store_true",
220 help="Disable IPv6 tests")
Rich Lane62b4fb12015-04-02 18:04:10 -0700221 group.add_option("--random-order", action="store_true",
222 help="Randomize order of tests")
macauleyc1b4bd72015-07-16 15:29:45 +0800223 group.add_option("--dump_packet", action="store_true",
macauleyc2ad42c2015-07-17 15:59:15 +0800224 help="Dump packet content on log when verify packet fail")
225 group.add_option("--cicada_poject", action="store_true",
226 help="True verify Cicada behavior, False verify AOS behaviro")
Rich Lane95f078b2013-01-06 13:24:58 -0800227 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000228
Dan Talayco48370102010-03-03 15:17:33 -0800229 # Might need this if other parsers want command line
230 # parser.allow_interspersed_args = False
231 (options, args) = parser.parse_args()
232
Rich Lane74b13d12013-05-03 17:58:50 -0700233 # If --test-dir wasn't given, pick one based on the OpenFlow version
234 if options.test_dir == None:
235 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100236 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700237 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100238 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700239
Rich Lane95f078b2013-01-06 13:24:58 -0800240 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100241 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800242 for key in config.keys():
243 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800244
245 return (config, args)
246
247def logging_setup(config):
248 """
249 Set up logging based on config
250 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700251
252 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
253
254 if config["log_dir"] != None:
255 if os.path.exists(config["log_dir"]):
256 import shutil
257 shutil.rmtree(config["log_dir"])
258 os.makedirs(config["log_dir"])
259 else:
260 if os.path.exists(config["log_file"]):
261 os.remove(config["log_file"])
262
263 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800264
Stephen Finucanee016cf22014-04-16 22:04:11 +0100265def xunit_setup(config):
266 """
267 Set up xUnit output based on config
268 """
269
270 if not config["xunit"]:
271 return
272
Stephen Finucane6219af72014-05-14 21:08:30 +0100273 if os.path.exists(config["xunit_dir"]):
274 import shutil
275 shutil.rmtree(config["xunit_dir"])
276 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100277
Rich Lane472aaea2013-08-27 09:27:38 -0700278def pcap_setup(config):
279 """
280 Set up dataplane packet capturing based on config
281 """
282
283 if config["log_dir"] == None:
284 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
285 oftest.dataplane_instance.start_pcap(filename)
286 else:
287 # start_pcap is called per-test in base_tests
288 pass
289
Stephen Finucane6219af72014-05-14 21:08:30 +0100290def profiler_setup(config):
291 """
292 Set up profiler based on config
293 """
294
295 if not config["profile"]:
296 return
297
298 import cProfile
299 profiler = cProfile.Profile()
300 profiler.enable()
301
302 return profiler
303
304def profiler_teardown(profiler):
305 """
306 Tear down profiler based on config
307 """
308
309 if not config["profile"]:
310 return
311
312 profiler.disable()
313 profiler.dump_stats(config["profile_file"])
314
Rich Lane472aaea2013-08-27 09:27:38 -0700315
Rich Lane943be672012-10-04 19:20:16 -0700316def load_test_modules(config):
317 """
318 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800319
Rich Lane943be672012-10-04 19:20:16 -0700320 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800321
Rich Lanecc45b8e2013-01-02 15:55:02 -0800322 Also updates the _groups member to include "standard" and
323 module test groups if appropriate.
324
Dan Talayco2c0dba32010-03-06 22:47:06 -0800325 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700326 @returns A dictionary from test module names to tuples of
327 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800328 """
329
Rich Lane943be672012-10-04 19:20:16 -0700330 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800331
Rich Lane943be672012-10-04 19:20:16 -0700332 for root, dirs, filenames in os.walk(config["test_dir"]):
333 # Iterate over each python file
334 for filename in fnmatch.filter(filenames, '[!.]*.py'):
335 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800336
Rich Lane943be672012-10-04 19:20:16 -0700337 try:
338 if sys.modules.has_key(modname):
339 mod = sys.modules[modname]
340 else:
341 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
342 except:
343 logging.warning("Could not import file " + filename)
344 raise
Rich Lane520e4152012-07-09 16:18:16 -0700345
Rich Lane943be672012-10-04 19:20:16 -0700346 # Find all testcases defined in the module
347 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700348 issubclass(v, unittest.TestCase) and
349 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700350 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800351 for (testname, test) in tests.items():
352 # Set default annotation values
353 if not hasattr(test, "_groups"):
354 test._groups = []
355 if not hasattr(test, "_nonstandard"):
356 test._nonstandard = False
357 if not hasattr(test, "_disabled"):
358 test._disabled = False
359
360 # Put test in its module's test group
361 if not test._disabled:
362 test._groups.append(modname)
363
364 # Put test in the standard test group
365 if not test._disabled and not test._nonstandard:
366 test._groups.append("standard")
367 test._groups.append("all") # backwards compatibility
368
Rich Lane943be672012-10-04 19:20:16 -0700369 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700370
Rich Lane943be672012-10-04 19:20:16 -0700371 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800372
Rich Lane5a9a1922013-01-11 14:29:30 -0800373def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700374 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800375 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800376 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800377 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700378 @param test_modules Same format as the output of load_test_modules.
379 @returns Same format as the output of load_test_modules.
380 """
381 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800382 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700383 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800384
385 if e.startswith('^'):
386 negated = True
387 e = e[1:]
388 else:
389 negated = False
390
Rich Lane15f64de2012-10-04 21:25:57 -0700391 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800392 for (testname, test) in tests.items():
393 if e in test._groups or e == "%s.%s" % (modname, testname):
394 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800395 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800396 if not hasattr(test, "_versions") or version in test._versions:
397 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800398 else:
399 if modname in result and testname in result[modname][1]:
400 del result[modname][1][testname]
401 if not result[modname][1]:
402 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800403 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800404
Rich Lane15f64de2012-10-04 21:25:57 -0700405 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800406 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800407
Rich Lane15f64de2012-10-04 21:25:57 -0700408 return result
409
Dan Talayco2c0dba32010-03-06 22:47:06 -0800410def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800411 logging.critical(msg)
412 sys.exit(exit_val)
413
Dan Talayco48370102010-03-03 15:17:33 -0800414#
415# Main script
416#
417
Rich Lane477f4812012-10-04 22:49:00 -0700418# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800419(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700420oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800421
Rich Laned7a85c42012-09-28 15:38:45 -0700422logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100423xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700424logging.info("++++++++ " + time.asctime() + " ++++++++")
425
Rich Lane9fd05682013-01-10 15:30:38 -0800426# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700427name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
428sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800429
430# HACK: testutils.py imports controller.py, which needs the ofp module
431import oftest.testutils
432
Rich Lanee284b6b2012-10-03 09:19:58 -0700433# Allow tests to import each other
434sys.path.append(config["test_dir"])
435
Rich Lanec76b09a2013-01-02 16:53:22 -0800436test_specs = args
437if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100438 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800439 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800440if config["test_file"] != None:
441 with open(config["test_file"], 'r') as f:
442 for line in f:
443 line, _, _ = line.partition('#') # remove comments
444 line = line.strip()
445 if line:
446 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800447if test_specs == []:
448 test_specs = ["standard"]
449
Rich Laned8e45482013-01-02 17:36:21 -0800450test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800451
452# Check if test list is requested; display and exit if so
453if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700454 mod_count = 0
455 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800456 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100457
458 print("""
459Tests are shown grouped by module. If a test is in any groups beyond
460"standard" and its module's group then they are shown in parentheses.
461
Rich Lanee811e7b2013-01-03 13:36:54 -0800462Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100463special switch configuration. These are not part of the "standard" test group.
464
465Tests marked with '!' are disabled because they are experimental,
466special-purpose, or are too long to be run normally. These are not part of
467the "standard" test group or their module's test group.
468
469Tests marked (TP1) after name take --test-params including:
470
471 'vid=N;strip_vlan=bool;add_vlan=bool'
472
473Test List:
474""")
Rich Lane943be672012-10-04 19:20:16 -0700475 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700476 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800477 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100478 print(" Module %13s: %s" % (mod.__name__, desc))
479
Rich Lane943be672012-10-04 19:20:16 -0700480 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100481 desc = (test.__doc__ or "No description").strip().split('\n')[0]
482
Rich Lanee811e7b2013-01-03 13:36:54 -0800483 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800484 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800485 if groups:
486 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800487 if hasattr(test, "_versions"):
488 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100489
Rich Lanee811e7b2013-01-03 13:36:54 -0800490 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
491 test._disabled and "!" or " ",
492 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100493 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700494 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800495 print
Stephen Finucane01588652014-05-15 20:03:12 +0100496 print("'%d' modules shown with a total of '%d' tests\n" %
497 (mod_count, test_count))
498 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800499
Dan Talayco2c0dba32010-03-06 22:47:06 -0800500 sys.exit(0)
501
Rich Lane5a9a1922013-01-11 14:29:30 -0800502test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800503
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700504# Check if test list is requested; display and exit if so
505if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700506 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700507 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100508 print("%s.%s" % (modname, testname))
509
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700510 sys.exit(0)
511
Dan Talayco2c0dba32010-03-06 22:47:06 -0800512# Generate the test suite
513#@todo Decide if multiple suites are ever needed
514suite = unittest.TestSuite()
515
Rich Lane62b4fb12015-04-02 18:04:10 -0700516sorted_tests = []
Rich Lanec72ef462015-04-02 17:52:44 -0700517for (modname, (mod, tests)) in sorted(test_modules.items()):
518 for (testname, test) in sorted(tests.items()):
Rich Lane62b4fb12015-04-02 18:04:10 -0700519 sorted_tests.append(test)
520
521if config["random_order"]:
522 random.shuffle(sorted_tests)
523
524for test in sorted_tests:
525 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800526
Rich Lane51590f62012-10-09 15:06:29 -0700527# Allow platforms to import each other
528sys.path.append(config["platform_dir"])
529
Rich Lane8aebc5e2012-09-25 17:57:53 -0700530# Load the platform module
531platform_name = config["platform"]
532logging.info("Importing platform: " + platform_name)
533platform_mod = None
534try:
Rich Lane483e1542012-10-05 09:29:39 -0700535 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700536except:
537 logging.warn("Failed to import " + platform_name + " platform module")
538 raise
Dan Talayco48370102010-03-03 15:17:33 -0800539
540try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700541 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800542except:
543 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700544 raise
Dan Talayco48370102010-03-03 15:17:33 -0800545
546if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700547 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800548
549logging.debug("Configuration: " + str(config))
550logging.info("OF port map: " + str(config["port_map"]))
551
Rich Lanee55abf72012-07-26 20:11:42 -0700552oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700553oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700554oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700555
Rich Laneee57ad02012-07-13 15:40:36 -0700556if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100557 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700558 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700559
Rich Lane8592bec2012-09-03 09:06:59 -0700560if config["random_seed"] is not None:
561 logging.info("Random seed: %d" % config["random_seed"])
562 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700563else:
564 # Generate random seed and report to log file
565 seed = random.randrange(100000000)
566 logging.info("Autogen random seed: %d" % seed)
567 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700568
Rich Lane5bd6cf92012-10-04 17:57:24 -0700569# Remove python's signal handler which raises KeyboardError. Exiting from an
570# exception waits for all threads to terminate which might not happen.
571signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700572
573if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100574 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700575
Rich Lane2c7812c2012-12-27 17:52:23 -0800576 # Set up the dataplane
577 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700578 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800579 for of_port, ifname in config["port_map"].items():
580 oftest.dataplane_instance.port_add(ifname, of_port)
581
Dan Talaycoac25cf32010-07-20 14:08:28 -0700582 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100583 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100584 try:
585 import xmlrunner # fail-fast if module missing
586 except ImportError as ex:
587 oftest.dataplane_instance.kill()
588 profiler_teardown(profiler)
589 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100590 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
591 outsuffix="",
592 verbosity=2)
593 result = runner.run(suite)
594 else:
595 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700596 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700597 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100598 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
599 logging.info(message)
600 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800601
602 # Shutdown the dataplane
603 oftest.dataplane_instance.kill()
604 oftest.dataplane_instance = None
605
Stephen Finucane6219af72014-05-14 21:08:30 +0100606 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700607
Rich Lane50d42eb2012-07-16 11:57:03 -0700608 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700609 # exit(1) hangs sometimes
610 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700611 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700612 os._exit(1)