blob: c812c4eae0c188491d5dc64897ab598c8fd17835 [file] [log] [blame]
Dan Talayco48370102010-03-03 15:17:33 -08001#!/usr/bin/env python
2"""
3@package oft
4
5OpenFlow test framework top level script
6
Rich Laneea5060d2013-01-06 13:59:00 -08007This script is the entry point for running OpenFlow tests using the OFT
8framework. For usage information, see --help or the README.
Dan Talayco48370102010-03-03 15:17:33 -08009
Stephen Finucane7424a742014-05-15 19:46:06 +010010To add a new command line option, edit both the CONFIG_DEFAULT dictionary and
Rich Laneea5060d2013-01-06 13:59:00 -080011the config_setup function. The option's result will end up in the global
12oftest.config dictionary.
Dan Talayco48370102010-03-03 15:17:33 -080013"""
14
Stephen Finucane01588652014-05-15 20:03:12 +010015from __future__ import print_function
16
Dan Talayco48370102010-03-03 15:17:33 -080017import sys
Rich Lane95f078b2013-01-06 13:24:58 -080018import optparse
Dan Talayco48370102010-03-03 15:17:33 -080019import logging
20import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080021import time
Brandon Heller446c1432010-04-01 12:43:27 -070022import os
Rich Lane6b452bc2012-07-09 16:52:21 -070023import imp
Rich Lane8592bec2012-09-03 09:06:59 -070024import random
Rich Lane5bd6cf92012-10-04 17:57:24 -070025import signal
Rich Lane943be672012-10-04 19:20:16 -070026import fnmatch
Rich Lane2d6d4822013-01-08 10:49:16 -080027import copy
Dan Talayco48370102010-03-03 15:17:33 -080028
Stephen Finucane7424a742014-05-15 19:46:06 +010029ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
Rich Lanefadf3452012-10-03 16:23:37 -070030
Stephen Finucane7424a742014-05-15 19:46:06 +010031PY_SRC_DIR = os.path.join(ROOT_DIR, 'src', 'python')
32if os.path.exists(os.path.join(PY_SRC_DIR, 'oftest')):
Rich Lane39878042012-07-09 14:45:35 -070033 # Running from source tree
Stephen Finucane7424a742014-05-15 19:46:06 +010034 sys.path.insert(0, PY_SRC_DIR)
Rich Lane39878042012-07-09 14:45:35 -070035
Rich Lane477f4812012-10-04 22:49:00 -070036import oftest
37from oftest import config
Rich Lanee55abf72012-07-26 20:11:42 -070038import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080039import oftest.help_formatter
Rich Lane3f7098c2013-03-12 10:28:32 -070040import loxi
Dan Talaycoba3745c2010-07-21 21:51:08 -070041
Dan Talayco48370102010-03-03 15:17:33 -080042##@var DEBUG_LEVELS
43# Map from strings to debugging levels
44DEBUG_LEVELS = {
45 'debug' : logging.DEBUG,
46 'verbose' : logging.DEBUG,
47 'info' : logging.INFO,
48 'warning' : logging.WARNING,
49 'warn' : logging.WARNING,
50 'error' : logging.ERROR,
51 'critical' : logging.CRITICAL
52}
53
Stephen Finucane7424a742014-05-15 19:46:06 +010054##@var CONFIG_DEFAULT
Dan Talayco48370102010-03-03 15:17:33 -080055# The default configuration dictionary for OFT
Stephen Finucane7424a742014-05-15 19:46:06 +010056CONFIG_DEFAULT = {
Rich Lane95f078b2013-01-06 13:24:58 -080057 # Miscellaneous options
58 "list" : False,
59 "list_test_names" : False,
60 "allow_user" : False,
61
62 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080063 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080064 "test_file" : None,
Rich Lane74b13d12013-05-03 17:58:50 -070065 "test_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080066
67 # Switch connection options
68 "controller_host" : "0.0.0.0", # For passive bind
Rich Lane4d1f3eb2013-10-03 13:45:57 -070069 "controller_port" : 6653,
Rich Lane95f078b2013-01-06 13:24:58 -080070 "switch_ip" : None, # If not none, actively connect to switch
Rich Lane15f26322013-01-08 11:23:24 -080071 "platform" : "eth",
Rich Lane95f078b2013-01-06 13:24:58 -080072 "platform_args" : None,
Stephen Finucane7424a742014-05-15 19:46:06 +010073 "platform_dir" : os.path.join(ROOT_DIR, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080074 "interfaces" : [],
Rich Lane9fd05682013-01-10 15:30:38 -080075 "openflow_version" : "1.0",
Rich Lane95f078b2013-01-06 13:24:58 -080076
77 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080078 "log_file" : "oft.log",
Rich Lane69fd8e02013-08-23 16:23:42 -070079 "log_dir" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080080 "debug" : "verbose",
Rich Lane9631f002014-03-21 18:05:16 -070081 "profile" : False,
Stephen Finucane6219af72014-05-14 21:08:30 +010082 "profile_file" : "profile.out",
Stephen Finucanee016cf22014-04-16 22:04:11 +010083 "xunit" : False,
84 "xunit_dir" : "xunit",
Rich Lane95f078b2013-01-06 13:24:58 -080085
86 # Test behavior options
87 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070088 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070089 "fail_skipped" : False,
Rich Lane48f6aed2014-03-23 15:51:02 -070090 "default_timeout" : 2.0,
91 "default_negative_timeout" : 0.01,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000092 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070093 "random_seed" : None,
Stephen Finucane92f7cf62014-03-13 15:08:11 +000094 "disable_ipv6" : False,
Rich Lane62b4fb12015-04-02 18:04:10 -070095 "random_order" : False,
macauleyc1b4bd72015-07-16 15:29:45 +080096 "dump_packet" : True,
97
Rich Lane95f078b2013-01-06 13:24:58 -080098 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -080099 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -0800100}
101
Rich Lane95f078b2013-01-06 13:24:58 -0800102def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800103 """
104 Set up the configuration including parsing the arguments
105
Dan Talayco48370102010-03-03 15:17:33 -0800106 @return A pair (config, args) where config is an config
107 object and args is any additional arguments from the command line
108 """
109
Rich Lane4113a582013-01-03 10:13:02 -0800110 usage = "usage: %prog [options] (test|group)..."
111
Rich Lane95f078b2013-01-06 13:24:58 -0800112 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800113OFTest is a framework and set of tests for validating OpenFlow switches.
114
115The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700116connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800117the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
118dataplane.
119
120If no positional arguments are given then OFTest will run all tests that
121depend only on standard OpenFlow 1.0. Otherwise each positional argument
122is interpreted as either a test name or a test group name. The union of
123these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800124--list option. Tests and groups can be subtracted from the result by
125prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800126"""
127
Rich Lane2d6d4822013-01-08 10:49:16 -0800128 # Parse --interface
129 def check_interface(option, opt, value):
130 try:
131 ofport, interface = value.split('@', 1)
132 ofport = int(ofport)
133 except ValueError:
134 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
135 return (ofport, interface)
136
137 class Option(optparse.Option):
138 TYPES = optparse.Option.TYPES + ("interface",)
139 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
140 TYPE_CHECKER["interface"] = check_interface
141
Rich Lane95f078b2013-01-06 13:24:58 -0800142 parser = optparse.OptionParser(version="%prog 0.1",
143 usage=usage,
144 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800145 formatter=oftest.help_formatter.HelpFormatter(),
146 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800147
148 # Set up default values
Stephen Finucane7424a742014-05-15 19:46:06 +0100149 parser.set_defaults(**CONFIG_DEFAULT)
Dan Talayco48370102010-03-03 15:17:33 -0800150
Dan Talayco2c0dba32010-03-06 22:47:06 -0800151 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700152 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800153 parser.add_option("--list-test-names", action='store_true',
154 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700155 parser.add_option("--allow-user", action="store_true",
156 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800157
158 group = optparse.OptionGroup(parser, "Test selection options")
159 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
160 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
161 group.add_option("--test-dir", type="string", help="Directory containing tests")
162 parser.add_option_group(group)
163
164 group = optparse.OptionGroup(parser, "Switch connection options")
165 group.add_option("-H", "--host", dest="controller_host",
166 help="IP address to listen on (default %default)")
167 group.add_option("-p", "--port", dest="controller_port",
168 type="int", help="Port number to listen on (default %default)")
169 group.add_option("-S", "--switch-ip", dest="switch_ip",
170 help="If set, actively connect to this switch by IP")
171 group.add_option("-P", "--platform", help="Platform module name (default %default)")
172 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
173 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800174 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
175 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 -0700176 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800177 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800178 parser.add_option_group(group)
179
180 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700181 group.add_option("--log-file", help="Name of log file (default %default)")
182 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800183 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
184 group.add_option("--debug", choices=dbg_lvl_names,
185 help="Debug lvl: debug, info, warning, error, critical (default %default)")
186 group.add_option("-v", "--verbose", action="store_const", dest="debug",
187 const="verbose", help="Shortcut for --debug=verbose")
188 group.add_option("-q", "--quiet", action="store_const", dest="debug",
189 const="warning", help="Shortcut for --debug=warning")
Stephen Finucane6219af72014-05-14 21:08:30 +0100190 group.add_option("--profile", action="store_true", help="Enable Python profiling")
191 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100192 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
193 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800194 parser.add_option_group(group)
195
196 group = optparse.OptionGroup(parser, "Test behavior options")
197 group.add_option("--relax", action="store_true",
198 help="Relax packet match checks allowing other packets")
199 test_params_help = """Set test parameters: key=val;... (see --list)
200 """
201 group.add_option("-t", "--test-params", help=test_params_help)
202 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700203 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700204 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700205 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700206 group.add_option("--default-negative-timeout", type=float,
207 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800208 group.add_option("--minsize", type="int",
209 help="Minimum allowable packet size on the dataplane.")
210 group.add_option("--random-seed", type="int",
211 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000212 group.add_option("--disable-ipv6", action="store_true",
213 help="Disable IPv6 tests")
Rich Lane62b4fb12015-04-02 18:04:10 -0700214 group.add_option("--random-order", action="store_true",
215 help="Randomize order of tests")
macauleyc1b4bd72015-07-16 15:29:45 +0800216 group.add_option("--dump_packet", action="store_true",
217 help="Dump packet content on log when verify packet fail")
Rich Lane95f078b2013-01-06 13:24:58 -0800218 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000219
Dan Talayco48370102010-03-03 15:17:33 -0800220 # Might need this if other parsers want command line
221 # parser.allow_interspersed_args = False
222 (options, args) = parser.parse_args()
223
Rich Lane74b13d12013-05-03 17:58:50 -0700224 # If --test-dir wasn't given, pick one based on the OpenFlow version
225 if options.test_dir == None:
226 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100227 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700228 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100229 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700230
Rich Lane95f078b2013-01-06 13:24:58 -0800231 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100232 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800233 for key in config.keys():
234 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800235
236 return (config, args)
237
238def logging_setup(config):
239 """
240 Set up logging based on config
241 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700242
243 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
244
245 if config["log_dir"] != None:
246 if os.path.exists(config["log_dir"]):
247 import shutil
248 shutil.rmtree(config["log_dir"])
249 os.makedirs(config["log_dir"])
250 else:
251 if os.path.exists(config["log_file"]):
252 os.remove(config["log_file"])
253
254 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800255
Stephen Finucanee016cf22014-04-16 22:04:11 +0100256def xunit_setup(config):
257 """
258 Set up xUnit output based on config
259 """
260
261 if not config["xunit"]:
262 return
263
Stephen Finucane6219af72014-05-14 21:08:30 +0100264 if os.path.exists(config["xunit_dir"]):
265 import shutil
266 shutil.rmtree(config["xunit_dir"])
267 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100268
Rich Lane472aaea2013-08-27 09:27:38 -0700269def pcap_setup(config):
270 """
271 Set up dataplane packet capturing based on config
272 """
273
274 if config["log_dir"] == None:
275 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
276 oftest.dataplane_instance.start_pcap(filename)
277 else:
278 # start_pcap is called per-test in base_tests
279 pass
280
Stephen Finucane6219af72014-05-14 21:08:30 +0100281def profiler_setup(config):
282 """
283 Set up profiler based on config
284 """
285
286 if not config["profile"]:
287 return
288
289 import cProfile
290 profiler = cProfile.Profile()
291 profiler.enable()
292
293 return profiler
294
295def profiler_teardown(profiler):
296 """
297 Tear down profiler based on config
298 """
299
300 if not config["profile"]:
301 return
302
303 profiler.disable()
304 profiler.dump_stats(config["profile_file"])
305
Rich Lane472aaea2013-08-27 09:27:38 -0700306
Rich Lane943be672012-10-04 19:20:16 -0700307def load_test_modules(config):
308 """
309 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800310
Rich Lane943be672012-10-04 19:20:16 -0700311 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800312
Rich Lanecc45b8e2013-01-02 15:55:02 -0800313 Also updates the _groups member to include "standard" and
314 module test groups if appropriate.
315
Dan Talayco2c0dba32010-03-06 22:47:06 -0800316 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700317 @returns A dictionary from test module names to tuples of
318 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800319 """
320
Rich Lane943be672012-10-04 19:20:16 -0700321 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800322
Rich Lane943be672012-10-04 19:20:16 -0700323 for root, dirs, filenames in os.walk(config["test_dir"]):
324 # Iterate over each python file
325 for filename in fnmatch.filter(filenames, '[!.]*.py'):
326 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800327
Rich Lane943be672012-10-04 19:20:16 -0700328 try:
329 if sys.modules.has_key(modname):
330 mod = sys.modules[modname]
331 else:
332 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
333 except:
334 logging.warning("Could not import file " + filename)
335 raise
Rich Lane520e4152012-07-09 16:18:16 -0700336
Rich Lane943be672012-10-04 19:20:16 -0700337 # Find all testcases defined in the module
338 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700339 issubclass(v, unittest.TestCase) and
340 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700341 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800342 for (testname, test) in tests.items():
343 # Set default annotation values
344 if not hasattr(test, "_groups"):
345 test._groups = []
346 if not hasattr(test, "_nonstandard"):
347 test._nonstandard = False
348 if not hasattr(test, "_disabled"):
349 test._disabled = False
350
351 # Put test in its module's test group
352 if not test._disabled:
353 test._groups.append(modname)
354
355 # Put test in the standard test group
356 if not test._disabled and not test._nonstandard:
357 test._groups.append("standard")
358 test._groups.append("all") # backwards compatibility
359
Rich Lane943be672012-10-04 19:20:16 -0700360 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700361
Rich Lane943be672012-10-04 19:20:16 -0700362 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800363
Rich Lane5a9a1922013-01-11 14:29:30 -0800364def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700365 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800366 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800367 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800368 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700369 @param test_modules Same format as the output of load_test_modules.
370 @returns Same format as the output of load_test_modules.
371 """
372 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800373 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700374 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800375
376 if e.startswith('^'):
377 negated = True
378 e = e[1:]
379 else:
380 negated = False
381
Rich Lane15f64de2012-10-04 21:25:57 -0700382 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800383 for (testname, test) in tests.items():
384 if e in test._groups or e == "%s.%s" % (modname, testname):
385 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800386 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800387 if not hasattr(test, "_versions") or version in test._versions:
388 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800389 else:
390 if modname in result and testname in result[modname][1]:
391 del result[modname][1][testname]
392 if not result[modname][1]:
393 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800394 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800395
Rich Lane15f64de2012-10-04 21:25:57 -0700396 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800397 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800398
Rich Lane15f64de2012-10-04 21:25:57 -0700399 return result
400
Dan Talayco2c0dba32010-03-06 22:47:06 -0800401def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800402 logging.critical(msg)
403 sys.exit(exit_val)
404
Dan Talayco48370102010-03-03 15:17:33 -0800405#
406# Main script
407#
408
Rich Lane477f4812012-10-04 22:49:00 -0700409# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800410(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700411oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800412
Rich Laned7a85c42012-09-28 15:38:45 -0700413logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100414xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700415logging.info("++++++++ " + time.asctime() + " ++++++++")
416
Rich Lane9fd05682013-01-10 15:30:38 -0800417# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700418name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
419sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800420
421# HACK: testutils.py imports controller.py, which needs the ofp module
422import oftest.testutils
423
Rich Lanee284b6b2012-10-03 09:19:58 -0700424# Allow tests to import each other
425sys.path.append(config["test_dir"])
426
Rich Lanec76b09a2013-01-02 16:53:22 -0800427test_specs = args
428if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100429 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800430 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800431if config["test_file"] != None:
432 with open(config["test_file"], 'r') as f:
433 for line in f:
434 line, _, _ = line.partition('#') # remove comments
435 line = line.strip()
436 if line:
437 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800438if test_specs == []:
439 test_specs = ["standard"]
440
Rich Laned8e45482013-01-02 17:36:21 -0800441test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800442
443# Check if test list is requested; display and exit if so
444if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700445 mod_count = 0
446 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800447 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100448
449 print("""
450Tests are shown grouped by module. If a test is in any groups beyond
451"standard" and its module's group then they are shown in parentheses.
452
Rich Lanee811e7b2013-01-03 13:36:54 -0800453Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100454special switch configuration. These are not part of the "standard" test group.
455
456Tests marked with '!' are disabled because they are experimental,
457special-purpose, or are too long to be run normally. These are not part of
458the "standard" test group or their module's test group.
459
460Tests marked (TP1) after name take --test-params including:
461
462 'vid=N;strip_vlan=bool;add_vlan=bool'
463
464Test List:
465""")
Rich Lane943be672012-10-04 19:20:16 -0700466 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700467 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800468 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100469 print(" Module %13s: %s" % (mod.__name__, desc))
470
Rich Lane943be672012-10-04 19:20:16 -0700471 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100472 desc = (test.__doc__ or "No description").strip().split('\n')[0]
473
Rich Lanee811e7b2013-01-03 13:36:54 -0800474 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800475 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800476 if groups:
477 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800478 if hasattr(test, "_versions"):
479 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100480
Rich Lanee811e7b2013-01-03 13:36:54 -0800481 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
482 test._disabled and "!" or " ",
483 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100484 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700485 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800486 print
Stephen Finucane01588652014-05-15 20:03:12 +0100487 print("'%d' modules shown with a total of '%d' tests\n" %
488 (mod_count, test_count))
489 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800490
Dan Talayco2c0dba32010-03-06 22:47:06 -0800491 sys.exit(0)
492
Rich Lane5a9a1922013-01-11 14:29:30 -0800493test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800494
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700495# Check if test list is requested; display and exit if so
496if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700497 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700498 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100499 print("%s.%s" % (modname, testname))
500
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700501 sys.exit(0)
502
Dan Talayco2c0dba32010-03-06 22:47:06 -0800503# Generate the test suite
504#@todo Decide if multiple suites are ever needed
505suite = unittest.TestSuite()
506
Rich Lane62b4fb12015-04-02 18:04:10 -0700507sorted_tests = []
Rich Lanec72ef462015-04-02 17:52:44 -0700508for (modname, (mod, tests)) in sorted(test_modules.items()):
509 for (testname, test) in sorted(tests.items()):
Rich Lane62b4fb12015-04-02 18:04:10 -0700510 sorted_tests.append(test)
511
512if config["random_order"]:
513 random.shuffle(sorted_tests)
514
515for test in sorted_tests:
516 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800517
Rich Lane51590f62012-10-09 15:06:29 -0700518# Allow platforms to import each other
519sys.path.append(config["platform_dir"])
520
Rich Lane8aebc5e2012-09-25 17:57:53 -0700521# Load the platform module
522platform_name = config["platform"]
523logging.info("Importing platform: " + platform_name)
524platform_mod = None
525try:
Rich Lane483e1542012-10-05 09:29:39 -0700526 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700527except:
528 logging.warn("Failed to import " + platform_name + " platform module")
529 raise
Dan Talayco48370102010-03-03 15:17:33 -0800530
531try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700532 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800533except:
534 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700535 raise
Dan Talayco48370102010-03-03 15:17:33 -0800536
537if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700538 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800539
540logging.debug("Configuration: " + str(config))
541logging.info("OF port map: " + str(config["port_map"]))
542
Rich Lanee55abf72012-07-26 20:11:42 -0700543oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700544oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700545oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700546
Rich Laneee57ad02012-07-13 15:40:36 -0700547if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100548 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700549 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700550
Rich Lane8592bec2012-09-03 09:06:59 -0700551if config["random_seed"] is not None:
552 logging.info("Random seed: %d" % config["random_seed"])
553 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700554else:
555 # Generate random seed and report to log file
556 seed = random.randrange(100000000)
557 logging.info("Autogen random seed: %d" % seed)
558 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700559
Rich Lane5bd6cf92012-10-04 17:57:24 -0700560# Remove python's signal handler which raises KeyboardError. Exiting from an
561# exception waits for all threads to terminate which might not happen.
562signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700563
564if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100565 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700566
Rich Lane2c7812c2012-12-27 17:52:23 -0800567 # Set up the dataplane
568 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700569 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800570 for of_port, ifname in config["port_map"].items():
571 oftest.dataplane_instance.port_add(ifname, of_port)
572
Dan Talaycoac25cf32010-07-20 14:08:28 -0700573 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100574 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100575 try:
576 import xmlrunner # fail-fast if module missing
577 except ImportError as ex:
578 oftest.dataplane_instance.kill()
579 profiler_teardown(profiler)
580 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100581 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
582 outsuffix="",
583 verbosity=2)
584 result = runner.run(suite)
585 else:
586 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700587 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700588 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100589 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
590 logging.info(message)
591 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800592
593 # Shutdown the dataplane
594 oftest.dataplane_instance.kill()
595 oftest.dataplane_instance = None
596
Stephen Finucane6219af72014-05-14 21:08:30 +0100597 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700598
Rich Lane50d42eb2012-07-16 11:57:03 -0700599 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700600 # exit(1) hangs sometimes
601 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700602 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700603 os._exit(1)