blob: 72299200d68b765a4502104129eb3d4c42025b20 [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
18 port_count : (Optional) Number of ports in dataplane
19 base_of_port : (Optional) Base OpenFlow port number in dataplane
20 base_if_index : (Optional) Base OS network interface for dataplane
Dan Talayco2c0dba32010-03-06 22:47:06 -080021 test_dir : (TBD) Directory to search for test files (default .)
Dan Talayco48370102010-03-03 15:17:33 -080022 test_spec : (TBD) Specification of test(s) to run
23 log_file : Filename for test logging
Dan Talayco2c0dba32010-03-06 22:47:06 -080024 list : Boolean: List all tests and exit
Dan Talayco48370102010-03-03 15:17:33 -080025 debug : String giving debug level (info, warning, error...)
Dan Talayco2c0dba32010-03-06 22:47:06 -080026</pre>
Dan Talayco48370102010-03-03 15:17:33 -080027
28See config_defaults below for the default values.
29
Dan Talayco2c0dba32010-03-06 22:47:06 -080030The following are stored in the config dictionary, but are not currently
31configurable through the command line.
32
33<pre>
34 dbg_level : logging module value of debug level
35 port_map : Map of dataplane OpenFlow port to OS interface names
36 test_mod_map : Dictionary indexed by module names and whose value
37 is the module reference
38 all_tests : Dictionary indexed by module reference and whose
39 value is a list of functions in that module
40</pre>
41
Dan Talaycoc24aaae2010-07-08 14:05:24 -070042Each test may be assigned a priority by setting test_prio["TestName"] in
43the respective module. For now, the only use of this is to avoid
44automatic inclusion of tests into the default list. This is done by
45setting the test_prio value less than 0. Eventually we may add ordering
46of test execution by test priority.
47
Dan Talayco2c0dba32010-03-06 22:47:06 -080048To add a test to the system, either: edit an existing test case file (like
49basic.py) to add a test class which inherits from unittest.TestCase (directly
50or indirectly); or add a new file which includes a function definition
51test_set_init(config). Preferably the file is in the same directory as existing
52tests, though you can specify the directory on the command line. The file
53should not be called "all" as that's reserved for the test-spec.
54
55If you add a new file, the test_set_init function should record the port
56map object from the configuration along with whatever other configuration
57information it may need.
58
59TBD: To add configuration to the system, first add an entry to config_default
Dan Talayco48370102010-03-03 15:17:33 -080060below. If you want this to be a command line parameter, edit config_setup
61to add the option and default value to the parser. Then edit config_get
62to make sure the option value gets copied into the configuration
63structure (which then gets passed to everyone else).
64
65By convention, oft attempts to import the contents of a file by the
66name of $platform.py into the local namespace.
67
68IMPORTANT: That file should define a function platform_config_update which
69takes a configuration dictionary as an argument and updates it for the
70current run. In particular, it should set up config["port_map"] with
71the proper map from OF port numbers to OF interface names.
72
73You can add your own platform, say gp104, by adding a file gp104.py
74that defines the function platform_config_update and then use the
75parameter --platform=gp104 on the command line.
76
77If platform_config_update does not set config["port_map"], an attempt
78is made to generate a default map via the function default_port_map_setup.
79This will use "local" and "remote" for platform names if available
80and generate a sequential map based on the values of base_of_port and
81base_if_index in the configuration structure.
82
Dan Talayco48370102010-03-03 15:17:33 -080083The current model for test sets is basic.py. The current convention is
84that the test set should implement a function test_set_init which takes
85an oft configuration dictionary and returns a unittest.TestSuite object.
86Future test sets should do the same thing.
87
Dan Talayco52f64442010-03-03 15:32:41 -080088Default setup:
89
90The default setup runs locally using veth pairs. To exercise this,
91checkout and build an openflow userspace datapath. Then start it on
92the local host:
Dan Talayco2c0dba32010-03-06 22:47:06 -080093<pre>
Dan Talayco52f64442010-03-03 15:32:41 -080094 sudo ~/openflow/regress/bin/veth_setup.pl
95 sudo ofdatapath -i veth0,veth2,veth4,veth6 punix:/tmp/ofd &
96 sudo ofprotocol unix:/tmp/ofd tcp:127.0.0.1 --fail=closed --max-backoff=1 &
97
98Next, run oft:
99 sudo ./oft --debug=info
Dan Talayco2c0dba32010-03-06 22:47:06 -0800100</pre>
Dan Talayco52f64442010-03-03 15:32:41 -0800101
102Examine oft.log if things don't work.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800103
Dan Talayco1a88c122010-03-07 22:00:20 -0800104@todo Support per-component debug levels (esp controller vs dataplane)
105@todo Consider moving oft up a level
Dan Talayco2c0dba32010-03-06 22:47:06 -0800106
Dan Talayco1a88c122010-03-07 22:00:20 -0800107Current test case setup:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800108 Files in this or sub directories (or later, directory specified on
109command line) that contain a function test_set_init are considered test
110files.
111 The function test_set_init examines the test_spec config variable
112and generates a suite of tests.
113 Support a command line option --test_mod so that all tests in that
114module will be run.
115 Support all to specify all tests from the module.
116
Dan Talayco48370102010-03-03 15:17:33 -0800117"""
118
119import sys
120from optparse import OptionParser
Dan Talayco2c0dba32010-03-06 22:47:06 -0800121from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -0800122import logging
123import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800124import time
Brandon Heller446c1432010-04-01 12:43:27 -0700125import os
Rich Lane6b452bc2012-07-09 16:52:21 -0700126import imp
Dan Talayco48370102010-03-03 15:17:33 -0800127
Rich Lane39878042012-07-09 14:45:35 -0700128pydir = os.path.join(os.path.dirname(__file__), '..', 'src', 'python')
129if os.path.exists(os.path.join(pydir, 'oftest')):
130 # Running from source tree
131 sys.path.insert(0, pydir)
132
Dan Talaycoba3745c2010-07-21 21:51:08 -0700133import 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,
170 "port_count" : 4,
171 "base_of_port" : 1,
172 "base_if_index" : 1,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700173 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800174 "test_spec" : "all",
Rich Lane9d7330a2012-07-10 14:37:44 -0700175 "test_dir" : os.path.dirname(__file__),
Dan Talayco48370102010-03-03 15:17:33 -0800176 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800177 "list" : False,
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700178 "list_test_names" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800179 "debug" : _debug_default,
180 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700181 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700182 "test_params" : "None",
Rich Laneee57ad02012-07-13 15:40:36 -0700183 "profile" : None,
184 "allow_user" : False,
Rich Lane9a84a4f2012-07-17 12:27:42 -0700185 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700186 "default_timeout" : 2,
Dan Talayco48370102010-03-03 15:17:33 -0800187}
188
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700189# Default test priority
190TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700191TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700192
Dan Talayco1a88c122010-03-07 22:00:20 -0800193#@todo Set up a dict of config params so easier to manage:
194# <param> <cmdline flags> <default value> <help> <optional parser>
195
Dan Talayco48370102010-03-03 15:17:33 -0800196# Map options to config structure
197def config_get(opts):
198 "Convert options class to OFT configuration dictionary"
199 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800200 for key in cfg.keys():
201 cfg[key] = eval("opts." + key)
202
203 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800204 if opts.debug not in DEBUG_LEVELS.keys():
205 print "Warning: Bad value specified for debug level; using default"
206 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700207 if opts.verbose:
208 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800209 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800210
Dan Talayco48370102010-03-03 15:17:33 -0800211 return cfg
212
213def config_setup(cfg_dflt):
214 """
215 Set up the configuration including parsing the arguments
216
217 @param cfg_dflt The default configuration dictionary
218 @return A pair (config, args) where config is an config
219 object and args is any additional arguments from the command line
220 """
221
222 parser = OptionParser(version="%prog 0.1")
223
Dan Talayco2c0dba32010-03-06 22:47:06 -0800224 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800225 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800226 for key in cfg_dflt.keys():
227 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
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")
Dan Talayco48370102010-03-03 15:17:33 -0800280 # Might need this if other parsers want command line
281 # parser.allow_interspersed_args = False
282 (options, args) = parser.parse_args()
283
284 config = config_get(options)
285
286 return (config, args)
287
Dan Talaycod7c80d12012-04-03 15:20:57 -0700288def check_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700289 """
290 Import a profile from the profiles library
291 """
292
Dan Talaycod7c80d12012-04-03 15:20:57 -0700293 global profile_mod
294 if "profile" in config and config["profile"]:
Dan Talaycod15bed52012-04-04 10:39:52 -0700295 logging.info("Importing profile: %s" % config["profile"])
296 profile_name = "profiles." + config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700297 try:
Dan Talaycod15bed52012-04-04 10:39:52 -0700298 top_mod = __import__(profile_name)
299 profile_mod = eval("top_mod." + config["profile"])
300 logging.info("Imported profile %s. Dir: %s" %
301 (config["profile"], str(dir(profile_mod))))
Dan Talaycod7c80d12012-04-03 15:20:57 -0700302 except:
303 logging.info("Could not import profile: %s.py" %
304 config["profile"])
Dan Talaycod15bed52012-04-04 10:39:52 -0700305 print "Failed to import profile: %s" % config["profile"]
Rich Laneef403e52012-07-09 14:59:43 -0700306 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700307 else:
308 logging.info("No profile specified")
309
310
Dan Talayco48370102010-03-03 15:17:33 -0800311def logging_setup(config):
312 """
313 Set up logging based on config
314 """
315 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
316 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800317 logging.basicConfig(filename=config["log_file"],
318 level=config["dbg_level"],
319 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800320
321def default_port_map_setup(config):
322 """
323 Setup the OF port mapping based on config
324 @param config The OFT configuration structure
325 @return Port map dictionary
326 """
327 if (config["base_of_port"] is None) or not config["port_count"]:
328 return None
329 port_map = {}
330 if config["platform"] == "local":
331 # For local, use every other veth port
332 for idx in range(config["port_count"]):
333 port_map[config["base_of_port"] + idx] = "veth" + \
334 str(config["base_if_index"] + (2 * idx))
335 elif config["platform"] == "remote":
336 # For remote, use eth ports
337 for idx in range(config["port_count"]):
338 port_map[config["base_of_port"] + idx] = "eth" + \
339 str(config["base_if_index"] + idx)
340 else:
341 return None
342
343 logging.info("Built default port map")
344 return port_map
345
Dan Talayco2c0dba32010-03-06 22:47:06 -0800346def test_list_generate(config):
347 """Generate the list of all known tests indexed by module name
348
349 Conventions: Test files must implement the function test_set_init
350
Dan Talayco1a88c122010-03-07 22:00:20 -0800351 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800352
353 @param config The oft configuration dictionary
354 @returns An array of triples (mod-name, module, [tests]) where
355 mod-name is the string (filename) of the module, module is the
356 value returned from __import__'ing the module and [tests] is an
357 array of strings giving the test cases from the module.
358 """
359
360 # Find and import test files
361 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
362 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
363 stdin=p1.stdout, stdout=PIPE)
364
365 all_tests = {}
366 mod_name_map = {}
367 # There's an extra empty entry at the end of the list
368 filelist = p2.communicate()[0].split("\n")[:-1]
369 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700370 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800371 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700372 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800373
374 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700375 if sys.modules.has_key(modname):
376 mod = sys.modules[modname]
377 else:
378 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800379 except:
380 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700381 raise
Rich Lane520e4152012-07-09 16:18:16 -0700382
383 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
384 issubclass(getattr(mod, k), unittest.TestCase)]
385 if tests:
386 mod_name_map[modname] = mod
387 all_tests[mod] = tests
388
Dan Talayco2c0dba32010-03-06 22:47:06 -0800389 config["all_tests"] = all_tests
390 config["mod_name_map"] = mod_name_map
391
392def die(msg, exit_val=1):
393 print msg
394 logging.critical(msg)
395 sys.exit(exit_val)
396
397def add_test(suite, mod, name):
398 logging.info("Adding test " + mod.__name__ + "." + name)
399 suite.addTest(eval("mod." + name)())
400
Dan Talayco79f36082010-03-11 16:53:53 -0800401def _space_to(n, str):
402 """
403 Generate a string of spaces to achieve width n given string str
404 If length of str >= n, return one space
405 """
406 spaces = n - len(str)
407 if spaces > 0:
408 return " " * spaces
409 return " "
410
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700411def test_prio_get(mod, test):
412 """
413 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700414
415 If test is in "skip list" from profile, return the skip value
416
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700417 If set in the test_prio variable for the module, return
418 that value. Otherwise return 100 (default)
419 """
Dan Talaycod7c80d12012-04-03 15:20:57 -0700420 if profile_mod:
421 if profile_mod.skip_test_list and test in profile_mod.skip_test_list:
422 logging.info("Skipping test %s due to profile" % test)
423 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700424 if 'test_prio' in dir(mod):
425 if test in mod.test_prio.keys():
426 return mod.test_prio[test]
427 return TEST_PRIO_DEFAULT
428
Dan Talayco48370102010-03-03 15:17:33 -0800429#
430# Main script
431#
432
433# Get configuration, set up logging, import platform from file
434(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800435
Dan Talayco2c0dba32010-03-06 22:47:06 -0800436test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700437oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800438
439# Check if test list is requested; display and exit if so
440if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800441 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700442 mod_count = 0
443 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800444 print "\nTest List:"
445 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800446 if config["test_spec"] != "all" and \
447 config["test_spec"] != mod.__name__:
448 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700449 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800450 did_print = True
451 desc = mod.__doc__.strip()
452 desc = desc.split('\n')[0]
453 start_str = " Module " + mod.__name__ + ": "
454 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800455 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700456 try:
457 desc = eval('mod.' + test + '.__doc__.strip()')
458 desc = desc.split('\n')[0]
459 except:
460 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700461 if test_prio_get(mod, test) < 0:
462 start_str = " * " + test + ":"
463 else:
464 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700465 if len(start_str) > 22:
466 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800467 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700468 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800469 print
470 if not did_print:
471 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700472 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700473 print "%d modules shown with a total of %d tests" % \
474 (mod_count, test_count)
475 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700476 print "Tests preceded by * are not run by default"
477 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700478 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700479 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800480 sys.exit(0)
481
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700482# Check if test list is requested; display and exit if so
483if config["list_test_names"]:
484 for mod in config["all_tests"].keys():
485 if config["test_spec"] != "all" and \
486 config["test_spec"] != mod.__name__:
487 continue
488 desc = mod.__doc__.strip()
489 desc = desc.split('\n')[0]
490 for test in config["all_tests"][mod]:
491 print "%s.%s" % (mod.__name__, test)
492 sys.exit(0)
493
Dan Talayco2c0dba32010-03-06 22:47:06 -0800494logging_setup(config)
495logging.info("++++++++ " + time.asctime() + " ++++++++")
496
Dan Talaycod7c80d12012-04-03 15:20:57 -0700497check_profile(config)
498
Dan Talayco2c0dba32010-03-06 22:47:06 -0800499# Generate the test suite
500#@todo Decide if multiple suites are ever needed
501suite = unittest.TestSuite()
502
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700503#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800504if config["test_spec"] == "all":
505 for mod in config["all_tests"].keys():
506 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700507 # For now, a way to avoid tests
508 if test_prio_get(mod, test) >= 0:
509 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800510
511else:
512 for ts_entry in config["test_spec"].split(","):
513 parts = ts_entry.split(".")
514
515 if len(parts) == 1: # Either a module or test name
516 if ts_entry in config["mod_name_map"].keys():
517 mod = config["mod_name_map"][ts_entry]
518 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700519 if test_prio_get(mod, test) >= 0:
520 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800521 else: # Search for matching tests
522 test_found = False
523 for mod in config["all_tests"].keys():
524 if ts_entry in config["all_tests"][mod]:
525 add_test(suite, mod, ts_entry)
526 test_found = True
527 if not test_found:
528 die("Could not find module or test: " + ts_entry)
529
530 elif len(parts) == 2: # module.test
531 if parts[0] not in config["mod_name_map"]:
532 die("Unknown module in test spec: " + ts_entry)
533 mod = config["mod_name_map"][parts[0]]
534 if parts[1] in config["all_tests"][mod]:
535 add_test(suite, mod, parts[1])
536 else:
537 die("No known test matches: " + ts_entry)
538
539 else:
540 die("Bad test spec: " + ts_entry)
541
542# Check if platform specified
Dan Talayco48370102010-03-03 15:17:33 -0800543if config["platform"]:
544 _imp_string = "from " + config["platform"] + " import *"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800545 logging.info("Importing platform: " + _imp_string)
Dan Talayco48370102010-03-03 15:17:33 -0800546 try:
547 exec(_imp_string)
548 except:
549 logging.warn("Failed to import " + config["platform"] + " file")
Rich Laneef403e52012-07-09 14:59:43 -0700550 raise
Dan Talayco48370102010-03-03 15:17:33 -0800551
552try:
553 platform_config_update(config)
554except:
555 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700556 raise
Dan Talayco48370102010-03-03 15:17:33 -0800557
558if not config["port_map"]:
559 # Try to set up default port mapping if not done by platform
560 config["port_map"] = default_port_map_setup(config)
561
562if not config["port_map"]:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800563 die("Interface port map is not defined. Exiting")
Dan Talayco48370102010-03-03 15:17:33 -0800564
565logging.debug("Configuration: " + str(config))
566logging.info("OF port map: " + str(config["port_map"]))
567
568# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800569for (modname,mod) in config["mod_name_map"].items():
570 try:
571 mod.test_set_init(config)
572 except:
573 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700574 raise
Dan Talayco48370102010-03-03 15:17:33 -0800575
Dan Talayco2c0dba32010-03-06 22:47:06 -0800576if config["dbg_level"] == logging.CRITICAL:
577 _verb = 0
578elif config["dbg_level"] >= logging.WARNING:
579 _verb = 1
580else:
581 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800582
Rich Lanee55abf72012-07-26 20:11:42 -0700583oftest.ofutils.default_timeout = config["default_timeout"]
584
Rich Laneee57ad02012-07-13 15:40:36 -0700585if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700586 print "ERROR: Super-user privileges required. Please re-run with " \
587 "sudo or as root."
588 exit(1)
589
Dan Talaycoac25cf32010-07-20 14:08:28 -0700590
591if __name__ == "__main__":
592 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700593 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700594 if testutils.skipped_test_count > 0:
595 ts = " tests"
596 if testutils.skipped_test_count == 1: ts = " test"
597 logging.info("Skipped " + str(testutils.skipped_test_count) + ts)
598 print("Skipped " + str(testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700599 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700600 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700601 # exit(1) hangs sometimes
602 os._exit(1)
Rich Lane9a84a4f2012-07-17 12:27:42 -0700603 if testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700604 os._exit(1)