blob: bb02815de7ba1fc84d9b5be84173617c40048d6d [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,
macauleyc2ad42c2015-07-17 15:59:15 +080097 "cicada_poject" : False,
macauleyc1b4bd72015-07-16 15:29:45 +080098
Rich Lane95f078b2013-01-06 13:24:58 -080099 # Other configuration
Rich Lane15f26322013-01-08 11:23:24 -0800100 "port_map" : {},
Dan Talayco48370102010-03-03 15:17:33 -0800101}
102
Rich Lane95f078b2013-01-06 13:24:58 -0800103def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800104 """
105 Set up the configuration including parsing the arguments
106
Dan Talayco48370102010-03-03 15:17:33 -0800107 @return A pair (config, args) where config is an config
108 object and args is any additional arguments from the command line
109 """
110
Rich Lane4113a582013-01-03 10:13:02 -0800111 usage = "usage: %prog [options] (test|group)..."
112
Rich Lane95f078b2013-01-06 13:24:58 -0800113 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800114OFTest is a framework and set of tests for validating OpenFlow switches.
115
116The default configuration assumes that an OpenFlow 1.0 switch is attempting to
Rich Lane4d1f3eb2013-10-03 13:45:57 -0700117connect to a controller on the machine running OFTest, port 6653. Additionally,
Rich Lane4113a582013-01-03 10:13:02 -0800118the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
119dataplane.
120
121If no positional arguments are given then OFTest will run all tests that
122depend only on standard OpenFlow 1.0. Otherwise each positional argument
123is interpreted as either a test name or a test group name. The union of
124these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800125--list option. Tests and groups can be subtracted from the result by
126prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800127"""
128
Rich Lane2d6d4822013-01-08 10:49:16 -0800129 # Parse --interface
130 def check_interface(option, opt, value):
131 try:
132 ofport, interface = value.split('@', 1)
133 ofport = int(ofport)
134 except ValueError:
135 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
136 return (ofport, interface)
137
138 class Option(optparse.Option):
139 TYPES = optparse.Option.TYPES + ("interface",)
140 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
141 TYPE_CHECKER["interface"] = check_interface
142
Rich Lane95f078b2013-01-06 13:24:58 -0800143 parser = optparse.OptionParser(version="%prog 0.1",
144 usage=usage,
145 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800146 formatter=oftest.help_formatter.HelpFormatter(),
147 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800148
149 # Set up default values
Stephen Finucane7424a742014-05-15 19:46:06 +0100150 parser.set_defaults(**CONFIG_DEFAULT)
Dan Talayco48370102010-03-03 15:17:33 -0800151
Dan Talayco2c0dba32010-03-06 22:47:06 -0800152 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700153 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800154 parser.add_option("--list-test-names", action='store_true',
155 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700156 parser.add_option("--allow-user", action="store_true",
157 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800158
159 group = optparse.OptionGroup(parser, "Test selection options")
160 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
161 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
162 group.add_option("--test-dir", type="string", help="Directory containing tests")
163 parser.add_option_group(group)
164
165 group = optparse.OptionGroup(parser, "Switch connection options")
166 group.add_option("-H", "--host", dest="controller_host",
167 help="IP address to listen on (default %default)")
168 group.add_option("-p", "--port", dest="controller_port",
169 type="int", help="Port number to listen on (default %default)")
170 group.add_option("-S", "--switch-ip", dest="switch_ip",
171 help="If set, actively connect to this switch by IP")
172 group.add_option("-P", "--platform", help="Platform module name (default %default)")
173 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
174 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800175 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
176 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 -0700177 group.add_option("--of-version", "-V", dest="openflow_version", choices=loxi.version_names.values(),
Rich Lane9fd05682013-01-10 15:30:38 -0800178 help="OpenFlow version to use")
Rich Lane95f078b2013-01-06 13:24:58 -0800179 parser.add_option_group(group)
180
181 group = optparse.OptionGroup(parser, "Logging options")
Rich Lane69fd8e02013-08-23 16:23:42 -0700182 group.add_option("--log-file", help="Name of log file (default %default)")
183 group.add_option("--log-dir", help="Name of log directory")
Rich Lane95f078b2013-01-06 13:24:58 -0800184 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
185 group.add_option("--debug", choices=dbg_lvl_names,
186 help="Debug lvl: debug, info, warning, error, critical (default %default)")
187 group.add_option("-v", "--verbose", action="store_const", dest="debug",
188 const="verbose", help="Shortcut for --debug=verbose")
189 group.add_option("-q", "--quiet", action="store_const", dest="debug",
190 const="warning", help="Shortcut for --debug=warning")
Stephen Finucane6219af72014-05-14 21:08:30 +0100191 group.add_option("--profile", action="store_true", help="Enable Python profiling")
192 group.add_option("--profile-file", help="Output file for Python profiler")
Stephen Finucanee016cf22014-04-16 22:04:11 +0100193 group.add_option("--xunit", action="store_true", help="Enable xUnit-formatted results")
194 group.add_option("--xunit-dir", help="Output directory for xUnit-formatted results")
Rich Lane95f078b2013-01-06 13:24:58 -0800195 parser.add_option_group(group)
196
197 group = optparse.OptionGroup(parser, "Test behavior options")
198 group.add_option("--relax", action="store_true",
199 help="Relax packet match checks allowing other packets")
200 test_params_help = """Set test parameters: key=val;... (see --list)
201 """
202 group.add_option("-t", "--test-params", help=test_params_help)
203 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700204 help="Return failure if any test was skipped")
Rich Lane48f6aed2014-03-23 15:51:02 -0700205 group.add_option("--default-timeout", type=float,
Rich Lanee55abf72012-07-26 20:11:42 -0700206 help="Timeout in seconds for most operations")
Rich Lane48f6aed2014-03-23 15:51:02 -0700207 group.add_option("--default-negative-timeout", type=float,
208 help="Timeout in seconds for negative checks")
Rich Lane95f078b2013-01-06 13:24:58 -0800209 group.add_option("--minsize", type="int",
210 help="Minimum allowable packet size on the dataplane.")
211 group.add_option("--random-seed", type="int",
212 help="Random number generator seed")
Stephen Finucane92f7cf62014-03-13 15:08:11 +0000213 group.add_option("--disable-ipv6", action="store_true",
214 help="Disable IPv6 tests")
Rich Lane62b4fb12015-04-02 18:04:10 -0700215 group.add_option("--random-order", action="store_true",
216 help="Randomize order of tests")
macauleyc1b4bd72015-07-16 15:29:45 +0800217 group.add_option("--dump_packet", action="store_true",
macauleyc2ad42c2015-07-17 15:59:15 +0800218 help="Dump packet content on log when verify packet fail")
219 group.add_option("--cicada_poject", action="store_true",
220 help="True verify Cicada behavior, False verify AOS behaviro")
Rich Lane95f078b2013-01-06 13:24:58 -0800221 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000222
Dan Talayco48370102010-03-03 15:17:33 -0800223 # Might need this if other parsers want command line
224 # parser.allow_interspersed_args = False
225 (options, args) = parser.parse_args()
226
Rich Lane74b13d12013-05-03 17:58:50 -0700227 # If --test-dir wasn't given, pick one based on the OpenFlow version
228 if options.test_dir == None:
229 if options.openflow_version == "1.0":
Stephen Finucane7424a742014-05-15 19:46:06 +0100230 options.test_dir = os.path.join(ROOT_DIR, "tests")
Rich Lane74b13d12013-05-03 17:58:50 -0700231 else:
Stephen Finucane7424a742014-05-15 19:46:06 +0100232 options.test_dir = os.path.join(ROOT_DIR, "tests-" + options.openflow_version)
Rich Lane74b13d12013-05-03 17:58:50 -0700233
Rich Lane95f078b2013-01-06 13:24:58 -0800234 # Convert options from a Namespace to a plain dictionary
Stephen Finucane7424a742014-05-15 19:46:06 +0100235 config = CONFIG_DEFAULT.copy()
Rich Lane95f078b2013-01-06 13:24:58 -0800236 for key in config.keys():
237 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800238
239 return (config, args)
240
241def logging_setup(config):
242 """
243 Set up logging based on config
244 """
Rich Lane69fd8e02013-08-23 16:23:42 -0700245
246 logging.getLogger().setLevel(DEBUG_LEVELS[config["debug"]])
247
248 if config["log_dir"] != None:
249 if os.path.exists(config["log_dir"]):
250 import shutil
251 shutil.rmtree(config["log_dir"])
252 os.makedirs(config["log_dir"])
253 else:
254 if os.path.exists(config["log_file"]):
255 os.remove(config["log_file"])
256
257 oftest.open_logfile('main')
Dan Talayco48370102010-03-03 15:17:33 -0800258
Stephen Finucanee016cf22014-04-16 22:04:11 +0100259def xunit_setup(config):
260 """
261 Set up xUnit output based on config
262 """
263
264 if not config["xunit"]:
265 return
266
Stephen Finucane6219af72014-05-14 21:08:30 +0100267 if os.path.exists(config["xunit_dir"]):
268 import shutil
269 shutil.rmtree(config["xunit_dir"])
270 os.makedirs(config["xunit_dir"])
Stephen Finucanee016cf22014-04-16 22:04:11 +0100271
Rich Lane472aaea2013-08-27 09:27:38 -0700272def pcap_setup(config):
273 """
274 Set up dataplane packet capturing based on config
275 """
276
277 if config["log_dir"] == None:
278 filename = os.path.splitext(config["log_file"])[0] + '.pcap'
279 oftest.dataplane_instance.start_pcap(filename)
280 else:
281 # start_pcap is called per-test in base_tests
282 pass
283
Stephen Finucane6219af72014-05-14 21:08:30 +0100284def profiler_setup(config):
285 """
286 Set up profiler based on config
287 """
288
289 if not config["profile"]:
290 return
291
292 import cProfile
293 profiler = cProfile.Profile()
294 profiler.enable()
295
296 return profiler
297
298def profiler_teardown(profiler):
299 """
300 Tear down profiler based on config
301 """
302
303 if not config["profile"]:
304 return
305
306 profiler.disable()
307 profiler.dump_stats(config["profile_file"])
308
Rich Lane472aaea2013-08-27 09:27:38 -0700309
Rich Lane943be672012-10-04 19:20:16 -0700310def load_test_modules(config):
311 """
312 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800313
Rich Lane943be672012-10-04 19:20:16 -0700314 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800315
Rich Lanecc45b8e2013-01-02 15:55:02 -0800316 Also updates the _groups member to include "standard" and
317 module test groups if appropriate.
318
Dan Talayco2c0dba32010-03-06 22:47:06 -0800319 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700320 @returns A dictionary from test module names to tuples of
321 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800322 """
323
Rich Lane943be672012-10-04 19:20:16 -0700324 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800325
Rich Lane943be672012-10-04 19:20:16 -0700326 for root, dirs, filenames in os.walk(config["test_dir"]):
327 # Iterate over each python file
328 for filename in fnmatch.filter(filenames, '[!.]*.py'):
329 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800330
Rich Lane943be672012-10-04 19:20:16 -0700331 try:
332 if sys.modules.has_key(modname):
333 mod = sys.modules[modname]
334 else:
335 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
336 except:
337 logging.warning("Could not import file " + filename)
338 raise
Rich Lane520e4152012-07-09 16:18:16 -0700339
Rich Lane943be672012-10-04 19:20:16 -0700340 # Find all testcases defined in the module
341 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
Rich Lane9cef2742013-07-16 13:27:00 -0700342 issubclass(v, unittest.TestCase) and
343 hasattr(v, "runTest"))
Rich Lane943be672012-10-04 19:20:16 -0700344 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800345 for (testname, test) in tests.items():
346 # Set default annotation values
347 if not hasattr(test, "_groups"):
348 test._groups = []
349 if not hasattr(test, "_nonstandard"):
350 test._nonstandard = False
351 if not hasattr(test, "_disabled"):
352 test._disabled = False
353
354 # Put test in its module's test group
355 if not test._disabled:
356 test._groups.append(modname)
357
358 # Put test in the standard test group
359 if not test._disabled and not test._nonstandard:
360 test._groups.append("standard")
361 test._groups.append("all") # backwards compatibility
362
Rich Lane943be672012-10-04 19:20:16 -0700363 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700364
Rich Lane943be672012-10-04 19:20:16 -0700365 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800366
Rich Lane5a9a1922013-01-11 14:29:30 -0800367def prune_tests(test_specs, test_modules, version):
Rich Lane15f64de2012-10-04 21:25:57 -0700368 """
Rich Lane5a9a1922013-01-11 14:29:30 -0800369 Return tests matching the given test-specs and OpenFlow version
Rich Lanec76b09a2013-01-02 16:53:22 -0800370 @param test_specs A list of group names or test names.
Rich Lane5a9a1922013-01-11 14:29:30 -0800371 @param version An OpenFlow version (e.g. "1.0")
Rich Lane15f64de2012-10-04 21:25:57 -0700372 @param test_modules Same format as the output of load_test_modules.
373 @returns Same format as the output of load_test_modules.
374 """
375 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800376 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700377 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800378
379 if e.startswith('^'):
380 negated = True
381 e = e[1:]
382 else:
383 negated = False
384
Rich Lane15f64de2012-10-04 21:25:57 -0700385 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800386 for (testname, test) in tests.items():
387 if e in test._groups or e == "%s.%s" % (modname, testname):
388 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800389 if not negated:
Rich Lane5a9a1922013-01-11 14:29:30 -0800390 if not hasattr(test, "_versions") or version in test._versions:
391 result[modname][1][testname] = test
Rich Lane948f0302013-01-07 10:59:08 -0800392 else:
393 if modname in result and testname in result[modname][1]:
394 del result[modname][1][testname]
395 if not result[modname][1]:
396 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800397 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800398
Rich Lane15f64de2012-10-04 21:25:57 -0700399 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800400 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800401
Rich Lane15f64de2012-10-04 21:25:57 -0700402 return result
403
Dan Talayco2c0dba32010-03-06 22:47:06 -0800404def die(msg, exit_val=1):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800405 logging.critical(msg)
406 sys.exit(exit_val)
407
Dan Talayco48370102010-03-03 15:17:33 -0800408#
409# Main script
410#
411
Rich Lane477f4812012-10-04 22:49:00 -0700412# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800413(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700414oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800415
Rich Laned7a85c42012-09-28 15:38:45 -0700416logging_setup(config)
Stephen Finucanee016cf22014-04-16 22:04:11 +0100417xunit_setup(config)
Rich Laned7a85c42012-09-28 15:38:45 -0700418logging.info("++++++++ " + time.asctime() + " ++++++++")
419
Rich Lane9fd05682013-01-10 15:30:38 -0800420# Pick an OpenFlow protocol module based on the configured version
Rich Lane50cfa502013-04-25 13:45:08 -0700421name_to_version = dict((v,k) for k, v in loxi.version_names.iteritems())
422sys.modules["ofp"] = loxi.protocol(name_to_version[config["openflow_version"]])
Rich Lane9fd05682013-01-10 15:30:38 -0800423
424# HACK: testutils.py imports controller.py, which needs the ofp module
425import oftest.testutils
426
Rich Lanee284b6b2012-10-03 09:19:58 -0700427# Allow tests to import each other
428sys.path.append(config["test_dir"])
429
Rich Lanec76b09a2013-01-02 16:53:22 -0800430test_specs = args
431if config["test_spec"] != "":
Stephen Finucane01588652014-05-15 20:03:12 +0100432 logging.warning("The '--test-spec' option is deprecated.")
Rich Lanec76b09a2013-01-02 16:53:22 -0800433 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800434if config["test_file"] != None:
435 with open(config["test_file"], 'r') as f:
436 for line in f:
437 line, _, _ = line.partition('#') # remove comments
438 line = line.strip()
439 if line:
440 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800441if test_specs == []:
442 test_specs = ["standard"]
443
Rich Laned8e45482013-01-02 17:36:21 -0800444test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800445
446# Check if test list is requested; display and exit if so
447if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700448 mod_count = 0
449 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800450 all_groups = set()
Stephen Finucane01588652014-05-15 20:03:12 +0100451
452 print("""
453Tests are shown grouped by module. If a test is in any groups beyond
454"standard" and its module's group then they are shown in parentheses.
455
Rich Lanee811e7b2013-01-03 13:36:54 -0800456Tests marked with '*' are non-standard and may require vendor extensions or
Stephen Finucane01588652014-05-15 20:03:12 +0100457special switch configuration. These are not part of the "standard" test group.
458
459Tests marked with '!' are disabled because they are experimental,
460special-purpose, or are too long to be run normally. These are not part of
461the "standard" test group or their module's test group.
462
463Tests marked (TP1) after name take --test-params including:
464
465 'vid=N;strip_vlan=bool;add_vlan=bool'
466
467Test List:
468""")
Rich Lane943be672012-10-04 19:20:16 -0700469 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700470 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800471 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Stephen Finucane01588652014-05-15 20:03:12 +0100472 print(" Module %13s: %s" % (mod.__name__, desc))
473
Rich Lane943be672012-10-04 19:20:16 -0700474 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100475 desc = (test.__doc__ or "No description").strip().split('\n')[0]
476
Rich Lanee811e7b2013-01-03 13:36:54 -0800477 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800478 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800479 if groups:
480 desc = "(%s) %s" % (",".join(groups), desc)
Rich Lane5a9a1922013-01-11 14:29:30 -0800481 if hasattr(test, "_versions"):
482 desc = "(%s) %s" % (",".join(sorted(test._versions)), desc)
Stephen Finucane01588652014-05-15 20:03:12 +0100483
Rich Lanee811e7b2013-01-03 13:36:54 -0800484 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
485 test._disabled and "!" or " ",
486 testname)
Stephen Finucane01588652014-05-15 20:03:12 +0100487 print(" %22s : %s" % (start_str, desc))
Dan Talayco7f8dba82012-04-12 12:58:52 -0700488 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800489 print
Stephen Finucane01588652014-05-15 20:03:12 +0100490 print("'%d' modules shown with a total of '%d' tests\n" %
491 (mod_count, test_count))
492 print("Test groups: %s" % (', '.join(sorted(all_groups))))
Rich Lane37f42112013-01-03 13:41:49 -0800493
Dan Talayco2c0dba32010-03-06 22:47:06 -0800494 sys.exit(0)
495
Rich Lane5a9a1922013-01-11 14:29:30 -0800496test_modules = prune_tests(test_specs, test_modules, config["openflow_version"])
Rich Laned8e45482013-01-02 17:36:21 -0800497
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700498# Check if test list is requested; display and exit if so
499if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700500 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700501 for (testname, test) in tests.items():
Stephen Finucane01588652014-05-15 20:03:12 +0100502 print("%s.%s" % (modname, testname))
503
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700504 sys.exit(0)
505
Dan Talayco2c0dba32010-03-06 22:47:06 -0800506# Generate the test suite
507#@todo Decide if multiple suites are ever needed
508suite = unittest.TestSuite()
509
Rich Lane62b4fb12015-04-02 18:04:10 -0700510sorted_tests = []
Rich Lanec72ef462015-04-02 17:52:44 -0700511for (modname, (mod, tests)) in sorted(test_modules.items()):
512 for (testname, test) in sorted(tests.items()):
Rich Lane62b4fb12015-04-02 18:04:10 -0700513 sorted_tests.append(test)
514
515if config["random_order"]:
516 random.shuffle(sorted_tests)
517
518for test in sorted_tests:
519 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800520
Rich Lane51590f62012-10-09 15:06:29 -0700521# Allow platforms to import each other
522sys.path.append(config["platform_dir"])
523
Rich Lane8aebc5e2012-09-25 17:57:53 -0700524# Load the platform module
525platform_name = config["platform"]
526logging.info("Importing platform: " + platform_name)
527platform_mod = None
528try:
Rich Lane483e1542012-10-05 09:29:39 -0700529 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700530except:
531 logging.warn("Failed to import " + platform_name + " platform module")
532 raise
Dan Talayco48370102010-03-03 15:17:33 -0800533
534try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700535 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800536except:
537 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700538 raise
Dan Talayco48370102010-03-03 15:17:33 -0800539
540if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700541 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800542
543logging.debug("Configuration: " + str(config))
544logging.info("OF port map: " + str(config["port_map"]))
545
Rich Lanee55abf72012-07-26 20:11:42 -0700546oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane48f6aed2014-03-23 15:51:02 -0700547oftest.ofutils.default_negative_timeout = config["default_negative_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700548oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700549
Rich Laneee57ad02012-07-13 15:40:36 -0700550if os.getuid() != 0 and not config["allow_user"]:
Stephen Finucane01588652014-05-15 20:03:12 +0100551 die("Super-user privileges required. Please re-run with sudo or as root.")
Rich Laned1d9c282012-10-04 22:07:10 -0700552 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700553
Rich Lane8592bec2012-09-03 09:06:59 -0700554if config["random_seed"] is not None:
555 logging.info("Random seed: %d" % config["random_seed"])
556 random.seed(config["random_seed"])
Dan Talaycoec6084d2013-03-14 10:38:22 -0700557else:
558 # Generate random seed and report to log file
559 seed = random.randrange(100000000)
560 logging.info("Autogen random seed: %d" % seed)
561 random.seed(seed)
Rich Lane8592bec2012-09-03 09:06:59 -0700562
Rich Lane5bd6cf92012-10-04 17:57:24 -0700563# Remove python's signal handler which raises KeyboardError. Exiting from an
564# exception waits for all threads to terminate which might not happen.
565signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700566
567if __name__ == "__main__":
Stephen Finucane6219af72014-05-14 21:08:30 +0100568 profiler = profiler_setup(config)
Rich Lane9631f002014-03-21 18:05:16 -0700569
Rich Lane2c7812c2012-12-27 17:52:23 -0800570 # Set up the dataplane
571 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
Rich Lane472aaea2013-08-27 09:27:38 -0700572 pcap_setup(config)
Rich Lane2c7812c2012-12-27 17:52:23 -0800573 for of_port, ifname in config["port_map"].items():
574 oftest.dataplane_instance.port_add(ifname, of_port)
575
Dan Talaycoac25cf32010-07-20 14:08:28 -0700576 logging.info("*** TEST RUN START: " + time.asctime())
Stephen Finucanee016cf22014-04-16 22:04:11 +0100577 if config["xunit"]:
Stephen Finucane12782552014-05-20 21:55:04 +0100578 try:
579 import xmlrunner # fail-fast if module missing
580 except ImportError as ex:
581 oftest.dataplane_instance.kill()
582 profiler_teardown(profiler)
583 raise ex
Stephen Finucanee016cf22014-04-16 22:04:11 +0100584 runner = xmlrunner.XMLTestRunner(output=config["xunit_dir"],
585 outsuffix="",
586 verbosity=2)
587 result = runner.run(suite)
588 else:
589 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Lane69fd8e02013-08-23 16:23:42 -0700590 oftest.open_logfile('main')
Rich Laneda3b5ad2012-10-03 09:05:32 -0700591 if oftest.testutils.skipped_test_count > 0:
Stephen Finucane01588652014-05-15 20:03:12 +0100592 message = "Skipped %d test(s)" % oftest.testutils.skipped_test_count
593 logging.info(message)
594 logging.info("*** TEST RUN END : %s", time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800595
596 # Shutdown the dataplane
597 oftest.dataplane_instance.kill()
598 oftest.dataplane_instance = None
599
Stephen Finucane6219af72014-05-14 21:08:30 +0100600 profiler_teardown(profiler)
Rich Lane9631f002014-03-21 18:05:16 -0700601
Rich Lane50d42eb2012-07-16 11:57:03 -0700602 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700603 # exit(1) hangs sometimes
604 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700605 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700606 os._exit(1)