blob: 1faa8a16aae8f8a52bf59ea0de27aa7c80cae75c [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
11ygenerally called config. The keys have the following
12significance.
13
14 platform : String identifying the target platform
15 controller_host : Host on which test controller is running (for sockets)
16 controller_port : Port on which test controller listens for switch cxn
17 port_count : (Optional) Number of ports in dataplane
18 base_of_port : (Optional) Base OpenFlow port number in dataplane
19 base_if_index : (Optional) Base OS network interface for dataplane
20 test_spec : (TBD) Specification of test(s) to run
21 log_file : Filename for test logging
22 debug : String giving debug level (info, warning, error...)
23 dbg_level : logging module value of debug level
24 port_map : Map of dataplane OpenFlow port to OS interface names
25
26See config_defaults below for the default values.
27
28To add configuration to the system, first add an entry to config_default
29below. If you want this to be a command line parameter, edit config_setup
30to add the option and default value to the parser. Then edit config_get
31to make sure the option value gets copied into the configuration
32structure (which then gets passed to everyone else).
33
34By convention, oft attempts to import the contents of a file by the
35name of $platform.py into the local namespace.
36
37IMPORTANT: That file should define a function platform_config_update which
38takes a configuration dictionary as an argument and updates it for the
39current run. In particular, it should set up config["port_map"] with
40the proper map from OF port numbers to OF interface names.
41
42You can add your own platform, say gp104, by adding a file gp104.py
43that defines the function platform_config_update and then use the
44parameter --platform=gp104 on the command line.
45
46If platform_config_update does not set config["port_map"], an attempt
47is made to generate a default map via the function default_port_map_setup.
48This will use "local" and "remote" for platform names if available
49and generate a sequential map based on the values of base_of_port and
50base_if_index in the configuration structure.
51
52@todo Determine and implement conventions for test_spec.
53
54The current model for test sets is basic.py. The current convention is
55that the test set should implement a function test_set_init which takes
56an oft configuration dictionary and returns a unittest.TestSuite object.
57Future test sets should do the same thing.
58
59"""
60
61import sys
62from optparse import OptionParser
63import logging
64import unittest
65
66# Import test files
67import basic
68
69##@var DEBUG_LEVELS
70# Map from strings to debugging levels
71DEBUG_LEVELS = {
72 'debug' : logging.DEBUG,
73 'verbose' : logging.DEBUG,
74 'info' : logging.INFO,
75 'warning' : logging.WARNING,
76 'warn' : logging.WARNING,
77 'error' : logging.ERROR,
78 'critical' : logging.CRITICAL
79}
80
81_debug_default = "warning"
82_debug_level_default = DEBUG_LEVELS[_debug_default]
83
84##@var config_default
85# The default configuration dictionary for OFT
86config_default = {
87 "platform" : "local",
88 "controller_host" : "127.0.0.1",
89 "controller_port" : 6633,
90 "port_count" : 4,
91 "base_of_port" : 1,
92 "base_if_index" : 1,
93 "test_spec" : "basic",
94 "log_file" : "oft.log",
95 "debug" : _debug_default,
96 "dbg_level" : _debug_level_default,
97 "port_map" : {}
98}
99
100# Map options to config structure
101def config_get(opts):
102 "Convert options class to OFT configuration dictionary"
103 cfg = config_default.copy()
104 cfg["platform"] = opts.platform
105 cfg["controller_host"] = opts.controller_host
106 cfg["controller_port"] = opts.controller_port
107 cfg["test_spec"] = opts.test_spec
108 cfg["log_file"] = opts.log_file
109 if opts.debug not in DEBUG_LEVELS.keys():
110 print "Warning: Bad value specified for debug level; using default"
111 opts.debug = _debug_default
112 cfg["debug"] = opts.debug
113 cfg["dbg_level"] = DEBUG_LEVELS[cfg["debug"]]
114 cfg["base_of_port"] = opts.base_of_port
115 cfg["base_if_index"] = opts.base_if_index
116 cfg["port_count"] = opts.port_count
117 return cfg
118
119def config_setup(cfg_dflt):
120 """
121 Set up the configuration including parsing the arguments
122
123 @param cfg_dflt The default configuration dictionary
124 @return A pair (config, args) where config is an config
125 object and args is any additional arguments from the command line
126 """
127
128 parser = OptionParser(version="%prog 0.1")
129
130 # Set up default values
131 parser.set_defaults(platform=cfg_dflt["platform"])
132 parser.set_defaults(controller_host=cfg_dflt["controller_host"])
133 parser.set_defaults(controller_port=cfg_dflt["controller_port"])
134 parser.set_defaults(test_spec=cfg_dflt["test_spec"])
135 parser.set_defaults(log_file=cfg_dflt["log_file"])
136 parser.set_defaults(debug=cfg_dflt["debug"])
137 parser.set_defaults(base_of_port=cfg_dflt["base_of_port"])
138 parser.set_defaults(base_if_index=cfg_dflt["base_if_index"])
139 parser.set_defaults(port_count=cfg_dflt["port_count"])
140
141 plat_help = """Set the platform type. Valid values include:
142 local: User space virtual ethernet pair setup
143 remote: Remote embedded Broadcom based switch
144 """
145 parser.add_option("-P", "--platform", help=plat_help)
146 parser.add_option("-H", "--host", dest="controller_host",
147 help="The IP/name of the test controller host")
148 parser.add_option("-p", "--port", dest="controller_port",
149 type="int", help="Port number of the test controller")
150 parser.add_option("--test-spec", "--test-list",
151 help="Indicate tests to run (TBD)")
152 parser.add_option("--log-file",
153 help="Name of log file, empty string to log to console")
154 parser.add_option("--debug",
155 help="Debug lvl: debug, info, warning, error, critical")
156 parser.add_option("--port_count",
157 help="Number of ports to use (optional)")
158 parser.add_option("--base_of_port",
159 help="Base OpenFlow port number (optional)")
160 parser.add_option("--base_if_index",
161 help="Base interface index number (optional)")
162 # Might need this if other parsers want command line
163 # parser.allow_interspersed_args = False
164 (options, args) = parser.parse_args()
165
166 config = config_get(options)
167
168 return (config, args)
169
170def logging_setup(config):
171 """
172 Set up logging based on config
173 """
174 _format = "%(asctime)s %(name)-10s: %(levelname)-8s: %(message)s"
175 _datefmt = "%H:%M:%S"
176 if config["log_file"]:
177 logging.basicConfig(filename=config["log_file"],
178 level=config["dbg_level"],
179 format=_format, datefmt=_datefmt)
180 #@todo Handle "no log file"
181
182def default_port_map_setup(config):
183 """
184 Setup the OF port mapping based on config
185 @param config The OFT configuration structure
186 @return Port map dictionary
187 """
188 if (config["base_of_port"] is None) or not config["port_count"]:
189 return None
190 port_map = {}
191 if config["platform"] == "local":
192 # For local, use every other veth port
193 for idx in range(config["port_count"]):
194 port_map[config["base_of_port"] + idx] = "veth" + \
195 str(config["base_if_index"] + (2 * idx))
196 elif config["platform"] == "remote":
197 # For remote, use eth ports
198 for idx in range(config["port_count"]):
199 port_map[config["base_of_port"] + idx] = "eth" + \
200 str(config["base_if_index"] + idx)
201 else:
202 return None
203
204 logging.info("Built default port map")
205 return port_map
206
207#
208# Main script
209#
210
211# Get configuration, set up logging, import platform from file
212(config, args) = config_setup(config_default)
213logging_setup(config)
214logging.info("*** STARTING TEST RUN ***")
215
216of_os_port_map = None
217if config["platform"]:
218 _imp_string = "from " + config["platform"] + " import *"
219 logging.info("Importing: " + _imp_string)
220 try:
221 exec(_imp_string)
222 except:
223 logging.warn("Failed to import " + config["platform"] + " file")
224
225try:
226 platform_config_update(config)
227except:
228 logging.warn("Could not run platform host configuration")
229
230if not config["port_map"]:
231 # Try to set up default port mapping if not done by platform
232 config["port_map"] = default_port_map_setup(config)
233
234if not config["port_map"]:
235 logging.critical("Interface port map is not defined. Exiting")
236 print("Interface port map is not defined. Exiting")
237 sys.exit(1)
238
239logging.debug("Configuration: " + str(config))
240logging.info("OF port map: " + str(config["port_map"]))
241
242# Init the test sets
243#@todo Use test-spec from config to determine which tests to run
244basic_suite = basic.test_set_init(config)
245if config["dbg_level"] >= logging.WARNING: _verb = 1
246else: _verb = 2
247
248unittest.TextTestRunner(verbosity=_verb).run(basic_suite)
249
250logging.info("*** END OF TESTS ***")
251