blob: c8915553dc2e858b8503885cef098496ad8a865e [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():
Rich Lanea92f2522012-10-04 18:11:04 -0700202 cfg[key] = getattr(opts, key)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800203
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
Rich Lanea92f2522012-10-04 18:11:04 -0700227 parser.set_defaults(**cfg_dflt)
Dan Talayco48370102010-03-03 15:17:33 -0800228
Dan Talayco2c0dba32010-03-06 22:47:06 -0800229 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800230 plat_help = """Set the platform type. Valid values include:
231 local: User space virtual ethernet pair setup
232 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800233 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800234 """
Rich Lane941d1dd2012-07-30 14:27:53 -0700235 parser.add_option("-a", "--platform-args", help="Custom arguments per platform.")
Dan Talayco48370102010-03-03 15:17:33 -0800236 parser.add_option("-P", "--platform", help=plat_help)
237 parser.add_option("-H", "--host", dest="controller_host",
238 help="The IP/name of the test controller host")
239 parser.add_option("-p", "--port", dest="controller_port",
240 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800241 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700242 default) or a comma separated list of:
243 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800244 testcase Run tests in all modules with the name testcase
245 module.testcase Run the specific test case
246 """
Rich Lanef261a102012-07-25 13:41:38 -0700247 parser.add_option("-T", "--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800248 parser.add_option("--log-file",
249 help="Name of log file, empty string to log to console")
250 parser.add_option("--debug",
251 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700252 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800253 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700254 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800255 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700256 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800257 help="Base interface index number (optional)")
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700258 parser.add_option("--list-test-names", action='store_true',
259 help="List only test names.", default=False)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800260 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700261 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700262 parser.add_option("--verbose", action="store_true",
263 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700264 parser.add_option("--relax", action="store_true",
265 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700266 parser.add_option("--param", type="int",
267 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700268 parser.add_option("--profile",
269 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700270 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700271 help="""Set test parameters: key=val;...
272 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
273 See --list""")
Rich Laneee57ad02012-07-13 15:40:36 -0700274 parser.add_option("--allow-user", action="store_true",
275 help="Proceed even if oftest is not run as root")
Rich Lane9a84a4f2012-07-17 12:27:42 -0700276 parser.add_option("--fail-skipped", action="store_true",
277 help="Return failure if any test was skipped")
Rich Lanee55abf72012-07-26 20:11:42 -0700278 parser.add_option("--default-timeout", type="int",
279 help="Timeout in seconds for most operations")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000280 parser.add_option("--minsize", type="int",
281 help="Minimum allowable packet size on the dataplane.",
282 default=0)
Rich Lane8592bec2012-09-03 09:06:59 -0700283 parser.add_option("--random-seed", type="int",
284 help="Random number generator seed",
285 default=None)
Rich Lanebc3d2962012-09-25 09:34:17 -0700286 parser.add_option("--test-dir", type="string",
287 help="Directory containing tests")
Rich Lane8aebc5e2012-09-25 17:57:53 -0700288 parser.add_option("--platform-dir", type="string",
289 help="Directory containing platform modules")
Rich Lane1673b8f2012-09-25 18:35:18 -0700290 parser.add_option("--profile-dir", type="string",
291 help="Directory containing profile modules")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000292
Dan Talayco48370102010-03-03 15:17:33 -0800293 # Might need this if other parsers want command line
294 # parser.allow_interspersed_args = False
295 (options, args) = parser.parse_args()
296
297 config = config_get(options)
298
299 return (config, args)
300
Rich Lane1673b8f2012-09-25 18:35:18 -0700301def load_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700302 """
303 Import a profile from the profiles library
304 """
305
Dan Talaycod7c80d12012-04-03 15:20:57 -0700306 global profile_mod
Rich Lane1673b8f2012-09-25 18:35:18 -0700307 logging.info("Importing profile: %s" % config["profile"])
308 try:
309 profile_mod = imp.load_module(config["profile"], *imp.find_module(config["profile"], [config["profile_dir"]]))
310 if not "skip_test_list" in dir(profile_mod):
311 die("Profile did not define skip_test_list")
312 except:
313 logging.info("Could not import profile: %s.py" % config["profile"])
314 print "Failed to import profile: %s" % config["profile"]
315 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700316
Dan Talayco48370102010-03-03 15:17:33 -0800317def logging_setup(config):
318 """
319 Set up logging based on config
320 """
321 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
322 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800323 logging.basicConfig(filename=config["log_file"],
324 level=config["dbg_level"],
325 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800326
Dan Talayco2c0dba32010-03-06 22:47:06 -0800327def test_list_generate(config):
328 """Generate the list of all known tests indexed by module name
329
330 Conventions: Test files must implement the function test_set_init
331
Dan Talayco1a88c122010-03-07 22:00:20 -0800332 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800333
334 @param config The oft configuration dictionary
335 @returns An array of triples (mod-name, module, [tests]) where
336 mod-name is the string (filename) of the module, module is the
337 value returned from __import__'ing the module and [tests] is an
338 array of strings giving the test cases from the module.
339 """
340
341 # Find and import test files
342 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
343 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
344 stdin=p1.stdout, stdout=PIPE)
345
346 all_tests = {}
347 mod_name_map = {}
348 # There's an extra empty entry at the end of the list
349 filelist = p2.communicate()[0].split("\n")[:-1]
350 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700351 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800352 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700353 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800354
355 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700356 if sys.modules.has_key(modname):
357 mod = sys.modules[modname]
358 else:
359 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800360 except:
361 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700362 raise
Rich Lane520e4152012-07-09 16:18:16 -0700363
364 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
365 issubclass(getattr(mod, k), unittest.TestCase)]
366 if tests:
367 mod_name_map[modname] = mod
368 all_tests[mod] = tests
369
Dan Talayco2c0dba32010-03-06 22:47:06 -0800370 config["all_tests"] = all_tests
371 config["mod_name_map"] = mod_name_map
372
373def die(msg, exit_val=1):
374 print msg
375 logging.critical(msg)
376 sys.exit(exit_val)
377
378def add_test(suite, mod, name):
379 logging.info("Adding test " + mod.__name__ + "." + name)
Rich Lanea92f2522012-10-04 18:11:04 -0700380 suite.addTest(getattr(mod, name)())
Dan Talayco2c0dba32010-03-06 22:47:06 -0800381
Dan Talayco79f36082010-03-11 16:53:53 -0800382def _space_to(n, str):
383 """
384 Generate a string of spaces to achieve width n given string str
385 If length of str >= n, return one space
386 """
387 spaces = n - len(str)
388 if spaces > 0:
389 return " " * spaces
390 return " "
391
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700392def test_prio_get(mod, test):
393 """
394 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700395
396 If test is in "skip list" from profile, return the skip value
397
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700398 If set in the test_prio variable for the module, return
399 that value. Otherwise return 100 (default)
400 """
Rich Lane1673b8f2012-09-25 18:35:18 -0700401 if test in profile_mod.skip_test_list:
402 logging.info("Skipping test %s due to profile" % test)
403 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700404 if 'test_prio' in dir(mod):
405 if test in mod.test_prio.keys():
406 return mod.test_prio[test]
407 return TEST_PRIO_DEFAULT
408
Dan Talayco48370102010-03-03 15:17:33 -0800409#
410# Main script
411#
412
413# Get configuration, set up logging, import platform from file
414(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800415
Rich Laned7a85c42012-09-28 15:38:45 -0700416logging_setup(config)
417logging.info("++++++++ " + time.asctime() + " ++++++++")
418
Rich Lanee284b6b2012-10-03 09:19:58 -0700419# Allow tests to import each other
420sys.path.append(config["test_dir"])
421
Dan Talayco2c0dba32010-03-06 22:47:06 -0800422test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700423oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800424
Rich Lanec27fa002012-09-28 12:49:12 -0700425load_profile(config)
426
Dan Talayco2c0dba32010-03-06 22:47:06 -0800427# Check if test list is requested; display and exit if so
428if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800429 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700430 mod_count = 0
431 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800432 print "\nTest List:"
433 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800434 if config["test_spec"] != "all" and \
435 config["test_spec"] != mod.__name__:
436 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700437 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800438 did_print = True
439 desc = mod.__doc__.strip()
440 desc = desc.split('\n')[0]
441 start_str = " Module " + mod.__name__ + ": "
442 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800443 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700444 try:
Rich Lanea92f2522012-10-04 18:11:04 -0700445 desc = getattr(mod, test).__doc__.strip()
Dan Talayco551befa2010-07-15 17:05:32 -0700446 desc = desc.split('\n')[0]
447 except:
448 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700449 if test_prio_get(mod, test) < 0:
450 start_str = " * " + test + ":"
451 else:
452 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700453 if len(start_str) > 22:
454 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800455 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700456 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800457 print
458 if not did_print:
459 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700460 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700461 print "%d modules shown with a total of %d tests" % \
462 (mod_count, test_count)
463 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700464 print "Tests preceded by * are not run by default"
465 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700466 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700467 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800468 sys.exit(0)
469
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700470# Check if test list is requested; display and exit if so
471if config["list_test_names"]:
472 for mod in config["all_tests"].keys():
473 if config["test_spec"] != "all" and \
474 config["test_spec"] != mod.__name__:
475 continue
476 desc = mod.__doc__.strip()
477 desc = desc.split('\n')[0]
478 for test in config["all_tests"][mod]:
Jeffrey Townsendfb9ce272012-10-02 21:10:29 -0700479 if test_prio_get(mod, test) >= 0:
480 print "%s.%s" % (mod.__name__, test)
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700481 sys.exit(0)
482
Dan Talayco2c0dba32010-03-06 22:47:06 -0800483# Generate the test suite
484#@todo Decide if multiple suites are ever needed
485suite = unittest.TestSuite()
486
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700487#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800488if config["test_spec"] == "all":
489 for mod in config["all_tests"].keys():
490 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700491 # For now, a way to avoid tests
492 if test_prio_get(mod, test) >= 0:
493 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800494
495else:
496 for ts_entry in config["test_spec"].split(","):
497 parts = ts_entry.split(".")
498
499 if len(parts) == 1: # Either a module or test name
500 if ts_entry in config["mod_name_map"].keys():
501 mod = config["mod_name_map"][ts_entry]
502 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700503 if test_prio_get(mod, test) >= 0:
504 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800505 else: # Search for matching tests
506 test_found = False
507 for mod in config["all_tests"].keys():
508 if ts_entry in config["all_tests"][mod]:
509 add_test(suite, mod, ts_entry)
510 test_found = True
511 if not test_found:
512 die("Could not find module or test: " + ts_entry)
513
514 elif len(parts) == 2: # module.test
515 if parts[0] not in config["mod_name_map"]:
516 die("Unknown module in test spec: " + ts_entry)
517 mod = config["mod_name_map"][parts[0]]
518 if parts[1] in config["all_tests"][mod]:
519 add_test(suite, mod, parts[1])
520 else:
521 die("No known test matches: " + ts_entry)
522
523 else:
524 die("Bad test spec: " + ts_entry)
525
Rich Lane8aebc5e2012-09-25 17:57:53 -0700526# Load the platform module
527platform_name = config["platform"]
528logging.info("Importing platform: " + platform_name)
529platform_mod = None
530try:
531 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"], config["test_dir"]]))
532except:
533 logging.warn("Failed to import " + platform_name + " platform module")
534 raise
Dan Talayco48370102010-03-03 15:17:33 -0800535
536try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700537 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800538except:
539 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700540 raise
Dan Talayco48370102010-03-03 15:17:33 -0800541
542if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700543 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800544
545logging.debug("Configuration: " + str(config))
546logging.info("OF port map: " + str(config["port_map"]))
547
548# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800549for (modname,mod) in config["mod_name_map"].items():
550 try:
551 mod.test_set_init(config)
552 except:
553 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700554 raise
Dan Talayco48370102010-03-03 15:17:33 -0800555
Dan Talayco2c0dba32010-03-06 22:47:06 -0800556if config["dbg_level"] == logging.CRITICAL:
557 _verb = 0
558elif config["dbg_level"] >= logging.WARNING:
559 _verb = 1
560else:
561 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800562
Rich Lanee55abf72012-07-26 20:11:42 -0700563oftest.ofutils.default_timeout = config["default_timeout"]
Rich Laneda3b5ad2012-10-03 09:05:32 -0700564oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700565
Rich Laneee57ad02012-07-13 15:40:36 -0700566if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700567 print "ERROR: Super-user privileges required. Please re-run with " \
568 "sudo or as root."
569 exit(1)
570
Rich Lane8592bec2012-09-03 09:06:59 -0700571if config["random_seed"] is not None:
572 logging.info("Random seed: %d" % config["random_seed"])
573 random.seed(config["random_seed"])
574
Rich Lane5bd6cf92012-10-04 17:57:24 -0700575# Remove python's signal handler which raises KeyboardError. Exiting from an
576# exception waits for all threads to terminate which might not happen.
577signal.signal(signal.SIGINT, signal.SIG_DFL)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700578
579if __name__ == "__main__":
580 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700581 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700582 if oftest.testutils.skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700583 ts = " tests"
Rich Laneda3b5ad2012-10-03 09:05:32 -0700584 if oftest.testutils.skipped_test_count == 1: ts = " test"
585 logging.info("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
586 print("Skipped " + str(oftest.testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700587 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700588 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700589 # exit(1) hangs sometimes
590 os._exit(1)
Rich Laneda3b5ad2012-10-03 09:05:32 -0700591 if oftest.testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700592 os._exit(1)