blob: 993e58d988699117d14dc7142118b1a9637590a1 [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
Rich Laneea5060d2013-01-06 13:59:00 -080010To add a new command line option, edit both the config_default dictionary and
11the 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
15import sys
Rich Lane95f078b2013-01-06 13:24:58 -080016import optparse
Dan Talayco2c0dba32010-03-06 22:47:06 -080017from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -080018import logging
19import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -080020import time
Brandon Heller446c1432010-04-01 12:43:27 -070021import os
Rich Lane6b452bc2012-07-09 16:52:21 -070022import imp
Rich Lane8592bec2012-09-03 09:06:59 -070023import random
Rich Lane5bd6cf92012-10-04 17:57:24 -070024import signal
Rich Lane943be672012-10-04 19:20:16 -070025import fnmatch
Rich Lane2d6d4822013-01-08 10:49:16 -080026import copy
Dan Talayco48370102010-03-03 15:17:33 -080027
Rich Lanefadf3452012-10-03 16:23:37 -070028root_dir = os.path.dirname(os.path.realpath(__file__))
29
30pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -070031if os.path.exists(os.path.join(pydir, 'oftest')):
32 # Running from source tree
33 sys.path.insert(0, pydir)
34
Rich Lane477f4812012-10-04 22:49:00 -070035import oftest
36from oftest import config
Rich Laneda3b5ad2012-10-03 09:05:32 -070037import oftest.testutils
Rich Lanee55abf72012-07-26 20:11:42 -070038import oftest.ofutils
Rich Lane95f078b2013-01-06 13:24:58 -080039import oftest.help_formatter
Dan Talaycoba3745c2010-07-21 21:51:08 -070040
Dan Talayco02eca0b2010-04-15 16:09:43 -070041try:
42 import scapy.all as scapy
43except:
44 try:
45 import scapy as scapy
46 except:
47 sys.exit("Need to install scapy for packet parsing")
48
Dan Talayco48370102010-03-03 15:17:33 -080049##@var DEBUG_LEVELS
50# Map from strings to debugging levels
51DEBUG_LEVELS = {
52 'debug' : logging.DEBUG,
53 'verbose' : logging.DEBUG,
54 'info' : logging.INFO,
55 'warning' : logging.WARNING,
56 'warn' : logging.WARNING,
57 'error' : logging.ERROR,
58 'critical' : logging.CRITICAL
59}
60
Dan Talayco48370102010-03-03 15:17:33 -080061##@var config_default
62# The default configuration dictionary for OFT
63config_default = {
Rich Lane95f078b2013-01-06 13:24:58 -080064 # Miscellaneous options
65 "list" : False,
66 "list_test_names" : False,
67 "allow_user" : False,
68
69 # Test selection options
Rich Lanec76b09a2013-01-02 16:53:22 -080070 "test_spec" : "",
Rich Lane1a08d232013-01-04 16:03:50 -080071 "test_file" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080072 "test_dir" : os.path.join(root_dir, "tests"),
73
74 # Switch connection options
75 "controller_host" : "0.0.0.0", # For passive bind
76 "controller_port" : 6633,
77 "switch_ip" : None, # If not none, actively connect to switch
78 "platform" : "local",
79 "platform_args" : None,
80 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane2d6d4822013-01-08 10:49:16 -080081 "interfaces" : [],
Rich Lane95f078b2013-01-06 13:24:58 -080082
83 # Logging options
Dan Talayco48370102010-03-03 15:17:33 -080084 "log_file" : "oft.log",
Dan Talayco69ca4d62012-11-15 11:50:22 -080085 "log_append" : False,
Rich Lane95f078b2013-01-06 13:24:58 -080086 "debug" : "verbose",
87
88 # Test behavior options
89 "relax" : False,
Dan Talaycod7c80d12012-04-03 15:20:57 -070090 "test_params" : "None",
Rich Lane9a84a4f2012-07-17 12:27:42 -070091 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -070092 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000093 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -070094 "random_seed" : None,
Rich Lane95f078b2013-01-06 13:24:58 -080095
96 # Other configuration
97 "port_map" : {}, # TODO replace with --interface
Dan Talayco48370102010-03-03 15:17:33 -080098}
99
Rich Lane95f078b2013-01-06 13:24:58 -0800100def config_setup():
Dan Talayco48370102010-03-03 15:17:33 -0800101 """
102 Set up the configuration including parsing the arguments
103
Dan Talayco48370102010-03-03 15:17:33 -0800104 @return A pair (config, args) where config is an config
105 object and args is any additional arguments from the command line
106 """
107
Rich Lane4113a582013-01-03 10:13:02 -0800108 usage = "usage: %prog [options] (test|group)..."
109
Rich Lane95f078b2013-01-06 13:24:58 -0800110 description = """\
Rich Lane4113a582013-01-03 10:13:02 -0800111OFTest is a framework and set of tests for validating OpenFlow switches.
112
113The default configuration assumes that an OpenFlow 1.0 switch is attempting to
114connect to a controller on the machine running OFTest, port 6633. Additionally,
115the interfaces veth1, veth3, veth5, and veth7 should be connected to the switch's
116dataplane.
117
118If no positional arguments are given then OFTest will run all tests that
119depend only on standard OpenFlow 1.0. Otherwise each positional argument
120is interpreted as either a test name or a test group name. The union of
121these will be executed. To see what groups each test belongs to use the
Rich Lane948f0302013-01-07 10:59:08 -0800122--list option. Tests and groups can be subtracted from the result by
123prefixing them with the '^' character.
Rich Lane4113a582013-01-03 10:13:02 -0800124"""
125
Rich Lane2d6d4822013-01-08 10:49:16 -0800126 # Parse --interface
127 def check_interface(option, opt, value):
128 try:
129 ofport, interface = value.split('@', 1)
130 ofport = int(ofport)
131 except ValueError:
132 raise optparse.OptionValueError("incorrect interface syntax (got %s, expected 'ofport@interface')" % repr(value))
133 return (ofport, interface)
134
135 class Option(optparse.Option):
136 TYPES = optparse.Option.TYPES + ("interface",)
137 TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
138 TYPE_CHECKER["interface"] = check_interface
139
Rich Lane95f078b2013-01-06 13:24:58 -0800140 parser = optparse.OptionParser(version="%prog 0.1",
141 usage=usage,
142 description=description,
Rich Lane2d6d4822013-01-08 10:49:16 -0800143 formatter=oftest.help_formatter.HelpFormatter(),
144 option_class=Option)
Dan Talayco48370102010-03-03 15:17:33 -0800145
146 # Set up default values
Rich Lane95f078b2013-01-06 13:24:58 -0800147 parser.set_defaults(**config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800148
Dan Talayco2c0dba32010-03-06 22:47:06 -0800149 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700150 help="List all tests and exit")
Rich Lane95f078b2013-01-06 13:24:58 -0800151 parser.add_option("--list-test-names", action='store_true',
152 help="List test names matching the test spec and exit")
Rich Laneee57ad02012-07-13 15:40:36 -0700153 parser.add_option("--allow-user", action="store_true",
154 help="Proceed even if oftest is not run as root")
Rich Lane95f078b2013-01-06 13:24:58 -0800155
156 group = optparse.OptionGroup(parser, "Test selection options")
157 group.add_option("-T", "--test-spec", "--test-list", help="Tests to run, separated by commas")
158 group.add_option("-f", "--test-file", help="File of tests to run, one per line")
159 group.add_option("--test-dir", type="string", help="Directory containing tests")
160 parser.add_option_group(group)
161
162 group = optparse.OptionGroup(parser, "Switch connection options")
163 group.add_option("-H", "--host", dest="controller_host",
164 help="IP address to listen on (default %default)")
165 group.add_option("-p", "--port", dest="controller_port",
166 type="int", help="Port number to listen on (default %default)")
167 group.add_option("-S", "--switch-ip", dest="switch_ip",
168 help="If set, actively connect to this switch by IP")
169 group.add_option("-P", "--platform", help="Platform module name (default %default)")
170 group.add_option("-a", "--platform-args", help="Custom arguments for the platform")
171 group.add_option("--platform-dir", type="string", help="Directory containing platform modules")
Rich Lane2d6d4822013-01-08 10:49:16 -0800172 group.add_option("--interface", "-i", type="interface", dest="interfaces", metavar="INTERFACE", action="append",
173 help="Specify a OpenFlow port number and the dataplane interface to use. May be given multiple times. Example: 1@eth1")
Rich Lane95f078b2013-01-06 13:24:58 -0800174 parser.add_option_group(group)
175
176 group = optparse.OptionGroup(parser, "Logging options")
177 group.add_option("--log-file",
178 help="Name of log file, empty string to log to console (default %default)")
179 group.add_option("--log-append", action="store_true",
180 help="Do not delete log file if specified")
181 dbg_lvl_names = sorted(DEBUG_LEVELS.keys(), key=lambda x: DEBUG_LEVELS[x])
182 group.add_option("--debug", choices=dbg_lvl_names,
183 help="Debug lvl: debug, info, warning, error, critical (default %default)")
184 group.add_option("-v", "--verbose", action="store_const", dest="debug",
185 const="verbose", help="Shortcut for --debug=verbose")
186 group.add_option("-q", "--quiet", action="store_const", dest="debug",
187 const="warning", help="Shortcut for --debug=warning")
188 parser.add_option_group(group)
189
190 group = optparse.OptionGroup(parser, "Test behavior options")
191 group.add_option("--relax", action="store_true",
192 help="Relax packet match checks allowing other packets")
193 test_params_help = """Set test parameters: key=val;... (see --list)
194 """
195 group.add_option("-t", "--test-params", help=test_params_help)
196 group.add_option("--fail-skipped", action="store_true",
Rich Lane9a84a4f2012-07-17 12:27:42 -0700197 help="Return failure if any test was skipped")
Rich Lane95f078b2013-01-06 13:24:58 -0800198 group.add_option("--default-timeout", type="int",
Rich Lanee55abf72012-07-26 20:11:42 -0700199 help="Timeout in seconds for most operations")
Rich Lane95f078b2013-01-06 13:24:58 -0800200 group.add_option("--minsize", type="int",
201 help="Minimum allowable packet size on the dataplane.")
202 group.add_option("--random-seed", type="int",
203 help="Random number generator seed")
204 parser.add_option_group(group)
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000205
Dan Talayco48370102010-03-03 15:17:33 -0800206 # Might need this if other parsers want command line
207 # parser.allow_interspersed_args = False
208 (options, args) = parser.parse_args()
209
Rich Lane95f078b2013-01-06 13:24:58 -0800210 # Convert options from a Namespace to a plain dictionary
211 config = config_default.copy()
212 for key in config.keys():
213 config[key] = getattr(options, key)
Dan Talayco48370102010-03-03 15:17:33 -0800214
215 return (config, args)
216
217def logging_setup(config):
218 """
219 Set up logging based on config
220 """
Rich Lane71d887d2012-12-22 17:05:13 -0800221 _format = "%(asctime)s.%(msecs)d %(name)-10s: %(levelname)-8s: %(message)s"
Dan Talayco48370102010-03-03 15:17:33 -0800222 _datefmt = "%H:%M:%S"
Dan Talayco69ca4d62012-11-15 11:50:22 -0800223 _mode = config["log_append"] and "a" or "w"
Dan Talayco88fc8802010-03-07 11:37:52 -0800224 logging.basicConfig(filename=config["log_file"],
Dan Talayco69ca4d62012-11-15 11:50:22 -0800225 filemode=_mode,
Rich Lane95f078b2013-01-06 13:24:58 -0800226 level=DEBUG_LEVELS[config["debug"]],
Dan Talayco88fc8802010-03-07 11:37:52 -0800227 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800228
Rich Lane943be672012-10-04 19:20:16 -0700229def load_test_modules(config):
230 """
231 Load tests from the test_dir directory.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800232
Rich Lane943be672012-10-04 19:20:16 -0700233 Test cases are subclasses of unittest.TestCase
Dan Talayco2c0dba32010-03-06 22:47:06 -0800234
Rich Lanecc45b8e2013-01-02 15:55:02 -0800235 Also updates the _groups member to include "standard" and
236 module test groups if appropriate.
237
Dan Talayco2c0dba32010-03-06 22:47:06 -0800238 @param config The oft configuration dictionary
Rich Lane943be672012-10-04 19:20:16 -0700239 @returns A dictionary from test module names to tuples of
240 (module, dictionary from test names to test classes).
Dan Talayco2c0dba32010-03-06 22:47:06 -0800241 """
242
Rich Lane943be672012-10-04 19:20:16 -0700243 result = {}
Dan Talayco2c0dba32010-03-06 22:47:06 -0800244
Rich Lane943be672012-10-04 19:20:16 -0700245 for root, dirs, filenames in os.walk(config["test_dir"]):
246 # Iterate over each python file
247 for filename in fnmatch.filter(filenames, '[!.]*.py'):
248 modname = os.path.splitext(os.path.basename(filename))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800249
Rich Lane943be672012-10-04 19:20:16 -0700250 try:
251 if sys.modules.has_key(modname):
252 mod = sys.modules[modname]
253 else:
254 mod = imp.load_module(modname, *imp.find_module(modname, [root]))
255 except:
256 logging.warning("Could not import file " + filename)
257 raise
Rich Lane520e4152012-07-09 16:18:16 -0700258
Rich Lane943be672012-10-04 19:20:16 -0700259 # Find all testcases defined in the module
260 tests = dict((k, v) for (k, v) in mod.__dict__.items() if type(v) == type and
261 issubclass(v, unittest.TestCase))
262 if tests:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800263 for (testname, test) in tests.items():
264 # Set default annotation values
265 if not hasattr(test, "_groups"):
266 test._groups = []
267 if not hasattr(test, "_nonstandard"):
268 test._nonstandard = False
269 if not hasattr(test, "_disabled"):
270 test._disabled = False
271
272 # Put test in its module's test group
273 if not test._disabled:
274 test._groups.append(modname)
275
276 # Put test in the standard test group
277 if not test._disabled and not test._nonstandard:
278 test._groups.append("standard")
279 test._groups.append("all") # backwards compatibility
280
Rich Lane943be672012-10-04 19:20:16 -0700281 result[modname] = (mod, tests)
Rich Lane520e4152012-07-09 16:18:16 -0700282
Rich Lane943be672012-10-04 19:20:16 -0700283 return result
Dan Talayco2c0dba32010-03-06 22:47:06 -0800284
Rich Lanec76b09a2013-01-02 16:53:22 -0800285def prune_tests(test_specs, test_modules):
Rich Lane15f64de2012-10-04 21:25:57 -0700286 """
Rich Lanec76b09a2013-01-02 16:53:22 -0800287 Return tests matching the given test-specs
288 @param test_specs A list of group names or test names.
Rich Lane15f64de2012-10-04 21:25:57 -0700289 @param test_modules Same format as the output of load_test_modules.
290 @returns Same format as the output of load_test_modules.
291 """
292 result = {}
Rich Lanec76b09a2013-01-02 16:53:22 -0800293 for e in test_specs:
Rich Lane15f64de2012-10-04 21:25:57 -0700294 matched = False
Rich Lane948f0302013-01-07 10:59:08 -0800295
296 if e.startswith('^'):
297 negated = True
298 e = e[1:]
299 else:
300 negated = False
301
Rich Lane15f64de2012-10-04 21:25:57 -0700302 for (modname, (mod, tests)) in test_modules.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800303 for (testname, test) in tests.items():
304 if e in test._groups or e == "%s.%s" % (modname, testname):
305 result.setdefault(modname, (mod, {}))
Rich Lane948f0302013-01-07 10:59:08 -0800306 if not negated:
307 result[modname][1][testname] = test
308 else:
309 if modname in result and testname in result[modname][1]:
310 del result[modname][1][testname]
311 if not result[modname][1]:
312 del result[modname]
Rich Lanecc45b8e2013-01-02 15:55:02 -0800313 matched = True
Rich Lane948f0302013-01-07 10:59:08 -0800314
Rich Lane15f64de2012-10-04 21:25:57 -0700315 if not matched:
Rich Lanecc45b8e2013-01-02 15:55:02 -0800316 die("test-spec element %s did not match any tests" % e)
Rich Lane948f0302013-01-07 10:59:08 -0800317
Rich Lane15f64de2012-10-04 21:25:57 -0700318 return result
319
Dan Talayco2c0dba32010-03-06 22:47:06 -0800320def die(msg, exit_val=1):
321 print msg
322 logging.critical(msg)
323 sys.exit(exit_val)
324
Dan Talayco79f36082010-03-11 16:53:53 -0800325def _space_to(n, str):
326 """
327 Generate a string of spaces to achieve width n given string str
328 If length of str >= n, return one space
329 """
330 spaces = n - len(str)
331 if spaces > 0:
332 return " " * spaces
333 return " "
334
Dan Talayco48370102010-03-03 15:17:33 -0800335#
336# Main script
337#
338
Rich Lane477f4812012-10-04 22:49:00 -0700339# Setup global configuration
Rich Lane95f078b2013-01-06 13:24:58 -0800340(new_config, args) = config_setup()
Rich Lane477f4812012-10-04 22:49:00 -0700341oftest.config.update(new_config)
Dan Talayco48370102010-03-03 15:17:33 -0800342
Rich Laned7a85c42012-09-28 15:38:45 -0700343logging_setup(config)
344logging.info("++++++++ " + time.asctime() + " ++++++++")
345
Rich Lanee284b6b2012-10-03 09:19:58 -0700346# Allow tests to import each other
347sys.path.append(config["test_dir"])
348
Rich Lanec76b09a2013-01-02 16:53:22 -0800349test_specs = args
350if config["test_spec"] != "":
351 print >> sys.stderr, "WARNING: The --test-spec option is deprecated"
352 test_specs += config["test_spec"].split(',')
Rich Lane1a08d232013-01-04 16:03:50 -0800353if config["test_file"] != None:
354 with open(config["test_file"], 'r') as f:
355 for line in f:
356 line, _, _ = line.partition('#') # remove comments
357 line = line.strip()
358 if line:
359 test_specs.append(line)
Rich Lanec76b09a2013-01-02 16:53:22 -0800360if test_specs == []:
361 test_specs = ["standard"]
362
Rich Laned8e45482013-01-02 17:36:21 -0800363test_modules = load_test_modules(config)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800364
365# Check if test list is requested; display and exit if so
366if config["list"]:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700367 mod_count = 0
368 test_count = 0
Rich Lane37f42112013-01-03 13:41:49 -0800369 all_groups = set()
Rich Lanee811e7b2013-01-03 13:36:54 -0800370 print """\
371Tests are shown grouped by module. If a test is in any groups beyond "standard"
372and its module's group then they are shown in parentheses."""
373 print
374 print """\
375Tests marked with '*' are non-standard and may require vendor extensions or
376special switch configuration. These are not part of the "standard" test group."""
377 print
378 print """\
379Tests marked with '!' are disabled because they are experimental, special-purpose,
380or are too long to be run normally. These are not part of the "standard" test
381group or their module's test group."""
382 print
383 print "Tests marked (TP1) after name take --test-params including:"
384 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Rich Lanee811e7b2013-01-03 13:36:54 -0800385 print
386 print "Test List:"
Rich Lane943be672012-10-04 19:20:16 -0700387 for (modname, (mod, tests)) in test_modules.items():
Dan Talayco7f8dba82012-04-12 12:58:52 -0700388 mod_count += 1
Rich Lane80efd652013-01-02 14:05:20 -0800389 desc = (mod.__doc__ or "No description").strip().split('\n')[0]
Dan Talayco79f36082010-03-11 16:53:53 -0800390 start_str = " Module " + mod.__name__ + ": "
391 print start_str + _space_to(22, start_str) + desc
Rich Lane943be672012-10-04 19:20:16 -0700392 for (testname, test) in tests.items():
Dan Talayco551befa2010-07-15 17:05:32 -0700393 try:
Rich Lane0f5b9c72013-01-02 14:05:20 -0800394 desc = (test.__doc__ or "").strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700395 desc = desc.split('\n')[0]
396 except:
397 desc = "No description"
Rich Lanee811e7b2013-01-03 13:36:54 -0800398 groups = set(test._groups) - set(["all", "standard", modname])
Rich Lane37f42112013-01-03 13:41:49 -0800399 all_groups.update(test._groups)
Rich Lanee811e7b2013-01-03 13:36:54 -0800400 if groups:
401 desc = "(%s) %s" % (",".join(groups), desc)
402 start_str = " %s%s %s:" % (test._nonstandard and "*" or " ",
403 test._disabled and "!" or " ",
404 testname)
Dan Talayco551befa2010-07-15 17:05:32 -0700405 if len(start_str) > 22:
406 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800407 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700408 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800409 print
Rich Lane15f64de2012-10-04 21:25:57 -0700410 print "%d modules shown with a total of %d tests" % \
411 (mod_count, test_count)
412 print
Rich Lane37f42112013-01-03 13:41:49 -0800413 print "Test groups: %s" % (', '.join(sorted(all_groups)))
414
Dan Talayco2c0dba32010-03-06 22:47:06 -0800415 sys.exit(0)
416
Rich Laned8e45482013-01-02 17:36:21 -0800417test_modules = prune_tests(test_specs, test_modules)
418
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700419# Check if test list is requested; display and exit if so
420if config["list_test_names"]:
Rich Lane943be672012-10-04 19:20:16 -0700421 for (modname, (mod, tests)) in test_modules.items():
Rich Lane943be672012-10-04 19:20:16 -0700422 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800423 print "%s.%s" % (modname, testname)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700424 sys.exit(0)
425
Dan Talayco2c0dba32010-03-06 22:47:06 -0800426# Generate the test suite
427#@todo Decide if multiple suites are ever needed
428suite = unittest.TestSuite()
429
Rich Lane15f64de2012-10-04 21:25:57 -0700430for (modname, (mod, tests)) in test_modules.items():
431 for (testname, test) in tests.items():
Rich Lanecc45b8e2013-01-02 15:55:02 -0800432 logging.info("Adding test " + modname + "." + testname)
433 suite.addTest(test())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800434
Rich Lane51590f62012-10-09 15:06:29 -0700435# Allow platforms to import each other
436sys.path.append(config["platform_dir"])
437
Rich Lane8aebc5e2012-09-25 17:57:53 -0700438# Load the platform module
439platform_name = config["platform"]
440logging.info("Importing platform: " + platform_name)
441platform_mod = None
442try:
Rich Lane483e1542012-10-05 09:29:39 -0700443 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"]]))
Rich Lane8aebc5e2012-09-25 17:57:53 -0700444except:
445 logging.warn("Failed to import " + platform_name + " platform module")
446 raise
Dan Talayco48370102010-03-03 15:17:33 -0800447
448try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700449 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800450except:
451 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700452 raise
Dan Talayco48370102010-03-03 15:17:33 -0800453
454if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700455 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800456
457logging.debug("Configuration: " + str(config))
458logging.info("OF port map: " + str(config["port_map"]))
459
Rich Lanee55abf72012-07-26 20:11:42 -0700460oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700461oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700462
Rich Laneee57ad02012-07-13 15:40:36 -0700463if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700464 print "ERROR: Super-user privileges required. Please re-run with " \
465 "sudo or as root."
Rich Laned1d9c282012-10-04 22:07:10 -0700466 sys.exit(1)
Brandon Heller446c1432010-04-01 12:43:27 -0700467
Rich Lane8592bec2012-09-03 09:06:59 -0700468if config["random_seed"] is not None:
469 logging.info("Random seed: %d" % config["random_seed"])
470 random.seed(config["random_seed"])
471
Rich Lane5bd6cf92012-10-04 17:57:24 -0700472# Remove python's signal handler which raises KeyboardError. Exiting from an
473# exception waits for all threads to terminate which might not happen.
474signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700475
476if __name__ == "__main__":
Rich Lane2c7812c2012-12-27 17:52:23 -0800477 # Set up the dataplane
478 oftest.dataplane_instance = oftest.dataplane.DataPlane(config)
479 for of_port, ifname in config["port_map"].items():
480 oftest.dataplane_instance.port_add(ifname, of_port)
481
Dan Talaycoac25cf32010-07-20 14:08:28 -0700482 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane68edb3d2013-01-04 17:00:26 -0800483 result = unittest.TextTestRunner(verbosity=2).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700484 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700485 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700486 if oftest.testutils.skipped_test_count == 1: ts = " test"
487 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
488 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700489 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane2c7812c2012-12-27 17:52:23 -0800490
491 # Shutdown the dataplane
492 oftest.dataplane_instance.kill()
493 oftest.dataplane_instance = None
494
Rich Lane50d42eb2012-07-16 11:57:03 -0700495 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700496 # exit(1) hangs sometimes
497 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700498 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700499 os._exit(1)