blob: d527cb817f8855b76932f4286e9400dccf7b8254 [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)
97@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
Dan Talayco48370102010-03-03 15:17:33 -0800119
Rich Lane39878042012-07-09 14:45:35 -0700120pydir = os.path.join(os.path.dirname(__file__), '..', 'src', 'python')
121if os.path.exists(os.path.join(pydir, 'oftest')):
122 # Running from source tree
123 sys.path.insert(0, pydir)
124
Dan Talaycoba3745c2010-07-21 21:51:08 -0700125import testutils
Rich Lanee55abf72012-07-26 20:11:42 -0700126import oftest.ofutils
Dan Talaycoba3745c2010-07-21 21:51:08 -0700127
Dan Talayco02eca0b2010-04-15 16:09:43 -0700128try:
129 import scapy.all as scapy
130except:
131 try:
132 import scapy as scapy
133 except:
134 sys.exit("Need to install scapy for packet parsing")
135
Dan Talaycod7c80d12012-04-03 15:20:57 -0700136##@var Profile module
137profile_mod = None
138
Dan Talayco48370102010-03-03 15:17:33 -0800139##@var DEBUG_LEVELS
140# Map from strings to debugging levels
141DEBUG_LEVELS = {
142 'debug' : logging.DEBUG,
143 'verbose' : logging.DEBUG,
144 'info' : logging.INFO,
145 'warning' : logging.WARNING,
146 'warn' : logging.WARNING,
147 'error' : logging.ERROR,
148 'critical' : logging.CRITICAL
149}
150
151_debug_default = "warning"
152_debug_level_default = DEBUG_LEVELS[_debug_default]
153
Rich Lane8aebc5e2012-09-25 17:57:53 -0700154root_dir = os.path.join(os.path.dirname(__file__), "..")
155
Dan Talayco48370102010-03-03 15:17:33 -0800156##@var config_default
157# The default configuration dictionary for OFT
158config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700159 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800160 "platform" : "local",
Rich Lane941d1dd2012-07-30 14:27:53 -0700161 "platform_args" : None,
Rich Lane1aeccc42012-07-29 17:58:10 -0700162 "controller_host" : "0.0.0.0",
Dan Talayco48370102010-03-03 15:17:33 -0800163 "controller_port" : 6633,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700164 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800165 "test_spec" : "all",
Rich Lane9d7330a2012-07-10 14:37:44 -0700166 "test_dir" : os.path.dirname(__file__),
Dan Talayco48370102010-03-03 15:17:33 -0800167 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800168 "list" : False,
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700169 "list_test_names" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800170 "debug" : _debug_default,
171 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700172 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700173 "test_params" : "None",
Rich Laneee57ad02012-07-13 15:40:36 -0700174 "profile" : None,
175 "allow_user" : False,
Rich Lane9a84a4f2012-07-17 12:27:42 -0700176 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700177 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000178 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -0700179 "random_seed" : None,
Rich Lane8aebc5e2012-09-25 17:57:53 -0700180 "platform_dir" : os.path.join(root_dir, "platforms"),
Dan Talayco48370102010-03-03 15:17:33 -0800181}
182
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700183# Default test priority
184TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700185TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700186
Dan Talayco1a88c122010-03-07 22:00:20 -0800187#@todo Set up a dict of config params so easier to manage:
188# <param> <cmdline flags> <default value> <help> <optional parser>
189
Dan Talayco48370102010-03-03 15:17:33 -0800190# Map options to config structure
191def config_get(opts):
192 "Convert options class to OFT configuration dictionary"
193 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800194 for key in cfg.keys():
195 cfg[key] = eval("opts." + key)
196
197 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800198 if opts.debug not in DEBUG_LEVELS.keys():
199 print "Warning: Bad value specified for debug level; using default"
200 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700201 if opts.verbose:
202 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800203 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800204
Dan Talayco48370102010-03-03 15:17:33 -0800205 return cfg
206
207def config_setup(cfg_dflt):
208 """
209 Set up the configuration including parsing the arguments
210
211 @param cfg_dflt The default configuration dictionary
212 @return A pair (config, args) where config is an config
213 object and args is any additional arguments from the command line
214 """
215
216 parser = OptionParser(version="%prog 0.1")
217
Dan Talayco2c0dba32010-03-06 22:47:06 -0800218 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800219 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800220 for key in cfg_dflt.keys():
221 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
Dan Talayco48370102010-03-03 15:17:33 -0800222
Dan Talayco2c0dba32010-03-06 22:47:06 -0800223 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800224 plat_help = """Set the platform type. Valid values include:
225 local: User space virtual ethernet pair setup
226 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800227 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800228 """
Rich Lane941d1dd2012-07-30 14:27:53 -0700229 parser.add_option("-a", "--platform-args", help="Custom arguments per platform.")
Dan Talayco48370102010-03-03 15:17:33 -0800230 parser.add_option("-P", "--platform", help=plat_help)
231 parser.add_option("-H", "--host", dest="controller_host",
232 help="The IP/name of the test controller host")
233 parser.add_option("-p", "--port", dest="controller_port",
234 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800235 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700236 default) or a comma separated list of:
237 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800238 testcase Run tests in all modules with the name testcase
239 module.testcase Run the specific test case
240 """
Rich Lanef261a102012-07-25 13:41:38 -0700241 parser.add_option("-T", "--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800242 parser.add_option("--log-file",
243 help="Name of log file, empty string to log to console")
244 parser.add_option("--debug",
245 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700246 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800247 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700248 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800249 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700250 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800251 help="Base interface index number (optional)")
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700252 parser.add_option("--list-test-names", action='store_true',
253 help="List only test names.", default=False)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800254 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700255 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700256 parser.add_option("--verbose", action="store_true",
257 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700258 parser.add_option("--relax", action="store_true",
259 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700260 parser.add_option("--param", type="int",
261 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700262 parser.add_option("--profile",
263 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700264 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700265 help="""Set test parameters: key=val;...
266 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
267 See --list""")
Rich Laneee57ad02012-07-13 15:40:36 -0700268 parser.add_option("--allow-user", action="store_true",
269 help="Proceed even if oftest is not run as root")
Rich Lane9a84a4f2012-07-17 12:27:42 -0700270 parser.add_option("--fail-skipped", action="store_true",
271 help="Return failure if any test was skipped")
Rich Lanee55abf72012-07-26 20:11:42 -0700272 parser.add_option("--default-timeout", type="int",
273 help="Timeout in seconds for most operations")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000274 parser.add_option("--minsize", type="int",
275 help="Minimum allowable packet size on the dataplane.",
276 default=0)
Rich Lane8592bec2012-09-03 09:06:59 -0700277 parser.add_option("--random-seed", type="int",
278 help="Random number generator seed",
279 default=None)
Rich Lanebc3d2962012-09-25 09:34:17 -0700280 parser.add_option("--test-dir", type="string",
281 help="Directory containing tests")
Rich Lane8aebc5e2012-09-25 17:57:53 -0700282 parser.add_option("--platform-dir", type="string",
283 help="Directory containing platform modules")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000284
Dan Talayco48370102010-03-03 15:17:33 -0800285 # Might need this if other parsers want command line
286 # parser.allow_interspersed_args = False
287 (options, args) = parser.parse_args()
288
289 config = config_get(options)
290
291 return (config, args)
292
Dan Talaycod7c80d12012-04-03 15:20:57 -0700293def check_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700294 """
295 Import a profile from the profiles library
296 """
297
Dan Talaycod7c80d12012-04-03 15:20:57 -0700298 global profile_mod
299 if "profile" in config and config["profile"]:
Dan Talaycod15bed52012-04-04 10:39:52 -0700300 logging.info("Importing profile: %s" % config["profile"])
301 profile_name = "profiles." + config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700302 try:
Dan Talaycod15bed52012-04-04 10:39:52 -0700303 top_mod = __import__(profile_name)
304 profile_mod = eval("top_mod." + config["profile"])
305 logging.info("Imported profile %s. Dir: %s" %
306 (config["profile"], str(dir(profile_mod))))
Dan Talaycod7c80d12012-04-03 15:20:57 -0700307 except:
308 logging.info("Could not import profile: %s.py" %
309 config["profile"])
Dan Talaycod15bed52012-04-04 10:39:52 -0700310 print "Failed to import profile: %s" % config["profile"]
Rich Laneef403e52012-07-09 14:59:43 -0700311 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700312 else:
313 logging.info("No profile specified")
314
315
Dan Talayco48370102010-03-03 15:17:33 -0800316def logging_setup(config):
317 """
318 Set up logging based on config
319 """
320 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
321 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800322 logging.basicConfig(filename=config["log_file"],
323 level=config["dbg_level"],
324 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800325
Dan Talayco2c0dba32010-03-06 22:47:06 -0800326def test_list_generate(config):
327 """Generate the list of all known tests indexed by module name
328
329 Conventions: Test files must implement the function test_set_init
330
Dan Talayco1a88c122010-03-07 22:00:20 -0800331 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800332
333 @param config The oft configuration dictionary
334 @returns An array of triples (mod-name, module, [tests]) where
335 mod-name is the string (filename) of the module, module is the
336 value returned from __import__'ing the module and [tests] is an
337 array of strings giving the test cases from the module.
338 """
339
340 # Find and import test files
341 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
342 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
343 stdin=p1.stdout, stdout=PIPE)
344
345 all_tests = {}
346 mod_name_map = {}
347 # There's an extra empty entry at the end of the list
348 filelist = p2.communicate()[0].split("\n")[:-1]
349 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700350 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800351 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700352 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800353
354 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700355 if sys.modules.has_key(modname):
356 mod = sys.modules[modname]
357 else:
358 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800359 except:
360 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700361 raise
Rich Lane520e4152012-07-09 16:18:16 -0700362
363 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
364 issubclass(getattr(mod, k), unittest.TestCase)]
365 if tests:
366 mod_name_map[modname] = mod
367 all_tests[mod] = tests
368
Dan Talayco2c0dba32010-03-06 22:47:06 -0800369 config["all_tests"] = all_tests
370 config["mod_name_map"] = mod_name_map
371
372def die(msg, exit_val=1):
373 print msg
374 logging.critical(msg)
375 sys.exit(exit_val)
376
377def add_test(suite, mod, name):
378 logging.info("Adding test " + mod.__name__ + "." + name)
379 suite.addTest(eval("mod." + name)())
380
Dan Talayco79f36082010-03-11 16:53:53 -0800381def _space_to(n, str):
382 """
383 Generate a string of spaces to achieve width n given string str
384 If length of str >= n, return one space
385 """
386 spaces = n - len(str)
387 if spaces > 0:
388 return " " * spaces
389 return " "
390
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700391def test_prio_get(mod, test):
392 """
393 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700394
395 If test is in "skip list" from profile, return the skip value
396
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700397 If set in the test_prio variable for the module, return
398 that value. Otherwise return 100 (default)
399 """
Dan Talaycod7c80d12012-04-03 15:20:57 -0700400 if profile_mod:
401 if profile_mod.skip_test_list and 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
Dan Talayco2c0dba32010-03-06 22:47:06 -0800416test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700417oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800418
419# Check if test list is requested; display and exit if so
420if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800421 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700422 mod_count = 0
423 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800424 print "\nTest List:"
425 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800426 if config["test_spec"] != "all" and \
427 config["test_spec"] != mod.__name__:
428 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700429 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800430 did_print = True
431 desc = mod.__doc__.strip()
432 desc = desc.split('\n')[0]
433 start_str = " Module " + mod.__name__ + ": "
434 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800435 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700436 try:
437 desc = eval('mod.' + test + '.__doc__.strip()')
438 desc = desc.split('\n')[0]
439 except:
440 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700441 if test_prio_get(mod, test) < 0:
442 start_str = " * " + test + ":"
443 else:
444 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700445 if len(start_str) > 22:
446 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800447 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700448 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800449 print
450 if not did_print:
451 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700452 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700453 print "%d modules shown with a total of %d tests" % \
454 (mod_count, test_count)
455 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700456 print "Tests preceded by * are not run by default"
457 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700458 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700459 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800460 sys.exit(0)
461
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700462# Check if test list is requested; display and exit if so
463if config["list_test_names"]:
464 for mod in config["all_tests"].keys():
465 if config["test_spec"] != "all" and \
466 config["test_spec"] != mod.__name__:
467 continue
468 desc = mod.__doc__.strip()
469 desc = desc.split('\n')[0]
470 for test in config["all_tests"][mod]:
471 print "%s.%s" % (mod.__name__, test)
472 sys.exit(0)
473
Dan Talayco2c0dba32010-03-06 22:47:06 -0800474logging_setup(config)
475logging.info("++++++++ " + time.asctime() + " ++++++++")
476
Dan Talaycod7c80d12012-04-03 15:20:57 -0700477check_profile(config)
478
Dan Talayco2c0dba32010-03-06 22:47:06 -0800479# Generate the test suite
480#@todo Decide if multiple suites are ever needed
481suite = unittest.TestSuite()
482
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700483#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800484if config["test_spec"] == "all":
485 for mod in config["all_tests"].keys():
486 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700487 # For now, a way to avoid tests
488 if test_prio_get(mod, test) >= 0:
489 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800490
491else:
492 for ts_entry in config["test_spec"].split(","):
493 parts = ts_entry.split(".")
494
495 if len(parts) == 1: # Either a module or test name
496 if ts_entry in config["mod_name_map"].keys():
497 mod = config["mod_name_map"][ts_entry]
498 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700499 if test_prio_get(mod, test) >= 0:
500 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800501 else: # Search for matching tests
502 test_found = False
503 for mod in config["all_tests"].keys():
504 if ts_entry in config["all_tests"][mod]:
505 add_test(suite, mod, ts_entry)
506 test_found = True
507 if not test_found:
508 die("Could not find module or test: " + ts_entry)
509
510 elif len(parts) == 2: # module.test
511 if parts[0] not in config["mod_name_map"]:
512 die("Unknown module in test spec: " + ts_entry)
513 mod = config["mod_name_map"][parts[0]]
514 if parts[1] in config["all_tests"][mod]:
515 add_test(suite, mod, parts[1])
516 else:
517 die("No known test matches: " + ts_entry)
518
519 else:
520 die("Bad test spec: " + ts_entry)
521
Rich Lane8aebc5e2012-09-25 17:57:53 -0700522# Load the platform module
523platform_name = config["platform"]
524logging.info("Importing platform: " + platform_name)
525platform_mod = None
526try:
527 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"], config["test_dir"]]))
528except:
529 logging.warn("Failed to import " + platform_name + " platform module")
530 raise
Dan Talayco48370102010-03-03 15:17:33 -0800531
532try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700533 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800534except:
535 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700536 raise
Dan Talayco48370102010-03-03 15:17:33 -0800537
538if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700539 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800540
541logging.debug("Configuration: " + str(config))
542logging.info("OF port map: " + str(config["port_map"]))
543
544# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800545for (modname,mod) in config["mod_name_map"].items():
546 try:
547 mod.test_set_init(config)
548 except:
549 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700550 raise
Dan Talayco48370102010-03-03 15:17:33 -0800551
Dan Talayco2c0dba32010-03-06 22:47:06 -0800552if config["dbg_level"] == logging.CRITICAL:
553 _verb = 0
554elif config["dbg_level"] >= logging.WARNING:
555 _verb = 1
556else:
557 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800558
Rich Lanee55abf72012-07-26 20:11:42 -0700559oftest.ofutils.default_timeout = config["default_timeout"]
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000560testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700561
Rich Laneee57ad02012-07-13 15:40:36 -0700562if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700563 print "ERROR: Super-user privileges required. Please re-run with " \
564 "sudo or as root."
565 exit(1)
566
Rich Lane8592bec2012-09-03 09:06:59 -0700567if config["random_seed"] is not None:
568 logging.info("Random seed: %d" % config["random_seed"])
569 random.seed(config["random_seed"])
570
Dan Talaycoac25cf32010-07-20 14:08:28 -0700571
572if __name__ == "__main__":
573 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700574 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700575 if testutils.skipped_test_count > 0:
576 ts = " tests"
577 if testutils.skipped_test_count == 1: ts = " test"
578 logging.info("Skipped " + str(testutils.skipped_test_count) + ts)
579 print("Skipped " + str(testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700580 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700581 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700582 # exit(1) hangs sometimes
583 os._exit(1)
Rich Lane9a84a4f2012-07-17 12:27:42 -0700584 if testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700585 os._exit(1)