blob: a70b9c6d0397fa2de4b068b160957f9cd120762e [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
Rich Lane8592bec2012-09-03 09:06:59 -0700127import random
Dan Talayco48370102010-03-03 15:17:33 -0800128
Rich Lane39878042012-07-09 14:45:35 -0700129pydir = os.path.join(os.path.dirname(__file__), '..', 'src', 'python')
130if os.path.exists(os.path.join(pydir, 'oftest')):
131 # Running from source tree
132 sys.path.insert(0, pydir)
133
Dan Talaycoba3745c2010-07-21 21:51:08 -0700134import testutils
Rich Lanee55abf72012-07-26 20:11:42 -0700135import oftest.ofutils
Dan Talaycoba3745c2010-07-21 21:51:08 -0700136
Dan Talayco02eca0b2010-04-15 16:09:43 -0700137try:
138 import scapy.all as scapy
139except:
140 try:
141 import scapy as scapy
142 except:
143 sys.exit("Need to install scapy for packet parsing")
144
Dan Talaycod7c80d12012-04-03 15:20:57 -0700145##@var Profile module
146profile_mod = None
147
Dan Talayco48370102010-03-03 15:17:33 -0800148##@var DEBUG_LEVELS
149# Map from strings to debugging levels
150DEBUG_LEVELS = {
151 'debug' : logging.DEBUG,
152 'verbose' : logging.DEBUG,
153 'info' : logging.INFO,
154 'warning' : logging.WARNING,
155 'warn' : logging.WARNING,
156 'error' : logging.ERROR,
157 'critical' : logging.CRITICAL
158}
159
160_debug_default = "warning"
161_debug_level_default = DEBUG_LEVELS[_debug_default]
162
163##@var config_default
164# The default configuration dictionary for OFT
165config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700166 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800167 "platform" : "local",
Rich Lane941d1dd2012-07-30 14:27:53 -0700168 "platform_args" : None,
Rich Lane1aeccc42012-07-29 17:58:10 -0700169 "controller_host" : "0.0.0.0",
Dan Talayco48370102010-03-03 15:17:33 -0800170 "controller_port" : 6633,
171 "port_count" : 4,
172 "base_of_port" : 1,
173 "base_if_index" : 1,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700174 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800175 "test_spec" : "all",
Rich Lane9d7330a2012-07-10 14:37:44 -0700176 "test_dir" : os.path.dirname(__file__),
Dan Talayco48370102010-03-03 15:17:33 -0800177 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800178 "list" : False,
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700179 "list_test_names" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800180 "debug" : _debug_default,
181 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700182 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700183 "test_params" : "None",
Rich Laneee57ad02012-07-13 15:40:36 -0700184 "profile" : None,
185 "allow_user" : False,
Rich Lane9a84a4f2012-07-17 12:27:42 -0700186 "fail_skipped" : False,
Rich Lanee55abf72012-07-26 20:11:42 -0700187 "default_timeout" : 2,
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000188 "minsize" : 0,
Rich Lane8592bec2012-09-03 09:06:59 -0700189 "random_seed" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800190}
191
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700192# Default test priority
193TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700194TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700195
Dan Talayco1a88c122010-03-07 22:00:20 -0800196#@todo Set up a dict of config params so easier to manage:
197# <param> <cmdline flags> <default value> <help> <optional parser>
198
Dan Talayco48370102010-03-03 15:17:33 -0800199# Map options to config structure
200def config_get(opts):
201 "Convert options class to OFT configuration dictionary"
202 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800203 for key in cfg.keys():
204 cfg[key] = eval("opts." + key)
205
206 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800207 if opts.debug not in DEBUG_LEVELS.keys():
208 print "Warning: Bad value specified for debug level; using default"
209 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700210 if opts.verbose:
211 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800212 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800213
Dan Talayco48370102010-03-03 15:17:33 -0800214 return cfg
215
216def config_setup(cfg_dflt):
217 """
218 Set up the configuration including parsing the arguments
219
220 @param cfg_dflt The default configuration dictionary
221 @return A pair (config, args) where config is an config
222 object and args is any additional arguments from the command line
223 """
224
225 parser = OptionParser(version="%prog 0.1")
226
Dan Talayco2c0dba32010-03-06 22:47:06 -0800227 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800228 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800229 for key in cfg_dflt.keys():
230 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
Dan Talayco48370102010-03-03 15:17:33 -0800231
Dan Talayco2c0dba32010-03-06 22:47:06 -0800232 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800233 plat_help = """Set the platform type. Valid values include:
234 local: User space virtual ethernet pair setup
235 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800236 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800237 """
Rich Lane941d1dd2012-07-30 14:27:53 -0700238 parser.add_option("-a", "--platform-args", help="Custom arguments per platform.")
Dan Talayco48370102010-03-03 15:17:33 -0800239 parser.add_option("-P", "--platform", help=plat_help)
240 parser.add_option("-H", "--host", dest="controller_host",
241 help="The IP/name of the test controller host")
242 parser.add_option("-p", "--port", dest="controller_port",
243 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800244 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700245 default) or a comma separated list of:
246 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800247 testcase Run tests in all modules with the name testcase
248 module.testcase Run the specific test case
249 """
Rich Lanef261a102012-07-25 13:41:38 -0700250 parser.add_option("-T", "--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800251 parser.add_option("--log-file",
252 help="Name of log file, empty string to log to console")
253 parser.add_option("--debug",
254 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700255 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800256 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700257 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800258 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700259 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800260 help="Base interface index number (optional)")
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700261 parser.add_option("--list-test-names", action='store_true',
262 help="List only test names.", default=False)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800263 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700264 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700265 parser.add_option("--verbose", action="store_true",
266 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700267 parser.add_option("--relax", action="store_true",
268 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700269 parser.add_option("--param", type="int",
270 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700271 parser.add_option("--profile",
272 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700273 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700274 help="""Set test parameters: key=val;...
275 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
276 See --list""")
Rich Laneee57ad02012-07-13 15:40:36 -0700277 parser.add_option("--allow-user", action="store_true",
278 help="Proceed even if oftest is not run as root")
Rich Lane9a84a4f2012-07-17 12:27:42 -0700279 parser.add_option("--fail-skipped", action="store_true",
280 help="Return failure if any test was skipped")
Rich Lanee55abf72012-07-26 20:11:42 -0700281 parser.add_option("--default-timeout", type="int",
282 help="Timeout in seconds for most operations")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000283 parser.add_option("--minsize", type="int",
284 help="Minimum allowable packet size on the dataplane.",
285 default=0)
Rich Lane8592bec2012-09-03 09:06:59 -0700286 parser.add_option("--random-seed", type="int",
287 help="Random number generator seed",
288 default=None)
Rich Lanebc3d2962012-09-25 09:34:17 -0700289 parser.add_option("--test-dir", type="string",
290 help="Directory containing tests")
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000291
Dan Talayco48370102010-03-03 15:17:33 -0800292 # Might need this if other parsers want command line
293 # parser.allow_interspersed_args = False
294 (options, args) = parser.parse_args()
295
296 config = config_get(options)
297
298 return (config, args)
299
Dan Talaycod7c80d12012-04-03 15:20:57 -0700300def check_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700301 """
302 Import a profile from the profiles library
303 """
304
Dan Talaycod7c80d12012-04-03 15:20:57 -0700305 global profile_mod
306 if "profile" in config and config["profile"]:
Dan Talaycod15bed52012-04-04 10:39:52 -0700307 logging.info("Importing profile: %s" % config["profile"])
308 profile_name = "profiles." + config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700309 try:
Dan Talaycod15bed52012-04-04 10:39:52 -0700310 top_mod = __import__(profile_name)
311 profile_mod = eval("top_mod." + config["profile"])
312 logging.info("Imported profile %s. Dir: %s" %
313 (config["profile"], str(dir(profile_mod))))
Dan Talaycod7c80d12012-04-03 15:20:57 -0700314 except:
315 logging.info("Could not import profile: %s.py" %
316 config["profile"])
Dan Talaycod15bed52012-04-04 10:39:52 -0700317 print "Failed to import profile: %s" % config["profile"]
Rich Laneef403e52012-07-09 14:59:43 -0700318 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700319 else:
320 logging.info("No profile specified")
321
322
Dan Talayco48370102010-03-03 15:17:33 -0800323def logging_setup(config):
324 """
325 Set up logging based on config
326 """
327 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
328 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800329 logging.basicConfig(filename=config["log_file"],
330 level=config["dbg_level"],
331 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800332
333def default_port_map_setup(config):
334 """
335 Setup the OF port mapping based on config
336 @param config The OFT configuration structure
337 @return Port map dictionary
338 """
339 if (config["base_of_port"] is None) or not config["port_count"]:
340 return None
341 port_map = {}
342 if config["platform"] == "local":
343 # For local, use every other veth port
344 for idx in range(config["port_count"]):
345 port_map[config["base_of_port"] + idx] = "veth" + \
346 str(config["base_if_index"] + (2 * idx))
347 elif config["platform"] == "remote":
348 # For remote, use eth ports
349 for idx in range(config["port_count"]):
350 port_map[config["base_of_port"] + idx] = "eth" + \
351 str(config["base_if_index"] + idx)
352 else:
353 return None
354
355 logging.info("Built default port map")
356 return port_map
357
Dan Talayco2c0dba32010-03-06 22:47:06 -0800358def test_list_generate(config):
359 """Generate the list of all known tests indexed by module name
360
361 Conventions: Test files must implement the function test_set_init
362
Dan Talayco1a88c122010-03-07 22:00:20 -0800363 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800364
365 @param config The oft configuration dictionary
366 @returns An array of triples (mod-name, module, [tests]) where
367 mod-name is the string (filename) of the module, module is the
368 value returned from __import__'ing the module and [tests] is an
369 array of strings giving the test cases from the module.
370 """
371
372 # Find and import test files
373 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
374 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
375 stdin=p1.stdout, stdout=PIPE)
376
377 all_tests = {}
378 mod_name_map = {}
379 # There's an extra empty entry at the end of the list
380 filelist = p2.communicate()[0].split("\n")[:-1]
381 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700382 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800383 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700384 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800385
386 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700387 if sys.modules.has_key(modname):
388 mod = sys.modules[modname]
389 else:
390 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800391 except:
392 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700393 raise
Rich Lane520e4152012-07-09 16:18:16 -0700394
395 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
396 issubclass(getattr(mod, k), unittest.TestCase)]
397 if tests:
398 mod_name_map[modname] = mod
399 all_tests[mod] = tests
400
Dan Talayco2c0dba32010-03-06 22:47:06 -0800401 config["all_tests"] = all_tests
402 config["mod_name_map"] = mod_name_map
403
404def die(msg, exit_val=1):
405 print msg
406 logging.critical(msg)
407 sys.exit(exit_val)
408
409def add_test(suite, mod, name):
410 logging.info("Adding test " + mod.__name__ + "." + name)
411 suite.addTest(eval("mod." + name)())
412
Dan Talayco79f36082010-03-11 16:53:53 -0800413def _space_to(n, str):
414 """
415 Generate a string of spaces to achieve width n given string str
416 If length of str >= n, return one space
417 """
418 spaces = n - len(str)
419 if spaces > 0:
420 return " " * spaces
421 return " "
422
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700423def test_prio_get(mod, test):
424 """
425 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700426
427 If test is in "skip list" from profile, return the skip value
428
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700429 If set in the test_prio variable for the module, return
430 that value. Otherwise return 100 (default)
431 """
Dan Talaycod7c80d12012-04-03 15:20:57 -0700432 if profile_mod:
433 if profile_mod.skip_test_list and test in profile_mod.skip_test_list:
434 logging.info("Skipping test %s due to profile" % test)
435 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700436 if 'test_prio' in dir(mod):
437 if test in mod.test_prio.keys():
438 return mod.test_prio[test]
439 return TEST_PRIO_DEFAULT
440
Dan Talayco48370102010-03-03 15:17:33 -0800441#
442# Main script
443#
444
445# Get configuration, set up logging, import platform from file
446(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800447
Dan Talayco2c0dba32010-03-06 22:47:06 -0800448test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700449oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800450
451# Check if test list is requested; display and exit if so
452if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800453 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700454 mod_count = 0
455 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800456 print "\nTest List:"
457 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800458 if config["test_spec"] != "all" and \
459 config["test_spec"] != mod.__name__:
460 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700461 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800462 did_print = True
463 desc = mod.__doc__.strip()
464 desc = desc.split('\n')[0]
465 start_str = " Module " + mod.__name__ + ": "
466 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800467 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700468 try:
469 desc = eval('mod.' + test + '.__doc__.strip()')
470 desc = desc.split('\n')[0]
471 except:
472 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700473 if test_prio_get(mod, test) < 0:
474 start_str = " * " + test + ":"
475 else:
476 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700477 if len(start_str) > 22:
478 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800479 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700480 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800481 print
482 if not did_print:
483 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700484 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700485 print "%d modules shown with a total of %d tests" % \
486 (mod_count, test_count)
487 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700488 print "Tests preceded by * are not run by default"
489 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700490 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700491 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800492 sys.exit(0)
493
Jeffrey Townsend6614f972012-08-16 13:27:18 -0700494# Check if test list is requested; display and exit if so
495if config["list_test_names"]:
496 for mod in config["all_tests"].keys():
497 if config["test_spec"] != "all" and \
498 config["test_spec"] != mod.__name__:
499 continue
500 desc = mod.__doc__.strip()
501 desc = desc.split('\n')[0]
502 for test in config["all_tests"][mod]:
503 print "%s.%s" % (mod.__name__, test)
504 sys.exit(0)
505
Dan Talayco2c0dba32010-03-06 22:47:06 -0800506logging_setup(config)
507logging.info("++++++++ " + time.asctime() + " ++++++++")
508
Dan Talaycod7c80d12012-04-03 15:20:57 -0700509check_profile(config)
510
Dan Talayco2c0dba32010-03-06 22:47:06 -0800511# Generate the test suite
512#@todo Decide if multiple suites are ever needed
513suite = unittest.TestSuite()
514
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700515#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800516if config["test_spec"] == "all":
517 for mod in config["all_tests"].keys():
518 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700519 # For now, a way to avoid tests
520 if test_prio_get(mod, test) >= 0:
521 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800522
523else:
524 for ts_entry in config["test_spec"].split(","):
525 parts = ts_entry.split(".")
526
527 if len(parts) == 1: # Either a module or test name
528 if ts_entry in config["mod_name_map"].keys():
529 mod = config["mod_name_map"][ts_entry]
530 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700531 if test_prio_get(mod, test) >= 0:
532 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800533 else: # Search for matching tests
534 test_found = False
535 for mod in config["all_tests"].keys():
536 if ts_entry in config["all_tests"][mod]:
537 add_test(suite, mod, ts_entry)
538 test_found = True
539 if not test_found:
540 die("Could not find module or test: " + ts_entry)
541
542 elif len(parts) == 2: # module.test
543 if parts[0] not in config["mod_name_map"]:
544 die("Unknown module in test spec: " + ts_entry)
545 mod = config["mod_name_map"][parts[0]]
546 if parts[1] in config["all_tests"][mod]:
547 add_test(suite, mod, parts[1])
548 else:
549 die("No known test matches: " + ts_entry)
550
551 else:
552 die("Bad test spec: " + ts_entry)
553
554# Check if platform specified
Dan Talayco48370102010-03-03 15:17:33 -0800555if config["platform"]:
556 _imp_string = "from " + config["platform"] + " import *"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800557 logging.info("Importing platform: " + _imp_string)
Dan Talayco48370102010-03-03 15:17:33 -0800558 try:
559 exec(_imp_string)
560 except:
561 logging.warn("Failed to import " + config["platform"] + " file")
Rich Laneef403e52012-07-09 14:59:43 -0700562 raise
Dan Talayco48370102010-03-03 15:17:33 -0800563
564try:
565 platform_config_update(config)
566except:
567 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700568 raise
Dan Talayco48370102010-03-03 15:17:33 -0800569
570if not config["port_map"]:
571 # Try to set up default port mapping if not done by platform
572 config["port_map"] = default_port_map_setup(config)
573
574if not config["port_map"]:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800575 die("Interface port map is not defined. Exiting")
Dan Talayco48370102010-03-03 15:17:33 -0800576
577logging.debug("Configuration: " + str(config))
578logging.info("OF port map: " + str(config["port_map"]))
579
580# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800581for (modname,mod) in config["mod_name_map"].items():
582 try:
583 mod.test_set_init(config)
584 except:
585 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700586 raise
Dan Talayco48370102010-03-03 15:17:33 -0800587
Dan Talayco2c0dba32010-03-06 22:47:06 -0800588if config["dbg_level"] == logging.CRITICAL:
589 _verb = 0
590elif config["dbg_level"] >= logging.WARNING:
591 _verb = 1
592else:
593 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800594
Rich Lanee55abf72012-07-26 20:11:42 -0700595oftest.ofutils.default_timeout = config["default_timeout"]
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000596testutils.MINSIZE = config['minsize']
Rich Lanee55abf72012-07-26 20:11:42 -0700597
Rich Laneee57ad02012-07-13 15:40:36 -0700598if os.getuid() != 0 and not config["allow_user"]:
Brandon Heller446c1432010-04-01 12:43:27 -0700599 print "ERROR: Super-user privileges required. Please re-run with " \
600 "sudo or as root."
601 exit(1)
602
Rich Lane8592bec2012-09-03 09:06:59 -0700603if config["random_seed"] is not None:
604 logging.info("Random seed: %d" % config["random_seed"])
605 random.seed(config["random_seed"])
606
Dan Talaycoac25cf32010-07-20 14:08:28 -0700607
608if __name__ == "__main__":
609 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700610 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700611 if testutils.skipped_test_count > 0:
612 ts = " tests"
613 if testutils.skipped_test_count == 1: ts = " test"
614 logging.info("Skipped " + str(testutils.skipped_test_count) + ts)
615 print("Skipped " + str(testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700616 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane50d42eb2012-07-16 11:57:03 -0700617 if result.failures or result.errors:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700618 # exit(1) hangs sometimes
619 os._exit(1)
Rich Lane9a84a4f2012-07-17 12:27:42 -0700620 if testutils.skipped_test_count > 0 and config["fail_skipped"]:
Shudong Zhoud895fcb2012-08-01 19:09:55 -0700621 os._exit(1)