blob: aa99b5cfecea78c77697bfdde87148be2151c06d [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
134
Dan Talayco02eca0b2010-04-15 16:09:43 -0700135try:
136 import scapy.all as scapy
137except:
138 try:
139 import scapy as scapy
140 except:
141 sys.exit("Need to install scapy for packet parsing")
142
Dan Talaycod7c80d12012-04-03 15:20:57 -0700143##@var Profile module
144profile_mod = None
145
Dan Talayco48370102010-03-03 15:17:33 -0800146##@var DEBUG_LEVELS
147# Map from strings to debugging levels
148DEBUG_LEVELS = {
149 'debug' : logging.DEBUG,
150 'verbose' : logging.DEBUG,
151 'info' : logging.INFO,
152 'warning' : logging.WARNING,
153 'warn' : logging.WARNING,
154 'error' : logging.ERROR,
155 'critical' : logging.CRITICAL
156}
157
158_debug_default = "warning"
159_debug_level_default = DEBUG_LEVELS[_debug_default]
160
161##@var config_default
162# The default configuration dictionary for OFT
163config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700164 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800165 "platform" : "local",
166 "controller_host" : "127.0.0.1",
167 "controller_port" : 6633,
168 "port_count" : 4,
169 "base_of_port" : 1,
170 "base_if_index" : 1,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700171 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800172 "test_spec" : "all",
Rich Lane9d7330a2012-07-10 14:37:44 -0700173 "test_dir" : os.path.dirname(__file__),
Dan Talayco48370102010-03-03 15:17:33 -0800174 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800175 "list" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800176 "debug" : _debug_default,
177 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700178 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700179 "test_params" : "None",
180 "profile" : None
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 """
229 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 """
240 parser.add_option("--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)")
251 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700252 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700253 parser.add_option("--verbose", action="store_true",
254 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700255 parser.add_option("--relax", action="store_true",
256 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700257 parser.add_option("--param", type="int",
258 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700259 parser.add_option("--profile",
260 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700261 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700262 help="""Set test parameters: key=val;...
263 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
264 See --list""")
Dan Talayco48370102010-03-03 15:17:33 -0800265 # Might need this if other parsers want command line
266 # parser.allow_interspersed_args = False
267 (options, args) = parser.parse_args()
268
269 config = config_get(options)
270
271 return (config, args)
272
Dan Talaycod7c80d12012-04-03 15:20:57 -0700273def check_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700274 """
275 Import a profile from the profiles library
276 """
277
Dan Talaycod7c80d12012-04-03 15:20:57 -0700278 global profile_mod
279 if "profile" in config and config["profile"]:
Dan Talaycod15bed52012-04-04 10:39:52 -0700280 logging.info("Importing profile: %s" % config["profile"])
281 profile_name = "profiles." + config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700282 try:
Dan Talaycod15bed52012-04-04 10:39:52 -0700283 top_mod = __import__(profile_name)
284 profile_mod = eval("top_mod." + config["profile"])
285 logging.info("Imported profile %s. Dir: %s" %
286 (config["profile"], str(dir(profile_mod))))
Dan Talaycod7c80d12012-04-03 15:20:57 -0700287 except:
288 logging.info("Could not import profile: %s.py" %
289 config["profile"])
Dan Talaycod15bed52012-04-04 10:39:52 -0700290 print "Failed to import profile: %s" % config["profile"]
Rich Laneef403e52012-07-09 14:59:43 -0700291 raise
Dan Talaycod7c80d12012-04-03 15:20:57 -0700292 else:
293 logging.info("No profile specified")
294
295
Dan Talayco48370102010-03-03 15:17:33 -0800296def logging_setup(config):
297 """
298 Set up logging based on config
299 """
300 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
301 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800302 logging.basicConfig(filename=config["log_file"],
303 level=config["dbg_level"],
304 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800305
306def default_port_map_setup(config):
307 """
308 Setup the OF port mapping based on config
309 @param config The OFT configuration structure
310 @return Port map dictionary
311 """
312 if (config["base_of_port"] is None) or not config["port_count"]:
313 return None
314 port_map = {}
315 if config["platform"] == "local":
316 # For local, use every other veth port
317 for idx in range(config["port_count"]):
318 port_map[config["base_of_port"] + idx] = "veth" + \
319 str(config["base_if_index"] + (2 * idx))
320 elif config["platform"] == "remote":
321 # For remote, use eth ports
322 for idx in range(config["port_count"]):
323 port_map[config["base_of_port"] + idx] = "eth" + \
324 str(config["base_if_index"] + idx)
325 else:
326 return None
327
328 logging.info("Built default port map")
329 return port_map
330
Dan Talayco2c0dba32010-03-06 22:47:06 -0800331def test_list_generate(config):
332 """Generate the list of all known tests indexed by module name
333
334 Conventions: Test files must implement the function test_set_init
335
Dan Talayco1a88c122010-03-07 22:00:20 -0800336 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800337
338 @param config The oft configuration dictionary
339 @returns An array of triples (mod-name, module, [tests]) where
340 mod-name is the string (filename) of the module, module is the
341 value returned from __import__'ing the module and [tests] is an
342 array of strings giving the test cases from the module.
343 """
344
345 # Find and import test files
346 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
347 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
348 stdin=p1.stdout, stdout=PIPE)
349
350 all_tests = {}
351 mod_name_map = {}
352 # There's an extra empty entry at the end of the list
353 filelist = p2.communicate()[0].split("\n")[:-1]
354 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700355 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800356 continue
Rich Lane1fac1542012-07-09 16:10:45 -0700357 modname = os.path.splitext(os.path.basename(file))[0]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800358
359 try:
Rich Lane6b452bc2012-07-09 16:52:21 -0700360 if sys.modules.has_key(modname):
361 mod = sys.modules[modname]
362 else:
363 mod = imp.load_module(modname, *imp.find_module(modname, [os.path.dirname(file)]))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800364 except:
365 logging.warning("Could not import file " + file)
Rich Laneef403e52012-07-09 14:59:43 -0700366 raise
Rich Lane520e4152012-07-09 16:18:16 -0700367
368 tests = [k for k in dir(mod) if type(getattr(mod, k)) == type and
369 issubclass(getattr(mod, k), unittest.TestCase)]
370 if tests:
371 mod_name_map[modname] = mod
372 all_tests[mod] = tests
373
Dan Talayco2c0dba32010-03-06 22:47:06 -0800374 config["all_tests"] = all_tests
375 config["mod_name_map"] = mod_name_map
376
377def die(msg, exit_val=1):
378 print msg
379 logging.critical(msg)
380 sys.exit(exit_val)
381
382def add_test(suite, mod, name):
383 logging.info("Adding test " + mod.__name__ + "." + name)
384 suite.addTest(eval("mod." + name)())
385
Dan Talayco79f36082010-03-11 16:53:53 -0800386def _space_to(n, str):
387 """
388 Generate a string of spaces to achieve width n given string str
389 If length of str >= n, return one space
390 """
391 spaces = n - len(str)
392 if spaces > 0:
393 return " " * spaces
394 return " "
395
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700396def test_prio_get(mod, test):
397 """
398 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700399
400 If test is in "skip list" from profile, return the skip value
401
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700402 If set in the test_prio variable for the module, return
403 that value. Otherwise return 100 (default)
404 """
Dan Talaycod7c80d12012-04-03 15:20:57 -0700405 if profile_mod:
406 if profile_mod.skip_test_list and test in profile_mod.skip_test_list:
407 logging.info("Skipping test %s due to profile" % test)
408 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700409 if 'test_prio' in dir(mod):
410 if test in mod.test_prio.keys():
411 return mod.test_prio[test]
412 return TEST_PRIO_DEFAULT
413
Dan Talayco48370102010-03-03 15:17:33 -0800414#
415# Main script
416#
417
418# Get configuration, set up logging, import platform from file
419(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800420
Dan Talayco2c0dba32010-03-06 22:47:06 -0800421test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700422oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800423
424# Check if test list is requested; display and exit if so
425if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800426 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700427 mod_count = 0
428 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800429 print "\nTest List:"
430 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800431 if config["test_spec"] != "all" and \
432 config["test_spec"] != mod.__name__:
433 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700434 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800435 did_print = True
436 desc = mod.__doc__.strip()
437 desc = desc.split('\n')[0]
438 start_str = " Module " + mod.__name__ + ": "
439 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800440 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700441 try:
442 desc = eval('mod.' + test + '.__doc__.strip()')
443 desc = desc.split('\n')[0]
444 except:
445 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700446 if test_prio_get(mod, test) < 0:
447 start_str = " * " + test + ":"
448 else:
449 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700450 if len(start_str) > 22:
451 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800452 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700453 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800454 print
455 if not did_print:
456 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700457 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700458 print "%d modules shown with a total of %d tests" % \
459 (mod_count, test_count)
460 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700461 print "Tests preceded by * are not run by default"
462 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700463 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700464 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800465 sys.exit(0)
466
467logging_setup(config)
468logging.info("++++++++ " + time.asctime() + " ++++++++")
469
Dan Talaycod7c80d12012-04-03 15:20:57 -0700470check_profile(config)
471
Dan Talayco2c0dba32010-03-06 22:47:06 -0800472# Generate the test suite
473#@todo Decide if multiple suites are ever needed
474suite = unittest.TestSuite()
475
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700476#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800477if config["test_spec"] == "all":
478 for mod in config["all_tests"].keys():
479 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700480 # For now, a way to avoid tests
481 if test_prio_get(mod, test) >= 0:
482 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800483
484else:
485 for ts_entry in config["test_spec"].split(","):
486 parts = ts_entry.split(".")
487
488 if len(parts) == 1: # Either a module or test name
489 if ts_entry in config["mod_name_map"].keys():
490 mod = config["mod_name_map"][ts_entry]
491 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700492 if test_prio_get(mod, test) >= 0:
493 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800494 else: # Search for matching tests
495 test_found = False
496 for mod in config["all_tests"].keys():
497 if ts_entry in config["all_tests"][mod]:
498 add_test(suite, mod, ts_entry)
499 test_found = True
500 if not test_found:
501 die("Could not find module or test: " + ts_entry)
502
503 elif len(parts) == 2: # module.test
504 if parts[0] not in config["mod_name_map"]:
505 die("Unknown module in test spec: " + ts_entry)
506 mod = config["mod_name_map"][parts[0]]
507 if parts[1] in config["all_tests"][mod]:
508 add_test(suite, mod, parts[1])
509 else:
510 die("No known test matches: " + ts_entry)
511
512 else:
513 die("Bad test spec: " + ts_entry)
514
515# Check if platform specified
Dan Talayco48370102010-03-03 15:17:33 -0800516if config["platform"]:
517 _imp_string = "from " + config["platform"] + " import *"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800518 logging.info("Importing platform: " + _imp_string)
Dan Talayco48370102010-03-03 15:17:33 -0800519 try:
520 exec(_imp_string)
521 except:
522 logging.warn("Failed to import " + config["platform"] + " file")
Rich Laneef403e52012-07-09 14:59:43 -0700523 raise
Dan Talayco48370102010-03-03 15:17:33 -0800524
525try:
526 platform_config_update(config)
527except:
528 logging.warn("Could not run platform host configuration")
Rich Laneef403e52012-07-09 14:59:43 -0700529 raise
Dan Talayco48370102010-03-03 15:17:33 -0800530
531if not config["port_map"]:
532 # Try to set up default port mapping if not done by platform
533 config["port_map"] = default_port_map_setup(config)
534
535if not config["port_map"]:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800536 die("Interface port map is not defined. Exiting")
Dan Talayco48370102010-03-03 15:17:33 -0800537
538logging.debug("Configuration: " + str(config))
539logging.info("OF port map: " + str(config["port_map"]))
540
541# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800542for (modname,mod) in config["mod_name_map"].items():
543 try:
544 mod.test_set_init(config)
545 except:
546 logging.warning("Could not run test_set_init for " + modname)
Rich Laneef403e52012-07-09 14:59:43 -0700547 raise
Dan Talayco48370102010-03-03 15:17:33 -0800548
Dan Talayco2c0dba32010-03-06 22:47:06 -0800549if config["dbg_level"] == logging.CRITICAL:
550 _verb = 0
551elif config["dbg_level"] >= logging.WARNING:
552 _verb = 1
553else:
554 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800555
Brandon Heller446c1432010-04-01 12:43:27 -0700556if os.getuid() != 0:
557 print "ERROR: Super-user privileges required. Please re-run with " \
558 "sudo or as root."
559 exit(1)
560
Dan Talaycoac25cf32010-07-20 14:08:28 -0700561
562if __name__ == "__main__":
563 logging.info("*** TEST RUN START: " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700564 result = unittest.TextTestRunner(verbosity=_verb).run(suite)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700565 if testutils.skipped_test_count > 0:
566 ts = " tests"
567 if testutils.skipped_test_count == 1: ts = " test"
568 logging.info("Skipped " + str(testutils.skipped_test_count) + ts)
569 print("Skipped " + str(testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700570 logging.info("*** TEST RUN END : " + time.asctime())
Rich Lane6a1ecb82012-07-10 18:59:44 -0700571 if result.failures:
572 exit(1)