blob: 5d8b20eae92bcc3d5d25013ce3294d801f503d60 [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
7This script is the entry point for running OpenFlow tests
8using the OFT framework.
9
10The global configuration is passed around in a dictionary
Brandon Heller88f709d2010-04-01 12:29:56 -070011generally called config. The keys have the following
Dan Talayco48370102010-03-03 15:17:33 -080012significance.
13
Dan Talayco2c0dba32010-03-06 22:47:06 -080014<pre>
Dan Talayco48370102010-03-03 15:17:33 -080015 platform : String identifying the target platform
16 controller_host : Host on which test controller is running (for sockets)
17 controller_port : Port on which test controller listens for switch cxn
Dan Talayco2c0dba32010-03-06 22:47:06 -080018 test_dir : (TBD) Directory to search for test files (default .)
Dan Talayco48370102010-03-03 15:17:33 -080019 test_spec : (TBD) Specification of test(s) to run
20 log_file : Filename for test logging
Dan Talayco2c0dba32010-03-06 22:47:06 -080021 list : Boolean: List all tests and exit
Dan Talayco48370102010-03-03 15:17:33 -080022 debug : String giving debug level (info, warning, error...)
Dan Talayco2c0dba32010-03-06 22:47:06 -080023</pre>
Dan Talayco48370102010-03-03 15:17:33 -080024
25See config_defaults below for the default values.
26
Dan Talayco2c0dba32010-03-06 22:47:06 -080027The following are stored in the config dictionary, but are not currently
28configurable through the command line.
29
30<pre>
31 dbg_level : logging module value of debug level
32 port_map : Map of dataplane OpenFlow port to OS interface names
33 test_mod_map : Dictionary indexed by module names and whose value
34 is the module reference
35 all_tests : Dictionary indexed by module reference and whose
36 value is a list of functions in that module
37</pre>
38
Dan Talaycoc24aaae2010-07-08 14:05:24 -070039Each test may be assigned a priority by setting test_prio["TestName"] in
40the respective module. For now, the only use of this is to avoid
41automatic inclusion of tests into the default list. This is done by
42setting the test_prio value less than 0. Eventually we may add ordering
43of test execution by test priority.
44
Dan Talayco2c0dba32010-03-06 22:47:06 -080045To add a test to the system, either: edit an existing test case file (like
46basic.py) to add a test class which inherits from unittest.TestCase (directly
47or indirectly); or add a new file which includes a function definition
48test_set_init(config). Preferably the file is in the same directory as existing
49tests, though you can specify the directory on the command line. The file
50should not be called "all" as that's reserved for the test-spec.
51
52If you add a new file, the test_set_init function should record the port
53map object from the configuration along with whatever other configuration
54information it may need.
55
56TBD: To add configuration to the system, first add an entry to config_default
Dan Talayco48370102010-03-03 15:17:33 -080057below. If you want this to be a command line parameter, edit config_setup
58to add the option and default value to the parser. Then edit config_get
59to make sure the option value gets copied into the configuration
60structure (which then gets passed to everyone else).
61
62By convention, oft attempts to import the contents of a file by the
63name of $platform.py into the local namespace.
64
65IMPORTANT: That file should define a function platform_config_update which
66takes a configuration dictionary as an argument and updates it for the
67current run. In particular, it should set up config["port_map"] with
68the proper map from OF port numbers to OF interface names.
69
Rich Lane8aebc5e2012-09-25 17:57:53 -070070You can add your own platform, say gp104, by adding a file gp104.py to the
71platforms directory that defines the function platform_config_update and then
72use the parameter --platform=gp104 on the command line. You can also use the
73--platform-dir option to change which directory is searched.
Dan Talayco48370102010-03-03 15:17:33 -080074
Dan Talayco48370102010-03-03 15:17:33 -080075The current model for test sets is basic.py. The current convention is
76that the test set should implement a function test_set_init which takes
77an oft configuration dictionary and returns a unittest.TestSuite object.
78Future test sets should do the same thing.
79
Dan Talayco52f64442010-03-03 15:32:41 -080080Default setup:
81
82The default setup runs locally using veth pairs. To exercise this,
83checkout and build an openflow userspace datapath. Then start it on
84the local host:
Dan Talayco2c0dba32010-03-06 22:47:06 -080085<pre>
Dan Talayco52f64442010-03-03 15:32:41 -080086 sudo ~/openflow/regress/bin/veth_setup.pl
87 sudo ofdatapath -i veth0,veth2,veth4,veth6 punix:/tmp/ofd &
88 sudo ofprotocol unix:/tmp/ofd tcp:127.0.0.1 --fail=closed --max-backoff=1 &
89
90Next, run oft:
91 sudo ./oft --debug=info
Dan Talayco2c0dba32010-03-06 22:47:06 -080092</pre>
Dan Talayco52f64442010-03-03 15:32:41 -080093
94Examine oft.log if things don't work.
Dan Talayco2c0dba32010-03-06 22:47:06 -080095
Dan Talayco1a88c122010-03-07 22:00:20 -080096@todo Support per-component debug levels (esp controller vs dataplane)
Rich Lane19df87e2012-09-28 12:45:56 -070097@todo Consider moving oft up a level
Dan Talayco2c0dba32010-03-06 22:47:06 -080098
Dan Talayco1a88c122010-03-07 22:00:20 -080099Current test case setup:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700100 Files in the tests direcoty that contain a function test_set_init are
101considered test files.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800102 The function test_set_init examines the test_spec config variable
103and generates a suite of tests.
104 Support a command line option --test_mod so that all tests in that
105module will be run.
106 Support all to specify all tests from the module.
107
Dan Talayco48370102010-03-03 15:17:33 -0800108"""
109
110import sys
111from optparse import OptionParser
Dan Talayco2c0dba32010-03-06 22:47:06 -0800112from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -0800113import logging
114import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800115import time
Brandon Heller446c1432010-04-01 12:43:27 -0700116import os
Rich Lane6b452bc2012-07-09 16:52:21 -0700117import imp
Rich Lane8592bec2012-09-03 09:06:59 -0700118import random
Rich Lane5bd6cf92012-10-04 17:57:24 -0700119import signal
Dan Talayco48370102010-03-03 15:17:33 -0800120
Rich Lanefadf3452012-10-03 16:23:37 -0700121root_dir = os.path.dirname(os.path.realpath(__file__))
122
123pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -0700124if os.path.exists(os.path.join(pydir, 'oftest')):
125 # Running from source tree
126 sys.path.insert(0, pydir)
127
Rich Laneb0470142012-10-04 15:50:35 -0700128try:
129 import oftest.message
130except:
131 sys.exit("Missing OpenFlow message classes: please run \"make -C tools/munger\"")
132
Rich Laneda3b5ad2012-10-03 09:05:32 -0700133import oftest.testutils
Rich Lanee55abf72012-07-26 20:11:42 -0700134import oftest.ofutils
Dan Talaycoba3745c2010-07-21 21:51:08 -0700135
Dan Talayco02eca0b2010-04-15 16:09:43 -0700136try:
137 import scapy.all as scapy
138except:
139 try:
140 import scapy as scapy
141 except:
142 sys.exit("Need to install scapy for packet parsing")
143
Dan Talaycod7c80d12012-04-03 15:20:57 -0700144##@var Profile module
145profile_mod = None
146
Dan Talayco48370102010-03-03 15:17:33 -0800147##@var DEBUG_LEVELS
148# Map from strings to debugging levels
149DEBUG_LEVELS = {
150 'debug' : logging.DEBUG,
151 'verbose' : logging.DEBUG,
152 'info' : logging.INFO,
153 'warning' : logging.WARNING,
154 'warn' : logging.WARNING,
155 'error' : logging.ERROR,
156 'critical' : logging.CRITICAL
157}
158
159_debug_default = "warning"
160_debug_level_default = DEBUG_LEVELS[_debug_default]
161
162##@var config_default
163# The default configuration dictionary for OFT
164config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700165 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800166 "platform" : "local",
Rich Lane941d1dd2012-07-30 14:27:53 -0700167 "platform_args" : None,
Rich Lane1aeccc42012-07-29 17:58:10 -0700168 "controller_host" : "0.0.0.0",
Dan Talayco48370102010-03-03 15:17:33 -0800169 "controller_port" : 6633,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700170 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800171 "test_spec" : "all",
Dan Talayco48370102010-03-03 15:17:33 -0800172 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800173 "list" : False,
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700174 "list_test_names" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800175 "debug" : _debug_default,
176 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700177 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700178 "test_params" : "None",
Rich Lane1673b8f2012-09-25 18:35:18 -0700179 "profile" : "default",
Rich Laneee57ad02012-07-13 15:40:36 -0700180 "allow_user" : False,
Rich Lane9a84a4f2012-07-17 12:27:42 -0700181 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700182 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000183 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -0700184 "random_seed" : None,
Rich Lanee284b6b2012-10-03 09:19:58 -0700185 "test_dir" : os.path.join(root_dir, "tests"),
Rich Lane8aebc5e2012-09-25 17:57:53 -0700186 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane1673b8f2012-09-25 18:35:18 -0700187 "profile_dir" : os.path.join(root_dir, "profiles"),
Dan Talayco48370102010-03-03 15:17:33 -0800188}
189
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700190# Default test priority
191TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700192TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700193
Dan Talayco1a88c122010-03-07 22:00:20 -0800194#@todo Set up a dict of config params so easier to manage:
195# <param> <cmdline flags> <default value> <help> <optional parser>
196
Dan Talayco48370102010-03-03 15:17:33 -0800197# Map options to config structure
198def config_get(opts):
199 "Convert options class to OFT configuration dictionary"
200 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800201 for key in cfg.keys():
202 cfg[key] = eval("opts." + key)
203
204 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800205 if opts.debug not in DEBUG_LEVELS.keys():
206 print "Warning: Bad value specified for debug level; using default"
207 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700208 if opts.verbose:
209 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800210 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800211
Dan Talayco48370102010-03-03 15:17:33 -0800212 return cfg
213
214def config_setup(cfg_dflt):
215 """
216 Set up the configuration including parsing the arguments
217
218 @param cfg_dflt The default configuration dictionary
219 @return A pair (config, args) where config is an config
220 object and args is any additional arguments from the command line
221 """
222
223 parser = OptionParser(version="%prog 0.1")
224
Dan Talayco2c0dba32010-03-06 22:47:06 -0800225 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800226 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800227 for key in cfg_dflt.keys():
228 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
Dan Talayco48370102010-03-03 15:17:33 -0800229
Dan Talayco2c0dba32010-03-06 22:47:06 -0800230 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800231 plat_help = """Set the platform type. Valid values include:
232 local: User space virtual ethernet pair setup
233 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800234 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800235 """
Rich Lane941d1dd2012-07-30 14:27:53 -0700236 parser.add_option("-a", "--platform-args", help="Custom arguments per platform.")
Dan Talayco48370102010-03-03 15:17:33 -0800237 parser.add_option("-P", "--platform", help=plat_help)
238 parser.add_option("-H", "--host", dest="controller_host",
239 help="The IP/name of the test controller host")
240 parser.add_option("-p", "--port", dest="controller_port",
241 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800242 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700243 default) or a comma separated list of:
244 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800245 testcase Run tests in all modules with the name testcase
246 module.testcase Run the specific test case
247 """
Rich Lanef261a102012-07-25 13:41:38 -0700248 parser.add_option("-T", "--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800249 parser.add_option("--log-file",
250 help="Name of log file, empty string to log to console")
251 parser.add_option("--debug",
252 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700253 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800254 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700255 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800256 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700257 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800258 help="Base interface index number (optional)")
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700259 parser.add_option("--list-test-names", action='store_true',
260 help="List only test names.", default=False)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800261 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700262 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700263 parser.add_option("--verbose", action="store_true",
264 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700265 parser.add_option("--relax", action="store_true",
266 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700267 parser.add_option("--param", type="int",
268 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700269 parser.add_option("--profile",
270 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700271 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700272 help="""Set test parameters: key=val;...
273 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
274 See --list""")
Rich Laneee57ad02012-07-13 15:40:36 -0700275 parser.add_option("--allow-user", action="store_true",
276 help="Proceed even if oftest is not run as root")
Rich Lane9a84a4f2012-07-17 12:27:42 -0700277 parser.add_option("--fail-skipped", action="store_true",
278 help="Return failure if any test was skipped")
Rich Lanee55abf72012-07-26 20:11:42 -0700279 parser.add_option("--default-timeout", type="int",
280 help="Timeout in seconds for most operations")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000281 parser.add_option("--minsize", type="int",
282 help="Minimum allowable packet size on the dataplane.",
283 default=0)
Rich Lane8592bec2012-09-03 09:06:59 -0700284 parser.add_option("--random-seed", type="int",
285 help="Random number generator seed",
286 default=None)
Rich Lanebc3d2962012-09-25 09:34:17 -0700287 parser.add_option("--test-dir", type="string",
288 help="Directory containing tests")
Rich Lane8aebc5e2012-09-25 17:57:53 -0700289 parser.add_option("--platform-dir", type="string",
290 help="Directory containing platform modules")
Rich Lane1673b8f2012-09-25 18:35:18 -0700291 parser.add_option("--profile-dir", type="string",
292 help="Directory containing profile modules")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000293
Dan Talayco48370102010-03-03 15:17:33 -0800294 # Might need this if other parsers want command line
295 # parser.allow_interspersed_args = False
296 (options, args) = parser.parse_args()
297
298 config = config_get(options)
299
300 return (config, args)
301
Rich Lane1673b8f2012-09-25 18:35:18 -0700302def load_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700303 """
304 Import a profile from the profiles library
305 """
306
Dan Talaycod7c80d12012-04-03 15:20:57 -0700307 global profile_mod
Rich Lane1673b8f2012-09-25 18:35:18 -0700308 logging.info("Importing profile: %s" % config["profile"])
309 try:
310 profile_mod = imp.load_module(config["profile"], *imp.find_module(config["profile"], [config["profile_dir"]]))
311 if not "skip_test_list" in dir(profile_mod):
312 die("Profile did not define skip_test_list")
313 except:
314 logging.info("Could not import profile: %s.py" % config["profile"])
315 print "Failed to import profile: %s" % config["profile"]
316 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700317
Dan Talayco48370102010-03-03 15:17:33 -0800318def logging_setup(config):
319 """
320 Set up logging based on config
321 """
322 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
323 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800324 logging.basicConfig(filename=config["log_file"],
325 level=config["dbg_level"],
326 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800327
Dan Talayco2c0dba32010-03-06 22:47:06 -0800328def test_list_generate(config):
329 """Generate the list of all known tests indexed by module name
330
331 Conventions: Test files must implement the function test_set_init
332
Dan Talayco1a88c122010-03-07 22:00:20 -0800333 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800334
335 @param config The oft configuration dictionary
336 @returns An array of triples (mod-name, module, [tests]) where
337 mod-name is the string (filename) of the module, module is the
338 value returned from __import__'ing the module and [tests] is an
339 array of strings giving the test cases from the module.
340 """
341
342 # Find and import test files
343 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
344 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
345 stdin=p1.stdout, stdout=PIPE)
346
347 all_tests = {}
348 mod_name_map = {}
349 # There's an extra empty entry at the end of the list
350 filelist = p2.communicate()[0].split("\n")[:-1]
351 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700352 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800353 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700354 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800355
356 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700357 if sys.modules.has_key(modname):
358 mod = sys.modules[modname]
359 else:
360 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800361 except:
362 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700363 raise
Rich Lane520e4152012-07-09 16:18:16 -0700364
365 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
366 issubclass(getattr(mod, k), unittest.TestCase)]
367 if tests:
368 mod_name_map[modname] = mod
369 all_tests[mod] = tests
370
Dan Talayco2c0dba32010-03-06 22:47:06 -0800371 config["all_tests"] = all_tests
372 config["mod_name_map"] = mod_name_map
373
374def die(msg, exit_val=1):
375 print msg
376 logging.critical(msg)
377 sys.exit(exit_val)
378
379def add_test(suite, mod, name):
380 logging.info("Adding test " + mod.__name__ + "." + name)
381 suite.addTest(eval("mod." + name)())
382
Dan Talayco79f36082010-03-11 16:53:53 -0800383def _space_to(n, str):
384 """
385 Generate a string of spaces to achieve width n given string str
386 If length of str >= n, return one space
387 """
388 spaces = n - len(str)
389 if spaces > 0:
390 return " " * spaces
391 return " "
392
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700393def test_prio_get(mod, test):
394 """
395 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700396
397 If test is in "skip list" from profile, return the skip value
398
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700399 If set in the test_prio variable for the module, return
400 that value. Otherwise return 100 (default)
401 """
Rich Lane1673b8f2012-09-25 18:35:18 -0700402 if test in profile_mod.skip_test_list:
403 logging.info("Skipping test %s due to profile" % test)
404 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700405 if 'test_prio' in dir(mod):
406 if test in mod.test_prio.keys():
407 return mod.test_prio[test]
408 return TEST_PRIO_DEFAULT
409
Dan Talayco48370102010-03-03 15:17:33 -0800410#
411# Main script
412#
413
414# Get configuration, set up logging, import platform from file
415(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800416
Rich Laned7a85c42012-09-28 15:38:45 -0700417logging_setup(config)
418logging.info("++++++++ " + time.asctime() + " ++++++++")
419
Rich Lanee284b6b2012-10-03 09:19:58 -0700420# Allow tests to import each other
421sys.path.append(config["test_dir"])
422
Dan Talayco2c0dba32010-03-06 22:47:06 -0800423test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700424oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800425
Rich Lanec27fa002012-09-28 12:49:12 -0700426load_profile(config)
427
Dan Talayco2c0dba32010-03-06 22:47:06 -0800428# Check if test list is requested; display and exit if so
429if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800430 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700431 mod_count = 0
432 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800433 print "\nTest List:"
434 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800435 if config["test_spec"] != "all" and \
436 config["test_spec"] != mod.__name__:
437 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700438 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800439 did_print = True
440 desc = mod.__doc__.strip()
441 desc = desc.split('\n')[0]
442 start_str = " Module " + mod.__name__ + ": "
443 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800444 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700445 try:
446 desc = eval('mod.' + test + '.__doc__.strip()')
447 desc = desc.split('\n')[0]
448 except:
449 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700450 if test_prio_get(mod, test) < 0:
451 start_str = " * " + test + ":"
452 else:
453 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700454 if len(start_str) > 22:
455 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800456 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700457 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800458 print
459 if not did_print:
460 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700461 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700462 print "%d modules shown with a total of %d tests" % \
463 (mod_count, test_count)
464 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700465 print "Tests preceded by * are not run by default"
466 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700467 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700468 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800469 sys.exit(0)
470
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700471# Check if test list is requested; display and exit if so
472if config["list_test_names"]:
473 for mod in config["all_tests"].keys():
474 if config["test_spec"] != "all" and \
475 config["test_spec"] != mod.__name__:
476 continue
477 desc = mod.__doc__.strip()
478 desc = desc.split('\n')[0]
479 for test in config["all_tests"][mod]:
Jeffrey Townsendfb9ce272012-10-02 21:10:29 -0700480 if test_prio_get(mod, test) >= 0:
481 print "%s.%s" % (mod.__name__, test)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700482 sys.exit(0)
483
Dan Talayco2c0dba32010-03-06 22:47:06 -0800484# Generate the test suite
485#@todo Decide if multiple suites are ever needed
486suite = unittest.TestSuite()
487
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700488#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800489if config["test_spec"] == "all":
490 for mod in config["all_tests"].keys():
491 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700492 # For now, a way to avoid tests
493 if test_prio_get(mod, test) >= 0:
494 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800495
496else:
497 for ts_entry in config["test_spec"].split(","):
498 parts = ts_entry.split(".")
499
500 if len(parts) == 1: # Either a module or test name
501 if ts_entry in config["mod_name_map"].keys():
502 mod = config["mod_name_map"][ts_entry]
503 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700504 if test_prio_get(mod, test) >= 0:
505 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800506 else: # Search for matching tests
507 test_found = False
508 for mod in config["all_tests"].keys():
509 if ts_entry in config["all_tests"][mod]:
510 add_test(suite, mod, ts_entry)
511 test_found = True
512 if not test_found:
513 die("Could not find module or test: " + ts_entry)
514
515 elif len(parts) == 2: # module.test
516 if parts[0] not in config["mod_name_map"]:
517 die("Unknown module in test spec: " + ts_entry)
518 mod = config["mod_name_map"][parts[0]]
519 if parts[1] in config["all_tests"][mod]:
520 add_test(suite, mod, parts[1])
521 else:
522 die("No known test matches: " + ts_entry)
523
524 else:
525 die("Bad test spec: " + ts_entry)
526
Rich Lane8aebc5e2012-09-25 17:57:53 -0700527# Load the platform module
528platform_name = config["platform"]
529logging.info("Importing platform: " + platform_name)
530platform_mod = None
531try:
532 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"], config["test_dir"]]))
533except:
534 logging.warn("Failed to import " + platform_name + " platform module")
535 raise
Dan Talayco48370102010-03-03 15:17:33 -0800536
537try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700538 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800539except:
540 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700541 raise
Dan Talayco48370102010-03-03 15:17:33 -0800542
543if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700544 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800545
546logging.debug("Configuration: " + str(config))
547logging.info("OF port map: " + str(config["port_map"]))
548
549# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800550for (modname,mod) in config["mod_name_map"].items():
551 try:
552 mod.test_set_init(config)
553 except:
554 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700555 raise
Dan Talayco48370102010-03-03 15:17:33 -0800556
Dan Talayco2c0dba32010-03-06 22:47:06 -0800557if config["dbg_level"] == logging.CRITICAL:
558 _verb = 0
559elif config["dbg_level"] >= logging.WARNING:
560 _verb = 1
561else:
562 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800563
Rich Lanee55abf72012-07-26 20:11:42 -0700564oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700565oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700566
Rich Laneee57ad02012-07-13 15:40:36 -0700567if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700568 print "ERROR: Super-user privileges required. Please re-run with " \
569 "sudo or as root."
570 exit(1)
571
Rich Lane8592bec2012-09-03 09:06:59 -0700572if config["random_seed"] is not None:
573 logging.info("Random seed: %d" % config["random_seed"])
574 random.seed(config["random_seed"])
575
Rich Lane5bd6cf92012-10-04 17:57:24 -0700576# Remove python's signal handler which raises KeyboardError. Exiting from an
577# exception waits for all threads to terminate which might not happen.
578signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700579
580if __name__ == "__main__":
581 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700582 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700583 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700584 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700585 if oftest.testutils.skipped_test_count == 1: ts = " test"
586 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
587 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700588 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700589 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700590 # exit(1) hangs sometimes
591 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700592 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700593 os._exit(1)