blob: bab5b47a67e5f89a71d2f9f450a7404dbc2063dc [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,
macauleyc1b4bd72015-07-16 15:29:45 +0800102
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")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000217 group.add_option("--disable-ipv6", action="store_true",
218 help="Disable IPv6 tests")
Rich Lane62b4fb12015-04-02 18:04:10 -0700219 group.add_option("--random-order", action="store_true",
220 help="Randomize order of tests")
macauleyc1b4bd72015-07-16 15:29:45 +0800221 group.add_option("--dump_packet", action="store_true",
macauleyc2ad42c2015-07-17 15:59:15 +0800222 help="Dump packet content on log when verify packet fail")
223 group.add_option("--cicada_poject", action="store_true",
224 help="True verify Cicada behavior, False verify AOS behaviro")
Rich Lane95f078b2013-01-06 13:24:58 -0800225 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000226
Dan Talayco48370102010-03-03 15:17:33 -0800227 # Might need this if other parsers want command line
228 # parser.allow_interspersed_args = False
229 (options, args) = parser.parse_args()
230
Rich Lane74b13d12013-05-03 17:58:50 -0700231 # If --test-dir wasn't given, pick one based on the OpenFlow version
232 if options.test_dir == None:
233 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100234 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700235 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100236 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700237
Rich Lane95f078b2013-01-06 13:24:58 -0800238 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100239 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800240 for key in config.keys():
241 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800242
243 return (config, args)
244
245def logging_setup(config):
246 """
247 Set up logging based on config
248 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700249
250 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
251
252 if config["log_dir"] != None:
253 if os.path.exists(config["log_dir"]):
254 import shutil
255 shutil.rmtree(config["log_dir"])
256 os.makedirs(config["log_dir"])
257 else:
258 if os.path.exists(config["log_file"]):
259 os.remove(config["log_file"])
260
261 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800262
Stephen Finucanee016cf22014-04-16 22:04:11 +0100263def xunit_setup(config):
264 """
265 Set up xUnit output based on config
266 """
267
268 if not config["xunit"]:
269 return
270
Stephen Finucane6219af72014-05-14 21:08:30 +0100271 if os.path.exists(config["xunit_dir"]):
272 import shutil
273 shutil.rmtree(config["xunit_dir"])
274 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100275
Rich Lane472aaea2013-08-27 09:27:38 -0700276def pcap_setup(config):
277 """
278 Set up dataplane packet capturing based on config
279 """
280
281 if config["log_dir"] == None:
282 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
283 oftest.dataplane_instance.start_pcap(filename)
284 else:
285 # start_pcap is called per-test in base_tests
286 pass
287
Stephen Finucane6219af72014-05-14 21:08:30 +0100288def profiler_setup(config):
289 """
290 Set up profiler based on config
291 """
292
293 if not config["profile"]:
294 return
295
296 import cProfile
297 profiler = cProfile.Profile()
298 profiler.enable()
299
300 return profiler
301
302def profiler_teardown(profiler):
303 """
304 Tear down profiler based on config
305 """
306
307 if not config["profile"]:
308 return
309
310 profiler.disable()
311 profiler.dump_stats(config["profile_file"])
312
Rich Lane472aaea2013-08-27 09:27:38 -0700313
Rich Lane943be672012-10-04 19:20:16 -0700314def load_test_modules(config):
315 """
316 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800317
Rich Lane943be672012-10-04 19:20:16 -0700318 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800319
Rich Lanecc45b8e2013-01-02 15:55:02 -0800320 Also updates the _groups member to include "standard" and
321 module test groups if appropriate.
322
Dan Talayco2c0dba32010-03-06 22:47:06 -0800323 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700324 @returns A dictionary from test module names to tuples of
325 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800326 """
327
Rich Lane943be672012-10-04 19:20:16 -0700328 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800329
Rich Lane943be672012-10-04 19:20:16 -0700330 for root, dirs, filenames in os.walk(config["test_dir"]):
331 # Iterate over each python file
332 for filename in fnmatch.filter(filenames, '[!.]*.py'):
333 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800334
Rich Lane943be672012-10-04 19:20:16 -0700335 try:
336 if sys.modules.has_key(modname):
337 mod = sys.modules[modname]
338 else:
339 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
340 except:
341 logging.warning("Could not import file " + filename)
342 raise
Rich Lane520e4152012-07-09 16:18:16 -0700343
Rich Lane943be672012-10-04 19:20:16 -0700344 # Find all testcases defined in the module
345 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700346 issubclass(v, unittest.TestCase) and
347 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700348 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800349 for (testname, test) in tests.items():
350 # Set default annotation values
351 if not hasattr(test, "_groups"):
352 test._groups = []
353 if not hasattr(test, "_nonstandard"):
354 test._nonstandard = False
355 if not hasattr(test, "_disabled"):
356 test._disabled = False
357
358 # Put test in its module's test group
359 if not test._disabled:
360 test._groups.append(modname)
361
362 # Put test in the standard test group
363 if not test._disabled and not test._nonstandard:
364 test._groups.append("standard")
365 test._groups.append("all") # backwards compatibility
366
Rich Lane943be672012-10-04 19:20:16 -0700367 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700368
Rich Lane943be672012-10-04 19:20:16 -0700369 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800370
Rich Lane5a9a1922013-01-11 14:29:30 -0800371def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700372 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800373 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800374 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800375 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700376 @param test_modules Same format as the output of load_test_modules.
377 @returns Same format as the output of load_test_modules.
378 """
379 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800380 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700381 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800382
383 if e.startswith('^'):
384 negated = True
385 e = e[1:]
386 else:
387 negated = False
388
Rich Lane15f64de2012-10-04 21:25:57 -0700389 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800390 for (testname, test) in tests.items():
391 if e in test._groups or e == "%s.%s" % (modname, testname):
392 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800393 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800394 if not hasattr(test, "_versions") or version in test._versions:
395 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800396 else:
397 if modname in result and testname in result[modname][1]:
398 del result[modname][1][testname]
399 if not result[modname][1]:
400 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800401 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800402
Rich Lane15f64de2012-10-04 21:25:57 -0700403 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800404 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800405
Rich Lane15f64de2012-10-04 21:25:57 -0700406 return result
407
Dan Talayco2c0dba32010-03-06 22:47:06 -0800408def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800409 logging.critical(msg)
410 sys.exit(exit_val)
411
Dan Talayco48370102010-03-03 15:17:33 -0800412#
413# Main script
414#
415
Rich Lane477f4812012-10-04 22:49:00 -0700416# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800417(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700418oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800419
Rich Laned7a85c42012-09-28 15:38:45 -0700420logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100421xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700422logging.info("++++++++ " + time.asctime() + " ++++++++")
423
Rich Lane9fd05682013-01-10 15:30:38 -0800424# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700425name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
426sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800427
428# HACK: testutils.py imports controller.py, which needs the ofp module
429import oftest.testutils
430
Rich Lanee284b6b2012-10-03 09:19:58 -0700431# Allow tests to import each other
432sys.path.append(config["test_dir"])
433
Rich Lanec76b09a2013-01-02 16:53:22 -0800434test_specs = args
435if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100436 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800437 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800438if config["test_file"] != None:
439 with open(config["test_file"], 'r') as f:
440 for line in f:
441 line, _, _ = line.partition('#') # remove comments
442 line = line.strip()
443 if line:
444 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800445if test_specs == []:
446 test_specs = ["standard"]
447
Rich Laned8e45482013-01-02 17:36:21 -0800448test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800449
450# Check if test list is requested; display and exit if so
451if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700452 mod_count = 0
453 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800454 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100455
456 print("""
457Tests are shown grouped by module. If a test is in any groups beyond
458"standard" and its module's group then they are shown in parentheses.
459
Rich Lanee811e7b2013-01-03 13:36:54 -0800460Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100461special switch configuration. These are not part of the "standard" test group.
462
463Tests marked with '!' are disabled because they are experimental,
464special-purpose, or are too long to be run normally. These are not part of
465the "standard" test group or their module's test group.
466
467Tests marked (TP1) after name take --test-params including:
468
469 'vid=N;strip_vlan=bool;add_vlan=bool'
470
471Test List:
472""")
Rich Lane943be672012-10-04 19:20:16 -0700473 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700474 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800475 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100476 print(" Module %13s: %s" % (mod.__name__, desc))
477
Rich Lane943be672012-10-04 19:20:16 -0700478 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100479 desc = (test.__doc__ or "No description").strip().split('\n')[0]
480
Rich Lanee811e7b2013-01-03 13:36:54 -0800481 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800482 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800483 if groups:
484 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800485 if hasattr(test, "_versions"):
486 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100487
Rich Lanee811e7b2013-01-03 13:36:54 -0800488 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
489 test._disabled and "!" or " ",
490 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100491 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700492 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800493 print
Stephen Finucane01588652014-05-15 20:03:12 +0100494 print("'%d' modules shown with a total of '%d' tests\n" %
495 (mod_count, test_count))
496 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800497
Dan Talayco2c0dba32010-03-06 22:47:06 -0800498 sys.exit(0)
499
Rich Lane5a9a1922013-01-11 14:29:30 -0800500test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800501
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700502# Check if test list is requested; display and exit if so
503if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700504 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700505 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100506 print("%s.%s" % (modname, testname))
507
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700508 sys.exit(0)
509
Dan Talayco2c0dba32010-03-06 22:47:06 -0800510# Generate the test suite
511#@todo Decide if multiple suites are ever needed
512suite = unittest.TestSuite()
513
Rich Lane62b4fb12015-04-02 18:04:10 -0700514sorted_tests = []
Rich Lanec72ef462015-04-02 17:52:44 -0700515for (modname, (mod, tests)) in sorted(test_modules.items()):
516 for (testname, test) in sorted(tests.items()):
Rich Lane62b4fb12015-04-02 18:04:10 -0700517 sorted_tests.append(test)
518
519if config["random_order"]:
520 random.shuffle(sorted_tests)
521
522for test in sorted_tests:
523 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800524
Rich Lane51590f62012-10-09 15:06:29 -0700525# Allow platforms to import each other
526sys.path.append(config["platform_dir"])
527
Rich Lane8aebc5e2012-09-25 17:57:53 -0700528# Load the platform module
529platform_name = config["platform"]
530logging.info("Importing platform: " + platform_name)
531platform_mod = None
532try:
Rich Lane483e1542012-10-05 09:29:39 -0700533 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700534except:
535 logging.warn("Failed to import " + platform_name + " platform module")
536 raise
Dan Talayco48370102010-03-03 15:17:33 -0800537
538try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700539 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800540except:
541 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700542 raise
Dan Talayco48370102010-03-03 15:17:33 -0800543
544if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700545 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800546
547logging.debug("Configuration: " + str(config))
548logging.info("OF port map: " + str(config["port_map"]))
549
Rich Lanee55abf72012-07-26 20:11:42 -0700550oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700551oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700552oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700553
Rich Laneee57ad02012-07-13 15:40:36 -0700554if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100555 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700556 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700557
Rich Lane8592bec2012-09-03 09:06:59 -0700558if config["random_seed"] is not None:
559 logging.info("Random seed: %d" % config["random_seed"])
560 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700561else:
562 # Generate random seed and report to log file
563 seed = random.randrange(100000000)
564 logging.info("Autogen random seed: %d" % seed)
565 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700566
Rich Lane5bd6cf92012-10-04 17:57:24 -0700567# Remove python's signal handler which raises KeyboardError. Exiting from an
568# exception waits for all threads to terminate which might not happen.
569signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700570
571if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100572 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700573
Rich Lane2c7812c2012-12-27 17:52:23 -0800574 # Set up the dataplane
575 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700576 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800577 for of_port, ifname in config["port_map"].items():
578 oftest.dataplane_instance.port_add(ifname, of_port)
579
Dan Talaycoac25cf32010-07-20 14:08:28 -0700580 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100581 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100582 try:
583 import xmlrunner # fail-fast if module missing
584 except ImportError as ex:
585 oftest.dataplane_instance.kill()
586 profiler_teardown(profiler)
587 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100588 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
589 outsuffix="",
590 verbosity=2)
591 result = runner.run(suite)
592 else:
593 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700594 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700595 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100596 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
597 logging.info(message)
598 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800599
600 # Shutdown the dataplane
601 oftest.dataplane_instance.kill()
602 oftest.dataplane_instance = None
603
Stephen Finucane6219af72014-05-14 21:08:30 +0100604 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700605
Rich Lane50d42eb2012-07-16 11:57:03 -0700606 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700607 # exit(1) hangs sometimes
608 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700609 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700610 os._exit(1)