blob: c834b8e1f72eb245f495e0c03c895a4ce8f054a0 [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
Dan Talayco48370102010-03-03 15:17:33 -0800126
Dan Talaycoba3745c2010-07-21 21:51:08 -0700127import testutils
128
Dan Talayco02eca0b2010-04-15 16:09:43 -0700129try:
130 import scapy.all as scapy
131except:
132 try:
133 import scapy as scapy
134 except:
135 sys.exit("Need to install scapy for packet parsing")
136
Dan Talaycod7c80d12012-04-03 15:20:57 -0700137##@var Profile module
138profile_mod = None
139
Dan Talayco48370102010-03-03 15:17:33 -0800140##@var DEBUG_LEVELS
141# Map from strings to debugging levels
142DEBUG_LEVELS = {
143 'debug' : logging.DEBUG,
144 'verbose' : logging.DEBUG,
145 'info' : logging.INFO,
146 'warning' : logging.WARNING,
147 'warn' : logging.WARNING,
148 'error' : logging.ERROR,
149 'critical' : logging.CRITICAL
150}
151
152_debug_default = "warning"
153_debug_level_default = DEBUG_LEVELS[_debug_default]
154
155##@var config_default
156# The default configuration dictionary for OFT
157config_default = {
Dan Talayco551befa2010-07-15 17:05:32 -0700158 "param" : None,
Dan Talayco48370102010-03-03 15:17:33 -0800159 "platform" : "local",
160 "controller_host" : "127.0.0.1",
161 "controller_port" : 6633,
162 "port_count" : 4,
163 "base_of_port" : 1,
164 "base_if_index" : 1,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700165 "relax" : False,
Dan Talayco2c0dba32010-03-06 22:47:06 -0800166 "test_spec" : "all",
167 "test_dir" : ".",
Dan Talayco48370102010-03-03 15:17:33 -0800168 "log_file" : "oft.log",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800169 "list" : False,
Dan Talayco48370102010-03-03 15:17:33 -0800170 "debug" : _debug_default,
171 "dbg_level" : _debug_level_default,
Dan Talaycoac25cf32010-07-20 14:08:28 -0700172 "port_map" : {},
Dan Talaycod7c80d12012-04-03 15:20:57 -0700173 "test_params" : "None",
174 "profile" : None
Dan Talayco48370102010-03-03 15:17:33 -0800175}
176
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700177# Default test priority
178TEST_PRIO_DEFAULT=100
Dan Talaycod7c80d12012-04-03 15:20:57 -0700179TEST_PRIO_SKIP=-1
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700180
Dan Talayco1a88c122010-03-07 22:00:20 -0800181#@todo Set up a dict of config params so easier to manage:
182# <param> <cmdline flags> <default value> <help> <optional parser>
183
Dan Talayco48370102010-03-03 15:17:33 -0800184# Map options to config structure
185def config_get(opts):
186 "Convert options class to OFT configuration dictionary"
187 cfg = config_default.copy()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800188 for key in cfg.keys():
189 cfg[key] = eval("opts." + key)
190
191 # Special case checks
Dan Talayco48370102010-03-03 15:17:33 -0800192 if opts.debug not in DEBUG_LEVELS.keys():
193 print "Warning: Bad value specified for debug level; using default"
194 opts.debug = _debug_default
Dan Talayco02eca0b2010-04-15 16:09:43 -0700195 if opts.verbose:
196 cfg["debug"] = "verbose"
Dan Talayco48370102010-03-03 15:17:33 -0800197 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
Dan Talayco2c0dba32010-03-06 22:47:06 -0800198
Dan Talayco48370102010-03-03 15:17:33 -0800199 return cfg
200
201def config_setup(cfg_dflt):
202 """
203 Set up the configuration including parsing the arguments
204
205 @param cfg_dflt The default configuration dictionary
206 @return A pair (config, args) where config is an config
207 object and args is any additional arguments from the command line
208 """
209
210 parser = OptionParser(version="%prog 0.1")
211
Dan Talayco2c0dba32010-03-06 22:47:06 -0800212 #@todo parse port map as option?
Dan Talayco48370102010-03-03 15:17:33 -0800213 # Set up default values
Dan Talayco2c0dba32010-03-06 22:47:06 -0800214 for key in cfg_dflt.keys():
215 eval("parser.set_defaults("+key+"=cfg_dflt['"+key+"'])")
Dan Talayco48370102010-03-03 15:17:33 -0800216
Dan Talayco2c0dba32010-03-06 22:47:06 -0800217 #@todo Add options via dictionary
Dan Talayco48370102010-03-03 15:17:33 -0800218 plat_help = """Set the platform type. Valid values include:
219 local: User space virtual ethernet pair setup
220 remote: Remote embedded Broadcom based switch
Dan Talayco673e0852010-03-06 23:09:23 -0800221 Create a new_plat.py file and use --platform=new_plat on the command line
Dan Talayco48370102010-03-03 15:17:33 -0800222 """
223 parser.add_option("-P", "--platform", help=plat_help)
224 parser.add_option("-H", "--host", dest="controller_host",
225 help="The IP/name of the test controller host")
226 parser.add_option("-p", "--port", dest="controller_port",
227 type="int", help="Port number of the test controller")
Dan Talayco673e0852010-03-06 23:09:23 -0800228 test_list_help = """Indicate tests to run. Valid entries are "all" (the
Dan Talaycocfa172f2012-03-23 12:03:00 -0700229 default) or a comma separated list of:
230 module Run all tests in the named module
Dan Talayco673e0852010-03-06 23:09:23 -0800231 testcase Run tests in all modules with the name testcase
232 module.testcase Run the specific test case
233 """
234 parser.add_option("--test-spec", "--test-list", help=test_list_help)
Dan Talayco48370102010-03-03 15:17:33 -0800235 parser.add_option("--log-file",
236 help="Name of log file, empty string to log to console")
237 parser.add_option("--debug",
238 help="Debug lvl: debug, info, warning, error, critical")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700239 parser.add_option("--port-count", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800240 help="Number of ports to use (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700241 parser.add_option("--base-of-port", type="int",
Dan Talayco48370102010-03-03 15:17:33 -0800242 help="Base OpenFlow port number (optional)")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700243 parser.add_option("--base-if-index", type="int",
Dan Talayco2c0dba32010-03-06 22:47:06 -0800244 help="Base interface index number (optional)")
245 parser.add_option("--list", action="store_true",
Brandon Heller824504e2010-04-01 12:21:37 -0700246 help="List all tests and exit")
Dan Talayco02eca0b2010-04-15 16:09:43 -0700247 parser.add_option("--verbose", action="store_true",
248 help="Short cut for --debug=verbose")
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700249 parser.add_option("--relax", action="store_true",
250 help="Relax packet match checks allowing other packets")
Dan Talayco551befa2010-07-15 17:05:32 -0700251 parser.add_option("--param", type="int",
252 help="Parameter sent to test (for debugging)")
Dan Talaycod7c80d12012-04-03 15:20:57 -0700253 parser.add_option("--profile",
254 help="File listing tests to skip/run")
Dan Talaycoac25cf32010-07-20 14:08:28 -0700255 parser.add_option("-t", "--test-params",
Dan Talaycof6e76c02012-03-23 10:56:12 -0700256 help="""Set test parameters: key=val;...
257 NOTE: key MUST be a valid Python identifier, egr_count not egr-count
258 See --list""")
Dan Talayco48370102010-03-03 15:17:33 -0800259 # Might need this if other parsers want command line
260 # parser.allow_interspersed_args = False
261 (options, args) = parser.parse_args()
262
263 config = config_get(options)
264
265 return (config, args)
266
Dan Talaycod7c80d12012-04-03 15:20:57 -0700267def check_profile(config):
Dan Talaycod15bed52012-04-04 10:39:52 -0700268 """
269 Import a profile from the profiles library
270 """
271
Dan Talaycod7c80d12012-04-03 15:20:57 -0700272 global profile_mod
273 if "profile" in config and config["profile"]:
Dan Talaycod15bed52012-04-04 10:39:52 -0700274 logging.info("Importing profile: %s" % config["profile"])
275 profile_name = "profiles." + config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700276 try:
Dan Talaycod15bed52012-04-04 10:39:52 -0700277 top_mod = __import__(profile_name)
278 profile_mod = eval("top_mod." + config["profile"])
279 logging.info("Imported profile %s. Dir: %s" %
280 (config["profile"], str(dir(profile_mod))))
Dan Talaycod7c80d12012-04-03 15:20:57 -0700281 except:
282 logging.info("Could not import profile: %s.py" %
283 config["profile"])
Dan Talaycod15bed52012-04-04 10:39:52 -0700284 print "Failed to import profile: %s" % config["profile"]
Dan Talaycod7c80d12012-04-03 15:20:57 -0700285 profile_mod = None
286 else:
287 logging.info("No profile specified")
288
289
Dan Talayco48370102010-03-03 15:17:33 -0800290def logging_setup(config):
291 """
292 Set up logging based on config
293 """
294 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
295 _datefmt = "%H:%M:%S"
Dan Talayco88fc8802010-03-07 11:37:52 -0800296 logging.basicConfig(filename=config["log_file"],
297 level=config["dbg_level"],
298 format=_format, datefmt=_datefmt)
Dan Talayco48370102010-03-03 15:17:33 -0800299
300def default_port_map_setup(config):
301 """
302 Setup the OF port mapping based on config
303 @param config The OFT configuration structure
304 @return Port map dictionary
305 """
306 if (config["base_of_port"] is None) or not config["port_count"]:
307 return None
308 port_map = {}
309 if config["platform"] == "local":
310 # For local, use every other veth port
311 for idx in range(config["port_count"]):
312 port_map[config["base_of_port"] + idx] = "veth" + \
313 str(config["base_if_index"] + (2 * idx))
314 elif config["platform"] == "remote":
315 # For remote, use eth ports
316 for idx in range(config["port_count"]):
317 port_map[config["base_of_port"] + idx] = "eth" + \
318 str(config["base_if_index"] + idx)
319 else:
320 return None
321
322 logging.info("Built default port map")
323 return port_map
324
Dan Talayco2c0dba32010-03-06 22:47:06 -0800325def test_list_generate(config):
326 """Generate the list of all known tests indexed by module name
327
328 Conventions: Test files must implement the function test_set_init
329
Dan Talayco1a88c122010-03-07 22:00:20 -0800330 Test cases are classes that implement runTest
Dan Talayco2c0dba32010-03-06 22:47:06 -0800331
332 @param config The oft configuration dictionary
333 @returns An array of triples (mod-name, module, [tests]) where
334 mod-name is the string (filename) of the module, module is the
335 value returned from __import__'ing the module and [tests] is an
336 array of strings giving the test cases from the module.
337 """
338
339 # Find and import test files
340 p1 = Popen(["find", config["test_dir"], "-type","f"], stdout = PIPE)
341 p2 = Popen(["xargs", "grep", "-l", "-e", "^def test_set_init"],
342 stdin=p1.stdout, stdout=PIPE)
343
344 all_tests = {}
345 mod_name_map = {}
346 # There's an extra empty entry at the end of the list
347 filelist = p2.communicate()[0].split("\n")[:-1]
348 for file in filelist:
Dan Talaycoac25cf32010-07-20 14:08:28 -0700349 if file[-1:] == '~' or file[0] == '#':
Dan Talaycode2a6392010-03-10 13:56:51 -0800350 continue
Dan Talayco2c0dba32010-03-06 22:47:06 -0800351 modfile = file.lstrip('./')[:-3]
352
353 try:
354 mod = __import__(modfile)
355 except:
356 logging.warning("Could not import file " + file)
357 continue
358 mod_name_map[modfile] = mod
359 added_fn = False
360 for fn in dir(mod):
361 if 'runTest' in dir(eval("mod." + fn)):
362 if not added_fn:
363 mod_name_map[modfile] = mod
364 all_tests[mod] = []
365 added_fn = True
366 all_tests[mod].append(fn)
367 config["all_tests"] = all_tests
368 config["mod_name_map"] = mod_name_map
369
370def die(msg, exit_val=1):
371 print msg
372 logging.critical(msg)
373 sys.exit(exit_val)
374
375def add_test(suite, mod, name):
376 logging.info("Adding test " + mod.__name__ + "." + name)
377 suite.addTest(eval("mod." + name)())
378
Dan Talayco79f36082010-03-11 16:53:53 -0800379def _space_to(n, str):
380 """
381 Generate a string of spaces to achieve width n given string str
382 If length of str >= n, return one space
383 """
384 spaces = n - len(str)
385 if spaces > 0:
386 return " " * spaces
387 return " "
388
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700389def test_prio_get(mod, test):
390 """
391 Return the priority of a test
Dan Talaycod7c80d12012-04-03 15:20:57 -0700392
393 If test is in "skip list" from profile, return the skip value
394
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700395 If set in the test_prio variable for the module, return
396 that value. Otherwise return 100 (default)
397 """
Dan Talaycod7c80d12012-04-03 15:20:57 -0700398 if profile_mod:
399 if profile_mod.skip_test_list and test in profile_mod.skip_test_list:
400 logging.info("Skipping test %s due to profile" % test)
401 return TEST_PRIO_SKIP
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700402 if 'test_prio' in dir(mod):
403 if test in mod.test_prio.keys():
404 return mod.test_prio[test]
405 return TEST_PRIO_DEFAULT
406
Dan Talayco48370102010-03-03 15:17:33 -0800407#
408# Main script
409#
410
411# Get configuration, set up logging, import platform from file
412(config, args) = config_setup(config_default)
Dan Talayco48370102010-03-03 15:17:33 -0800413
Dan Talayco2c0dba32010-03-06 22:47:06 -0800414test_list_generate(config)
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700415oft_config = config
Dan Talayco2c0dba32010-03-06 22:47:06 -0800416
417# Check if test list is requested; display and exit if so
418if config["list"]:
Dan Talayco79f36082010-03-11 16:53:53 -0800419 did_print = False
Dan Talayco7f8dba82012-04-12 12:58:52 -0700420 mod_count = 0
421 test_count = 0
Dan Talayco2c0dba32010-03-06 22:47:06 -0800422 print "\nTest List:"
423 for mod in config["all_tests"].keys():
Dan Talayco79f36082010-03-11 16:53:53 -0800424 if config["test_spec"] != "all" and \
425 config["test_spec"] != mod.__name__:
426 continue
Dan Talayco7f8dba82012-04-12 12:58:52 -0700427 mod_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800428 did_print = True
429 desc = mod.__doc__.strip()
430 desc = desc.split('\n')[0]
431 start_str = " Module " + mod.__name__ + ": "
432 print start_str + _space_to(22, start_str) + desc
Dan Talayco2c0dba32010-03-06 22:47:06 -0800433 for test in config["all_tests"][mod]:
Dan Talayco551befa2010-07-15 17:05:32 -0700434 try:
435 desc = eval('mod.' + test + '.__doc__.strip()')
436 desc = desc.split('\n')[0]
437 except:
438 desc = "No description"
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700439 if test_prio_get(mod, test) < 0:
440 start_str = " * " + test + ":"
441 else:
442 start_str = " " + test + ":"
Dan Talayco551befa2010-07-15 17:05:32 -0700443 if len(start_str) > 22:
444 desc = "\n" + _space_to(22, "") + desc
Dan Talayco79f36082010-03-11 16:53:53 -0800445 print start_str + _space_to(22, start_str) + desc
Dan Talayco7f8dba82012-04-12 12:58:52 -0700446 test_count += 1
Dan Talayco79f36082010-03-11 16:53:53 -0800447 print
448 if not did_print:
449 print "No tests found for " + config["test_spec"]
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700450 else:
Dan Talayco7f8dba82012-04-12 12:58:52 -0700451 print "%d modules shown with a total of %d tests" % \
452 (mod_count, test_count)
453 print
Dan Talayco7aa0b812010-07-20 14:51:41 -0700454 print "Tests preceded by * are not run by default"
455 print "Tests marked (TP1) after name take --test-params including:"
Dan Talaycoac25cf32010-07-20 14:08:28 -0700456 print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
Dan Talaycod7c80d12012-04-03 15:20:57 -0700457 print "Note that --profile may override which tests are run"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800458 sys.exit(0)
459
460logging_setup(config)
461logging.info("++++++++ " + time.asctime() + " ++++++++")
462
Dan Talaycod7c80d12012-04-03 15:20:57 -0700463check_profile(config)
464
Dan Talayco2c0dba32010-03-06 22:47:06 -0800465# Generate the test suite
466#@todo Decide if multiple suites are ever needed
467suite = unittest.TestSuite()
468
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700469#@todo Allow specification of priority to override prio check
Dan Talayco2c0dba32010-03-06 22:47:06 -0800470if config["test_spec"] == "all":
471 for mod in config["all_tests"].keys():
472 for test in config["all_tests"][mod]:
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700473 # For now, a way to avoid tests
474 if test_prio_get(mod, test) >= 0:
475 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800476
477else:
478 for ts_entry in config["test_spec"].split(","):
479 parts = ts_entry.split(".")
480
481 if len(parts) == 1: # Either a module or test name
482 if ts_entry in config["mod_name_map"].keys():
483 mod = config["mod_name_map"][ts_entry]
484 for test in config["all_tests"][mod]:
Dan Talayco830b4412011-08-23 22:49:21 -0700485 if test_prio_get(mod, test) >= 0:
486 add_test(suite, mod, test)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800487 else: # Search for matching tests
488 test_found = False
489 for mod in config["all_tests"].keys():
490 if ts_entry in config["all_tests"][mod]:
491 add_test(suite, mod, ts_entry)
492 test_found = True
493 if not test_found:
494 die("Could not find module or test: " + ts_entry)
495
496 elif len(parts) == 2: # module.test
497 if parts[0] not in config["mod_name_map"]:
498 die("Unknown module in test spec: " + ts_entry)
499 mod = config["mod_name_map"][parts[0]]
500 if parts[1] in config["all_tests"][mod]:
501 add_test(suite, mod, parts[1])
502 else:
503 die("No known test matches: " + ts_entry)
504
505 else:
506 die("Bad test spec: " + ts_entry)
507
508# Check if platform specified
Dan Talayco48370102010-03-03 15:17:33 -0800509if config["platform"]:
510 _imp_string = "from " + config["platform"] + " import *"
Dan Talayco2c0dba32010-03-06 22:47:06 -0800511 logging.info("Importing platform: " + _imp_string)
Dan Talayco48370102010-03-03 15:17:33 -0800512 try:
513 exec(_imp_string)
514 except:
515 logging.warn("Failed to import " + config["platform"] + " file")
516
517try:
518 platform_config_update(config)
519except:
520 logging.warn("Could not run platform host configuration")
521
522if not config["port_map"]:
523 # Try to set up default port mapping if not done by platform
524 config["port_map"] = default_port_map_setup(config)
525
526if not config["port_map"]:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800527 die("Interface port map is not defined. Exiting")
Dan Talayco48370102010-03-03 15:17:33 -0800528
529logging.debug("Configuration: " + str(config))
530logging.info("OF port map: " + str(config["port_map"]))
531
532# Init the test sets
Dan Talayco2c0dba32010-03-06 22:47:06 -0800533for (modname,mod) in config["mod_name_map"].items():
534 try:
535 mod.test_set_init(config)
536 except:
537 logging.warning("Could not run test_set_init for " + modname)
Dan Talayco48370102010-03-03 15:17:33 -0800538
Dan Talayco2c0dba32010-03-06 22:47:06 -0800539if config["dbg_level"] == logging.CRITICAL:
540 _verb = 0
541elif config["dbg_level"] >= logging.WARNING:
542 _verb = 1
543else:
544 _verb = 2
Dan Talayco48370102010-03-03 15:17:33 -0800545
Brandon Heller446c1432010-04-01 12:43:27 -0700546if os.getuid() != 0:
547 print "ERROR: Super-user privileges required. Please re-run with " \
548 "sudo or as root."
549 exit(1)
550
Dan Talaycoac25cf32010-07-20 14:08:28 -0700551
552if __name__ == "__main__":
553 logging.info("*** TEST RUN START: " + time.asctime())
554 unittest.TextTestRunner(verbosity=_verb).run(suite)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700555 if testutils.skipped_test_count > 0:
556 ts = " tests"
557 if testutils.skipped_test_count == 1: ts = " test"
558 logging.info("Skipped " + str(testutils.skipped_test_count) + ts)
559 print("Skipped " + str(testutils.skipped_test_count) + ts)
Dan Talaycoac25cf32010-07-20 14:08:28 -0700560 logging.info("*** TEST RUN END : " + time.asctime())
Dan Talaycoba3745c2010-07-21 21:51:08 -0700561
Dan Talayco48370102010-03-03 15:17:33 -0800562