blob: 5df57d9e64b7cbc3aa06164891af47c54b19d906 [file] [log] [blame]
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -07001#!/usr/bin/python
2
3import os
4import subprocess
5import argparse
6import sys
7import signal
8import time
9import ConfigParser
10import pprint
11
12###############################################################################
13#
14# Arguments
15#
16# Arguments can be specified directly, or via config file.
17# TODO -- Make this a separate reusable class
18#
19################################################################################
20gBasename = "ovs-ctl"
21
22gConfigParser = argparse.ArgumentParser(description=gBasename, add_help=False)
23
24gConfigParser.add_argument('-cf', '--config-file',
25 help="Load settings from the given config file",
26 nargs='+', metavar="FILE",
27 default=os.path.expanduser("~/."+gBasename))
28
29gConfigParser.add_argument('-c', '--config',
30 help="Use the specified configuration section",
31 default=None)
32
33gConfigParser.add_argument('-d', '--default-config-file',
34 help="Location of the local default config file",
35 metavar="FILE",
36 default="/opt/ovs/%s-default.conf" % (gBasename))
37
38gConfigParser.add_argument('-nd', '--no-default',
39 help="Do not load the default config file",
40 action='store_true', default=False)
41gConfigParser.add_argument('--dump-config',
42 help="Dump the loaded configuration settings",
43 action='store_true', default=False)
44
45#
46# Parse the config files first, then parse remaining command line arguments
47#
48gConfig = ConfigParser.SafeConfigParser()
49configArgs, remainingArgs = gConfigParser.parse_known_args()
50
51if not configArgs.no_default:
52 gConfig.read([configArgs.default_config_file])
53
54if isinstance(configArgs.config_file, str):
55 configFiles = [ configArgs.config_file]
56else:
57 configFiles = configArgs.config_file
58
59configFiles = [ os.path.expanduser(x) if x.startswith('~') else x
60 for x in configFiles ]
61
62gConfig.read(configFiles)
63
64# Dump loaded config if requested
65if configArgs.dump_config:
66 for section in gConfig.sections():
67 print section
68 for option in gConfig.options(section):
69 print " ", option, "=", gConfig.get(section, option)
70 sys.exit()
71
72
73
74#
75# Functional arguments go here
76#
77
78#
79# OVS target binaries -- these can all be specified individually.
80# If not specified, they are determined by the base directory settings
81#
82gParser = argparse.ArgumentParser(parents=[gConfigParser])
83
84gParser.add_argument('--ovs-vswitchd-schema',
85 help="""Path to the vswitchd.ovsschema file""")
86gParser.add_argument('--ovs-vswitchd-log',
87 help="""Path to the vswitchd log file""")
88gParser.add_argument('--ovs-vswitchd',
89 help="""Path to the target ovs-vswitchd binary""")
90gParser.add_argument('--ovs-vsctl',
91 help="""Path to the target ovs-vsctl binary""")
92gParser.add_argument('--ovs-ofctl',
93 help="""Path to the target ovs-ofctl binary""")
94gParser.add_argument('--ovsdb-tool',
95 help="""Path to the target ovsdb-tool binary""")
96gParser.add_argument('--ovsdb-server',
97 help="""Path to the target ovsdb-server binary""")
98gParser.add_argument('--ovs-kmod',
99 help="""Path to the OVS kernel module""")
100gParser.add_argument('--ovs-src-dir',
101 help="""Directory for the OVS Source Files""")
102
103gParser.add_argument('--ovs-log-dir',
104 help="""Directory for the OVS Runtime Log Files""")
105
106gParser.add_argument('--ovs-version')
107
108gParser.add_argument("--ovs-base-dir", help="OVS Base Installation Directory")
109
110gParser.add_argument("--ovs-runtime-dir",
111 help="OVS Runtime Directory",
112 default="/var/run/ovs")
113
114gParser.add_argument('--ovs-db-sock',
115 help="Domain Socket Location")
116
117gParser.add_argument('--ovs-db-file',
118 help="Location for the OVS database file")
119
120
121#
122# Logging/Debugging/Testing options
123#
124gParser.add_argument('--dry',
125 help="""Dry run only. Don't actually do anything""",
126 action='store_true', default=False)
127
128gParser.add_argument('--log',
129 help='Enable logging',
130 action='store_true', default=False)
131
132gParser.add_argument('--verbose',
133 help='Enable verbose output information',
134 action='store_true', default=False)
135
136gParser.add_argument("--kill", help="Kill running OVS",
137 default=False, action='store_true')
138
139gParser.add_argument("--keep-veths",
140 help="""By default, all existing veths will be destroyed and
141the veth module removed before initializing. If you don't want the veth module
142removed, specify this argument. Your mileage may vary if you use this.""",
143 default=False, action='store_true')
144
145gParser.add_argument("--no-kmod",
146 help="""Do not attempt to insert or remove the OVS kernel module.
147Your mileage may vary.""",
148 default=False, action='store_true')
149
150gParser.add_argument("--vlog",
151 help="""Tail the running vswitch.log file in a new xterm""",
152 default=False, action='store_true')
153
154#
155# Runtime and setup arguments
156#
157gParser.add_argument('-p', '--port-count', type=int,
158 help="Number of veth ports to connect.",
159 default='4')
160
161
162gParser.add_argument("--bridge", help="Name of OF OVS Bridge",
163 default="ofbr0")
164
165gParser.add_argument("--cip", help="Controller Connection",
166 default="127.0.0.1")
167
168gParser.add_argument("--cport", type=int, help="Controller Port",
169 default=6633)
170
171gParser.add_argument("--max_backoff", help="VSwitchD max backoff value",
172 default=1000, type=int)
173
174
175gParser.add_argument("--keep-db",
176 help="""By default, a new database is initialized at each
177execution. If you want to keep and use the old database (if it exists),
178use this option""",
179 action='store_true', default=False)
180
181
182
183#
184# Reset defaults based on config files and override
185#
186# Anything in the "Defaults" section gets slurped first:
187defaults = {}
188if gConfig.has_section('Defaults'):
189 defaults = dict(gConfig.items('Defaults'))
190 gParser.set_defaults(**defaults)
191
192#
193# The configuration to execute might be specified in on the command line, or in the Defaults section(s)
194# of the config files.
195#
196# If its specified on the command line, it will be present in configArgs.config
197# If its specified in the section, it will only be present in the defaults dict.
198# Need to check both. Command line takes precedence.
199#
200gConfigSection = None
201if configArgs.config != None:
202 gConfigSection = configArgs.config
203elif defaults.has_key('config'):
204 gConfigSection = defaults['config']
205
206
207if gConfigSection != None:
208 if gConfig.has_section(gConfigSection):
209 section = dict(gConfig.items(gConfigSection))
210 gParser.set_defaults(**section)
211 else:
212 print >>sys.stderr, "Requestion configuration '%s' does not exist" % (configArgs.config)
213 sys.exit()
214
215
216###############################################################################
217#
218# Done with argument setup. Parser remaining arguments
219#
220###############################################################################
221gArgs = gParser.parse_args(remainingArgs)
222
223
224#
225# At the end of all of this, we need the following things to be defined
226# or derived:
227#
228gRequiredOptions = [
229 [ 'ovs_vswitchd_schema', 'ovs_src_dir', '/vswitchd/vswitch.ovsschema', True, True ],
230 [ 'ovs_vswitchd', 'ovs_base_dir', '/sbin/ovs-vswitchd', True, True ],
231 [ 'ovs_vsctl', 'ovs_base_dir', '/bin/ovs-vsctl', True, True ],
232 [ 'ovs_ofctl', 'ovs_base_dir', '/bin/ovs-ofctl', True, True ],
233 [ 'ovsdb_tool', 'ovs_base_dir', '/bin/ovsdb-tool', True, True, ],
234 [ 'ovsdb_server', 'ovs_base_dir', '/sbin/ovsdb-server', True, True, ],
235 [ 'ovs_db_file', 'ovs_base_dir', '/ovs.db', False, True, ],
236 [ 'ovs_db_sock', 'ovs_runtime_dir', '/db.sock', False, True, ],
237 [ 'ovs_kmod', 'ovs_base_dir', '/sbin/openvswitch_mod.ko', True, not gArgs.no_kmod ],
238]
239
240def __require_option(key, basedir, path, exists=True):
241 value = getattr(gArgs, key)
242 if value is None:
243 # Unspecified -- try to default based on given path
244 value = getattr(gArgs, basedir)
245
246 if value is None:
247 return False
248
249 value += path
250
251 if exists and not os.path.exists(value):
252 return False
253
254 if gArgs.verbose:
255 print '--v-- option: %s @ %s' % (key, value)
256
257 setattr(gArgs, key, value)
258
259
260Validated = True
261# Try to validate as many things as we can
262for option in gRequiredOptions:
263 if option[4]:
264 if __require_option(option[0], option[1], option[2], option[3]) == False:
265 Validated = False
266
267# If any of them failed, try to be as helpful as possible
268if Validated == False:
269 print >>sys.stdout, "\nConfiguration problem. One or more required settings are missing or could not be derived:\n"
270 for option in gRequiredOptions:
271 if option[4] is False:
272 continue
273
274 value = getattr(gArgs, option[0])
275 if value:
276 print >>sys.stdout, " %s: %s" % (option[0], value),
277 if option[3] and not os.path.exists(value):
278 print >>sys.stdout, "-- does not exist"
279 else:
280 print "(OK)"
281 else:
282 print >>sys.stdout, " %s: Unknown. " % (option[0]),
283 base = getattr(gArgs, option[1])
284 if base:
285 print >>sys.stdout, "Search path was '%s'." % (base + option[2])
286 else:
287 print >>sys.stdout, "Could not derive path because '%s' was also unspecified." % (option[1])
288 print >>sys.stdout
289 sys.exit()
290
291
292
293
294#
295# If we aren't in a dry run, you must execute as root to accompish anything
296#
297if not os.geteuid() == 0 and gArgs.dry == False:
298 print >>sys.stderr, "Must run as root to accomplish any of this."
299 sys.exit()
300
301
302###############################################################################
303#
304# Helpers
305#
306###############################################################################
307
308def createVeths(count):
309 for idx in range(0, count):
310 lcall(["/sbin/ip", "link", "add", "type", "veth"])
311
312def vethsUp(count):
313 for idx in range(0, count*2):
314 lcall(["/sbin/ifconfig", 'veth%s' % (idx), "up"])
315
316def lcall(cmd, log=gArgs.log, dry=gArgs.dry, popen=False,
317 pidFile=None):
318
319 if log or gArgs.verbose:
320 print "%s: %s" % ('popen' if popen else 'call', cmd)
321
322 if not dry:
323 if not popen:
324 subprocess.call(cmd)
325 else:
326 p = subprocess.Popen(cmd)
327 if pidFile != None:
328 pidf = open(pidFile, "w");
329 print >>pidf, p.pid
330 pidf.close()
331
332
333
334def vsctl(argsList):
335 argsList.insert(0, "--db=unix:%s" % (gArgs.ovs_db_sock))
336 argsList.insert(0, gArgs.ovs_vsctl)
337 lcall(argsList)
338
339def ofctl(argsList):
340 argsList.insert(0, gArgs.ovs_ofctl)
341 lcall(argsList)
342
343def killpid(pid):
344 try:
345 os.kill(pid, signal.SIGTERM)
346 return False
347 except OSError, e:
348 return True
349
350
351def killp(pidfile):
352 if os.path.exists(pidfile):
353 pid=int(open(pidfile).read())
354 print "Killing %s, pid=%s..." % (pidfile, pid),
355 if not gArgs.dry:
356 while not killpid(pid):
357 time.sleep(1)
358 pass
359 print "dead"
360
361
362###############################################################################
363#
364# main
365#
366###############################################################################
367
368gServerPid = gArgs.ovs_runtime_dir + "/ovsdb-server.pid"
369gSwitchPid = gArgs.ovs_runtime_dir + "/ovs-vswitchd.pid"
370gLogPid= gArgs.ovs_runtime_dir + "/ovs-vswitchd-tail.pid"
371
372# Kill previous execution based on contents of the runtime dir
373if os.path.exists(gServerPid):
374 print gServerPid
375 vsctl(["del-br", gArgs.bridge])
376
377# Kill existing DB/vswitchd
378killp(gSwitchPid)
379killp(gServerPid)
380killp(gLogPid)
381
382# Remove old logpid file, since this does not happen automagically
383if os.path.exists(gLogPid):
384 os.remove(gLogPid)
385
386if gArgs.keep_veths == False:
387 lcall(['/sbin/rmmod', 'veth'])
388 lcall(['/sbin/modprobe', 'veth'])
389
390# Remove kmod
391lcall(['/sbin/rmmod', gArgs.ovs_kmod])
392
393if gArgs.kill == True:
394 # Don't do anything else
395 sys.exit()
396
397
398# Remove bridge module
399lcall(['/sbin/rmmod', 'bridge'])
400# Insert openvswitch module
401lcall(['/sbin/insmod', gArgs.ovs_kmod])
402
403createVeths(gArgs.port_count)
404vethsUp(gArgs.port_count)
405
406if not os.path.exists(gArgs.ovs_db_file) or gArgs.keep_db == False:
407 print "Initializing DB @ %s" % (gArgs.ovs_db_file)
408 if os.path.exists(gArgs.ovs_db_file) and not gArgs.dry:
409 os.remove(gArgs.ovs_db_file)
410
411 lcall([gArgs.ovsdb_tool, "create", gArgs.ovs_db_file,
412 gArgs.ovs_vswitchd_schema])
413else:
414 print "Keeping existing DB @ %s" % (gArgs.ovs_db_file)
415
416
417if not os.path.exists(gArgs.ovs_runtime_dir):
418 os.makedirs(gArgs.ovs_runtime_dir)
419
420# Start dbserver
421lcall([gArgs.ovsdb_server, gArgs.ovs_db_file,
422 "--remote=punix:%s" % (gArgs.ovs_db_sock),
423 "--detach", "--pidfile=%s" % (gServerPid)])
424
425# Init db
426vsctl(["--no-wait", "init"])
427
428# Start vswitchd
429startV = [ gArgs.ovs_vswitchd,
430 "unix:%s" % (gArgs.ovs_db_sock),
431 "--verbose", "--detach",
432 "--pidfile=%s" % (gSwitchPid) ]
433
434if gArgs.ovs_vswitchd_log:
435 startV.append("--log-file=%s" % (gArgs.ovs_vswitchd_log))
436
437lcall(startV)
438
439if gArgs.vlog:
440 lcall(["xterm", "-T", "vswitchd-log", "-e", "tail", "-f",
441 gArgs.ovs_vswitchd_log],
442 popen=True, pidFile=gLogPid)
443
444
445# Add a bridge
446vsctl(["add-br", gArgs.bridge])
447ofctl(["show", gArgs.bridge])
448
449# Add Veths to bridge
450for idx in range(0, gArgs.port_count):
451 vsctl(["add-port", gArgs.bridge, "veth%s" % (idx*2)])
452
453
454# Set controller
455vsctl(["set-controller", gArgs.bridge, "tcp:%s:%s" % (
456 gArgs.cip, gArgs.cport)])
457
458# Minimize default backoff for controller connections
459vsctl(["set", "Controller", gArgs.bridge,
460 "max_backoff=%s" % (gArgs.max_backoff)])
461
462
463ofctl(["show", gArgs.bridge])
464
465