blob: c3e46c2d787460580aab2df8d07da475ef920853 [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)
Dan Talayco2c0dba32010-03-06 22:47:06 -080097
Dan Talayco1a88c122010-03-07 22:00:20 -080098Current test case setup:
Rich Lane8aebc5e2012-09-25 17:57:53 -070099 Files in the tests direcoty that contain a function test_set_init are
100considered test files.
Dan Talayco2c0dba32010-03-06 22:47:06 -0800101 The function test_set_init examines the test_spec config variable
102and generates a suite of tests.
103 Support a command line option --test_mod so that all tests in that
104module will be run.
105 Support all to specify all tests from the module.
106
Dan Talayco48370102010-03-03 15:17:33 -0800107"""
108
109import sys
110from optparse import OptionParser
Dan Talayco2c0dba32010-03-06 22:47:06 -0800111from subprocess import Popen,PIPE
Dan Talayco48370102010-03-03 15:17:33 -0800112import logging
113import unittest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800114import time
Brandon Heller446c1432010-04-01 12:43:27 -0700115import os
Rich Lane6b452bc2012-07-09 16:52:21 -0700116import imp
Rich Lane8592bec2012-09-03 09:06:59 -0700117import random
Dan Talayco48370102010-03-03 15:17:33 -0800118
Rich Lane23f08d62012-09-25 20:40:38 -0700119root_dir = os.path.dirname(os.path.realpath(__file__))
120pydir = os.path.join(root_dir, 'src', 'python')
Rich Lane39878042012-07-09 14:45:35 -0700121if os.path.exists(os.path.join(pydir, 'oftest')):
122 # Running from source tree
123 sys.path.insert(0, pydir)
124
Rich Lane23f08d62012-09-25 20:40:38 -0700125import oftest.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
154##@var config_default
155# The default configuration dictionary for OFT
156config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700157 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800158 "platform" : "local",
Rich Lane941d1dd2012-07-30 14:27:53 -0700159 "platform_args" : None,
Rich Lane1aeccc42012-07-29 17:58:10 -0700160 "controller_host" : "0.0.0.0",
Dan Talayco48370102010-03-03 15:17:33 -0800161 "controller_port" : 6633,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700162 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800163 "test_spec" : "all",
Dan Talayco48370102010-03-03 15:17:33 -0800164 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800165 "list" : False,
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700166 "list_test_names" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800167 "debug" : _debug_default,
168 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700169 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700170 "test_params" : "None",
Rich Lane1673b8f2012-09-25 18:35:18 -0700171 "profile" : "default",
Rich Laneee57ad02012-07-13 15:40:36 -0700172 "allow_user" : False,
Rich Lane9a84a4f2012-07-17 12:27:42 -0700173 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700174 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000175 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -0700176 "random_seed" : None,
Rich Lane23f08d62012-09-25 20:40:38 -0700177 "test_dir" : os.path.join(root_dir, "tests"),
Rich Lane8aebc5e2012-09-25 17:57:53 -0700178 "platform_dir" : os.path.join(root_dir, "platforms"),
Rich Lane1673b8f2012-09-25 18:35:18 -0700179 "profile_dir" : os.path.join(root_dir, "profiles"),
Dan Talayco48370102010-03-03 15:17:33 -0800180}
181
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700182# Default test priority
183TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700184TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700185
Dan Talayco1a88c122010-03-07 22:00:20 -0800186#@todo Set up a dict of config params so easier to manage:
187# <param> <cmdline flags> <default value> <help> <optional parser>
188
Dan Talayco48370102010-03-03 15:17:33 -0800189# Map options to config structure
190def config_get(opts):
191 "Convert options class to OFT configuration dictionary"
192 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800193 for key in cfg.keys():
194 cfg[key] = eval("opts." + key)
195
196 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800197 if opts.debug not in DEBUG_LEVELS.keys():
198 print "Warning: Bad value specified for debug level; using default"
199 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700200 if opts.verbose:
201 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800202 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800203
Dan Talayco48370102010-03-03 15:17:33 -0800204 return cfg
205
206def config_setup(cfg_dflt):
207 """
208 Set up the configuration including parsing the arguments
209
210 @param cfg_dflt The default configuration dictionary
211 @return A pair (config, args) where config is an config
212 object and args is any additional arguments from the command line
213 """
214
215 parser = OptionParser(version="%prog 0.1")
216
Dan Talayco2c0dba32010-03-06 22:47:06 -0800217 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800218 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800219 for key in cfg_dflt.keys():
220 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
Dan Talayco48370102010-03-03 15:17:33 -0800221
Dan Talayco2c0dba32010-03-06 22:47:06 -0800222 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800223 plat_help = """Set the platform type. Valid values include:
224 local: User space virtual ethernet pair setup
225 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800226 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800227 """
Rich Lane941d1dd2012-07-30 14:27:53 -0700228 parser.add_option("-a", "--platform-args", help="Custom arguments per platform.")
Dan Talayco48370102010-03-03 15:17:33 -0800229 parser.add_option("-P", "--platform", help=plat_help)
230 parser.add_option("-H", "--host", dest="controller_host",
231 help="The IP/name of the test controller host")
232 parser.add_option("-p", "--port", dest="controller_port",
233 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800234 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700235 default) or a comma separated list of:
236 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800237 testcase Run tests in all modules with the name testcase
238 module.testcase Run the specific test case
239 """
Rich Lanef261a102012-07-25 13:41:38 -0700240 parser.add_option("-T", "--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800241 parser.add_option("--log-file",
242 help="Name of log file, empty string to log to console")
243 parser.add_option("--debug",
244 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700245 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800246 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700247 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800248 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700249 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800250 help="Base interface index number (optional)")
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700251 parser.add_option("--list-test-names", action='store_true',
252 help="List only test names.", default=False)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800253 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700254 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700255 parser.add_option("--verbose", action="store_true",
256 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700257 parser.add_option("--relax", action="store_true",
258 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700259 parser.add_option("--param", type="int",
260 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700261 parser.add_option("--profile",
262 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700263 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700264 help="""Set test parameters: key=val;...
265 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
266 See --list""")
Rich Laneee57ad02012-07-13 15:40:36 -0700267 parser.add_option("--allow-user", action="store_true",
268 help="Proceed even if oftest is not run as root")
Rich Lane9a84a4f2012-07-17 12:27:42 -0700269 parser.add_option("--fail-skipped", action="store_true",
270 help="Return failure if any test was skipped")
Rich Lanee55abf72012-07-26 20:11:42 -0700271 parser.add_option("--default-timeout", type="int",
272 help="Timeout in seconds for most operations")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000273 parser.add_option("--minsize", type="int",
274 help="Minimum allowable packet size on the dataplane.",
275 default=0)
Rich Lane8592bec2012-09-03 09:06:59 -0700276 parser.add_option("--random-seed", type="int",
277 help="Random number generator seed",
278 default=None)
Rich Lanebc3d2962012-09-25 09:34:17 -0700279 parser.add_option("--test-dir", type="string",
280 help="Directory containing tests")
Rich Lane8aebc5e2012-09-25 17:57:53 -0700281 parser.add_option("--platform-dir", type="string",
282 help="Directory containing platform modules")
Rich Lane1673b8f2012-09-25 18:35:18 -0700283 parser.add_option("--profile-dir", type="string",
284 help="Directory containing profile modules")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000285
Dan Talayco48370102010-03-03 15:17:33 -0800286 # Might need this if other parsers want command line
287 # parser.allow_interspersed_args = False
288 (options, args) = parser.parse_args()
289
290 config = config_get(options)
291
292 return (config, args)
293
Rich Lane1673b8f2012-09-25 18:35:18 -0700294def load_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700295 """
296 Import a profile from the profiles library
297 """
298
Dan Talaycod7c80d12012-04-03 15:20:57 -0700299 global profile_mod
Rich Lane1673b8f2012-09-25 18:35:18 -0700300 logging.info("Importing profile: %s" % config["profile"])
301 try:
302 profile_mod = imp.load_module(config["profile"], *imp.find_module(config["profile"], [config["profile_dir"]]))
303 if not "skip_test_list" in dir(profile_mod):
304 die("Profile did not define skip_test_list")
305 except:
306 logging.info("Could not import profile: %s.py" % config["profile"])
307 print "Failed to import profile: %s" % config["profile"]
308 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700309
Dan Talayco48370102010-03-03 15:17:33 -0800310def logging_setup(config):
311 """
312 Set up logging based on config
313 """
314 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
315 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800316 logging.basicConfig(filename=config["log_file"],
317 level=config["dbg_level"],
318 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800319
Dan Talayco2c0dba32010-03-06 22:47:06 -0800320def test_list_generate(config):
321 """Generate the list of all known tests indexed by module name
322
323 Conventions: Test files must implement the function test_set_init
324
Dan Talayco1a88c122010-03-07 22:00:20 -0800325 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800326
327 @param config The oft configuration dictionary
328 @returns An array of triples (mod-name, module, [tests]) where
329 mod-name is the string (filename) of the module, module is the
330 value returned from __import__'ing the module and [tests] is an
331 array of strings giving the test cases from the module.
332 """
333
334 # Find and import test files
335 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
336 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
337 stdin=p1.stdout, stdout=PIPE)
338
339 all_tests = {}
340 mod_name_map = {}
341 # There's an extra empty entry at the end of the list
342 filelist = p2.communicate()[0].split("\n")[:-1]
343 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700344 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800345 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700346 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800347
348 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700349 if sys.modules.has_key(modname):
350 mod = sys.modules[modname]
351 else:
352 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800353 except:
354 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700355 raise
Rich Lane520e4152012-07-09 16:18:16 -0700356
357 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
358 issubclass(getattr(mod, k), unittest.TestCase)]
359 if tests:
360 mod_name_map[modname] = mod
361 all_tests[mod] = tests
362
Dan Talayco2c0dba32010-03-06 22:47:06 -0800363 config["all_tests"] = all_tests
364 config["mod_name_map"] = mod_name_map
365
366def die(msg, exit_val=1):
367 print msg
368 logging.critical(msg)
369 sys.exit(exit_val)
370
371def add_test(suite, mod, name):
372 logging.info("Adding test " + mod.__name__ + "." + name)
373 suite.addTest(eval("mod." + name)())
374
Dan Talayco79f36082010-03-11 16:53:53 -0800375def _space_to(n, str):
376 """
377 Generate a string of spaces to achieve width n given string str
378 If length of str >= n, return one space
379 """
380 spaces = n - len(str)
381 if spaces > 0:
382 return " " * spaces
383 return " "
384
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700385def test_prio_get(mod, test):
386 """
387 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700388
389 If test is in "skip list" from profile, return the skip value
390
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700391 If set in the test_prio variable for the module, return
392 that value. Otherwise return 100 (default)
393 """
Rich Lane1673b8f2012-09-25 18:35:18 -0700394 if test in profile_mod.skip_test_list:
395 logging.info("Skipping test %s due to profile" % test)
396 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700397 if 'test_prio' in dir(mod):
398 if test in mod.test_prio.keys():
399 return mod.test_prio[test]
400 return TEST_PRIO_DEFAULT
401
Dan Talayco48370102010-03-03 15:17:33 -0800402#
403# Main script
404#
405
406# Get configuration, set up logging, import platform from file
407(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800408
Rich Lane23f08d62012-09-25 20:40:38 -0700409# Allow modules to import each other.
410sys.path.insert(0, config["test_dir"])
411
Dan Talayco2c0dba32010-03-06 22:47:06 -0800412test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700413oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800414
415# Check if test list is requested; display and exit if so
416if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800417 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700418 mod_count = 0
419 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800420 print "\nTest List:"
421 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800422 if config["test_spec"] != "all" and \
423 config["test_spec"] != mod.__name__:
424 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700425 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800426 did_print = True
427 desc = mod.__doc__.strip()
428 desc = desc.split('\n')[0]
429 start_str = " Module " + mod.__name__ + ": "
430 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800431 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700432 try:
433 desc = eval('mod.' + test + '.__doc__.strip()')
434 desc = desc.split('\n')[0]
435 except:
436 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700437 if test_prio_get(mod, test) < 0:
438 start_str = " * " + test + ":"
439 else:
440 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700441 if len(start_str) > 22:
442 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800443 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700444 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800445 print
446 if not did_print:
447 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700448 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700449 print "%d modules shown with a total of %d tests" % \
450 (mod_count, test_count)
451 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700452 print "Tests preceded by * are not run by default"
453 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700454 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700455 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800456 sys.exit(0)
457
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700458# Check if test list is requested; display and exit if so
459if config["list_test_names"]:
460 for mod in config["all_tests"].keys():
461 if config["test_spec"] != "all" and \
462 config["test_spec"] != mod.__name__:
463 continue
464 desc = mod.__doc__.strip()
465 desc = desc.split('\n')[0]
466 for test in config["all_tests"][mod]:
467 print "%s.%s" % (mod.__name__, test)
468 sys.exit(0)
469
Dan Talayco2c0dba32010-03-06 22:47:06 -0800470logging_setup(config)
471logging.info("++++++++ " + time.asctime() + " ++++++++")
472
Rich Lane1673b8f2012-09-25 18:35:18 -0700473load_profile(config)
Dan Talaycod7c80d12012-04-03 15:20:57 -0700474
Dan Talayco2c0dba32010-03-06 22:47:06 -0800475# Generate the test suite
476#@todo Decide if multiple suites are ever needed
477suite = unittest.TestSuite()
478
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700479#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800480if config["test_spec"] == "all":
481 for mod in config["all_tests"].keys():
482 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700483 # For now, a way to avoid tests
484 if test_prio_get(mod, test) >= 0:
485 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800486
487else:
488 for ts_entry in config["test_spec"].split(","):
489 parts = ts_entry.split(".")
490
491 if len(parts) == 1: # Either a module or test name
492 if ts_entry in config["mod_name_map"].keys():
493 mod = config["mod_name_map"][ts_entry]
494 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700495 if test_prio_get(mod, test) >= 0:
496 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800497 else: # Search for matching tests
498 test_found = False
499 for mod in config["all_tests"].keys():
500 if ts_entry in config["all_tests"][mod]:
501 add_test(suite, mod, ts_entry)
502 test_found = True
503 if not test_found:
504 die("Could not find module or test: " + ts_entry)
505
506 elif len(parts) == 2: # module.test
507 if parts[0] not in config["mod_name_map"]:
508 die("Unknown module in test spec: " + ts_entry)
509 mod = config["mod_name_map"][parts[0]]
510 if parts[1] in config["all_tests"][mod]:
511 add_test(suite, mod, parts[1])
512 else:
513 die("No known test matches: " + ts_entry)
514
515 else:
516 die("Bad test spec: " + ts_entry)
517
Rich Lane8aebc5e2012-09-25 17:57:53 -0700518# Load the platform module
519platform_name = config["platform"]
520logging.info("Importing platform: " + platform_name)
521platform_mod = None
522try:
523 platform_mod = imp.load_module(platform_name, *imp.find_module(platform_name, [config["platform_dir"], config["test_dir"]]))
524except:
525 logging.warn("Failed to import " + platform_name + " platform module")
526 raise
Dan Talayco48370102010-03-03 15:17:33 -0800527
528try:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700529 platform_mod.platform_config_update(config)
Dan Talayco48370102010-03-03 15:17:33 -0800530except:
531 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700532 raise
Dan Talayco48370102010-03-03 15:17:33 -0800533
534if not config["port_map"]:
Rich Lane8aebc5e2012-09-25 17:57:53 -0700535 die("Interface port map was not defined by the platform. Exiting.")
Dan Talayco48370102010-03-03 15:17:33 -0800536
537logging.debug("Configuration: " + str(config))
538logging.info("OF port map: " + str(config["port_map"]))
539
540# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800541for (modname,mod) in config["mod_name_map"].items():
542 try:
543 mod.test_set_init(config)
544 except:
545 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700546 raise
Dan Talayco48370102010-03-03 15:17:33 -0800547
Dan Talayco2c0dba32010-03-06 22:47:06 -0800548if config["dbg_level"] == logging.CRITICAL:
549 _verb = 0
550elif config["dbg_level"] >= logging.WARNING:
551 _verb = 1
552else:
553 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800554
Rich Lanee55abf72012-07-26 20:11:42 -0700555oftest.ofutils.default_timeout = config["default_timeout"]
Rich Lane23f08d62012-09-25 20:40:38 -0700556oftest.testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700557
Rich Laneee57ad02012-07-13 15:40:36 -0700558if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700559 print "ERROR: Super-user privileges required. Please re-run with " \
560 "sudo or as root."
561 exit(1)
562
Rich Lane8592bec2012-09-03 09:06:59 -0700563if config["random_seed"] is not None:
564 logging.info("Random seed: %d" % config["random_seed"])
565 random.seed(config["random_seed"])
566
Dan Talaycoac25cf32010-07-20 14:08:28 -0700567
568if __name__ == "__main__":
569 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700570 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Rich Lane23f08d62012-09-25 20:40:38 -0700571 skipped_test_count = oftest.testutils.skipped_test_count
572 if skipped_test_count > 0:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700573 ts = " tests"
Rich Lane23f08d62012-09-25 20:40:38 -0700574 if skipped_test_count == 1: ts = " test"
575 logging.info("Skipped " + str(skipped_test_count) + ts)
576 print("Skipped " + str(skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700577 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700578 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700579 # exit(1) hangs sometimes
580 os._exit(1)
Rich Lane23f08d62012-09-25 20:40:38 -0700581 if skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700582 os._exit(1)