blob: 2c435dcc52bae777fa6bfd07c63fda91c05cf39a [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
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700182gParser.add_argument("--cli",
183 help="Run the ovs-ctl cli after initialization",
184 action='store_true', default=False)
185
186gParser.add_argument("--teardown",
187 help="Kill OVS instance after CLI exits",
188 action='store_true', default=False)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700189
190#
191# Reset defaults based on config files and override
192#
193# Anything in the "Defaults" section gets slurped first:
194defaults = {}
195if gConfig.has_section('Defaults'):
196 defaults = dict(gConfig.items('Defaults'))
197 gParser.set_defaults(**defaults)
198
199#
200# The configuration to execute might be specified in on the command line, or in the Defaults section(s)
201# of the config files.
202#
203# If its specified on the command line, it will be present in configArgs.config
204# If its specified in the section, it will only be present in the defaults dict.
205# Need to check both. Command line takes precedence.
206#
207gConfigSection = None
208if configArgs.config != None:
209 gConfigSection = configArgs.config
210elif defaults.has_key('config'):
211 gConfigSection = defaults['config']
212
213
214if gConfigSection != None:
215 if gConfig.has_section(gConfigSection):
216 section = dict(gConfig.items(gConfigSection))
217 gParser.set_defaults(**section)
218 else:
219 print >>sys.stderr, "Requestion configuration '%s' does not exist" % (configArgs.config)
220 sys.exit()
221
222
223###############################################################################
224#
225# Done with argument setup. Parser remaining arguments
226#
227###############################################################################
228gArgs = gParser.parse_args(remainingArgs)
229
230
231#
232# At the end of all of this, we need the following things to be defined
233# or derived:
234#
235gRequiredOptions = [
236 [ 'ovs_vswitchd_schema', 'ovs_src_dir', '/vswitchd/vswitch.ovsschema', True, True ],
237 [ 'ovs_vswitchd', 'ovs_base_dir', '/sbin/ovs-vswitchd', True, True ],
238 [ 'ovs_vsctl', 'ovs_base_dir', '/bin/ovs-vsctl', True, True ],
239 [ 'ovs_ofctl', 'ovs_base_dir', '/bin/ovs-ofctl', True, True ],
240 [ 'ovsdb_tool', 'ovs_base_dir', '/bin/ovsdb-tool', True, True, ],
241 [ 'ovsdb_server', 'ovs_base_dir', '/sbin/ovsdb-server', True, True, ],
242 [ 'ovs_db_file', 'ovs_base_dir', '/ovs.db', False, True, ],
243 [ 'ovs_db_sock', 'ovs_runtime_dir', '/db.sock', False, True, ],
244 [ 'ovs_kmod', 'ovs_base_dir', '/sbin/openvswitch_mod.ko', True, not gArgs.no_kmod ],
245]
246
247def __require_option(key, basedir, path, exists=True):
248 value = getattr(gArgs, key)
249 if value is None:
250 # Unspecified -- try to default based on given path
251 value = getattr(gArgs, basedir)
252
253 if value is None:
254 return False
255
256 value += path
257
258 if exists and not os.path.exists(value):
259 return False
260
261 if gArgs.verbose:
262 print '--v-- option: %s @ %s' % (key, value)
263
264 setattr(gArgs, key, value)
265
266
267Validated = True
268# Try to validate as many things as we can
269for option in gRequiredOptions:
270 if option[4]:
271 if __require_option(option[0], option[1], option[2], option[3]) == False:
272 Validated = False
273
274# If any of them failed, try to be as helpful as possible
275if Validated == False:
276 print >>sys.stdout, "\nConfiguration problem. One or more required settings are missing or could not be derived:\n"
277 for option in gRequiredOptions:
278 if option[4] is False:
279 continue
280
281 value = getattr(gArgs, option[0])
282 if value:
283 print >>sys.stdout, " %s: %s" % (option[0], value),
284 if option[3] and not os.path.exists(value):
285 print >>sys.stdout, "-- does not exist"
286 else:
287 print "(OK)"
288 else:
289 print >>sys.stdout, " %s: Unknown. " % (option[0]),
290 base = getattr(gArgs, option[1])
291 if base:
292 print >>sys.stdout, "Search path was '%s'." % (base + option[2])
293 else:
294 print >>sys.stdout, "Could not derive path because '%s' was also unspecified." % (option[1])
295 print >>sys.stdout
296 sys.exit()
297
298
299
300
301#
302# If we aren't in a dry run, you must execute as root to accompish anything
303#
304if not os.geteuid() == 0 and gArgs.dry == False:
305 print >>sys.stderr, "Must run as root to accomplish any of this."
306 sys.exit()
307
308
309###############################################################################
310#
311# Helpers
312#
313###############################################################################
314
315def createVeths(count):
316 for idx in range(0, count):
317 lcall(["/sbin/ip", "link", "add", "type", "veth"])
318
319def vethsUp(count):
320 for idx in range(0, count*2):
321 lcall(["/sbin/ifconfig", 'veth%s' % (idx), "up"])
322
323def lcall(cmd, log=gArgs.log, dry=gArgs.dry, popen=False,
324 pidFile=None):
325
326 if log or gArgs.verbose:
327 print "%s: %s" % ('popen' if popen else 'call', cmd)
328
329 if not dry:
330 if not popen:
331 subprocess.call(cmd)
332 else:
333 p = subprocess.Popen(cmd)
334 if pidFile != None:
335 pidf = open(pidFile, "w");
336 print >>pidf, p.pid
337 pidf.close()
338
339
340
341def vsctl(argsList):
342 argsList.insert(0, "--db=unix:%s" % (gArgs.ovs_db_sock))
343 argsList.insert(0, gArgs.ovs_vsctl)
344 lcall(argsList)
345
346def ofctl(argsList):
347 argsList.insert(0, gArgs.ovs_ofctl)
348 lcall(argsList)
349
350def killpid(pid):
351 try:
352 os.kill(pid, signal.SIGTERM)
353 return False
354 except OSError, e:
355 return True
356
357
358def killp(pidfile):
359 if os.path.exists(pidfile):
360 pid=int(open(pidfile).read())
361 print "Killing %s, pid=%s..." % (pidfile, pid),
362 if not gArgs.dry:
363 while not killpid(pid):
364 time.sleep(1)
365 pass
366 print "dead"
367
368
369###############################################################################
370#
371# main
372#
373###############################################################################
374
375gServerPid = gArgs.ovs_runtime_dir + "/ovsdb-server.pid"
376gSwitchPid = gArgs.ovs_runtime_dir + "/ovs-vswitchd.pid"
377gLogPid= gArgs.ovs_runtime_dir + "/ovs-vswitchd-tail.pid"
378
379# Kill previous execution based on contents of the runtime dir
380if os.path.exists(gServerPid):
381 print gServerPid
382 vsctl(["del-br", gArgs.bridge])
383
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700384
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700385def killall():
386 # Kill existing DB/vswitchd
387 killp(gSwitchPid)
388 killp(gServerPid)
389 killp(gLogPid)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700390
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700391 # Remove old logpid file, since this does not happen automagically
392 if os.path.exists(gLogPid):
393 os.remove(gLogPid)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700394
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700395 if gArgs.keep_veths == False:
396 lcall(['/sbin/rmmod', 'veth'])
397 lcall(['/sbin/modprobe', 'veth'])
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700398
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700399 # Remove kmod
400 lcall(['/sbin/rmmod', gArgs.ovs_kmod])
401
402
403killall()
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700404if gArgs.kill == True:
405 # Don't do anything else
406 sys.exit()
407
408
409# Remove bridge module
410lcall(['/sbin/rmmod', 'bridge'])
411# Insert openvswitch module
412lcall(['/sbin/insmod', gArgs.ovs_kmod])
413
414createVeths(gArgs.port_count)
415vethsUp(gArgs.port_count)
416
417if not os.path.exists(gArgs.ovs_db_file) or gArgs.keep_db == False:
418 print "Initializing DB @ %s" % (gArgs.ovs_db_file)
419 if os.path.exists(gArgs.ovs_db_file) and not gArgs.dry:
420 os.remove(gArgs.ovs_db_file)
421
422 lcall([gArgs.ovsdb_tool, "create", gArgs.ovs_db_file,
423 gArgs.ovs_vswitchd_schema])
424else:
425 print "Keeping existing DB @ %s" % (gArgs.ovs_db_file)
426
427
428if not os.path.exists(gArgs.ovs_runtime_dir):
429 os.makedirs(gArgs.ovs_runtime_dir)
430
431# Start dbserver
432lcall([gArgs.ovsdb_server, gArgs.ovs_db_file,
433 "--remote=punix:%s" % (gArgs.ovs_db_sock),
434 "--detach", "--pidfile=%s" % (gServerPid)])
435
436# Init db
437vsctl(["--no-wait", "init"])
438
439# Start vswitchd
440startV = [ gArgs.ovs_vswitchd,
441 "unix:%s" % (gArgs.ovs_db_sock),
442 "--verbose", "--detach",
443 "--pidfile=%s" % (gSwitchPid) ]
444
445if gArgs.ovs_vswitchd_log:
446 startV.append("--log-file=%s" % (gArgs.ovs_vswitchd_log))
447
448lcall(startV)
449
450if gArgs.vlog:
451 lcall(["xterm", "-T", "vswitchd-log", "-e", "tail", "-f",
452 gArgs.ovs_vswitchd_log],
453 popen=True, pidFile=gLogPid)
454
455
456# Add a bridge
457vsctl(["add-br", gArgs.bridge])
458ofctl(["show", gArgs.bridge])
459
460# Add Veths to bridge
461for idx in range(0, gArgs.port_count):
462 vsctl(["add-port", gArgs.bridge, "veth%s" % (idx*2)])
463
464
465# Set controller
466vsctl(["set-controller", gArgs.bridge, "tcp:%s:%s" % (
467 gArgs.cip, gArgs.cport)])
468
469# Minimize default backoff for controller connections
470vsctl(["set", "Controller", gArgs.bridge,
471 "max_backoff=%s" % (gArgs.max_backoff)])
472
473
474ofctl(["show", gArgs.bridge])
475
476
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700477if gArgs.cli:
478 while True:
479 cmd = raw_input("[%s] ovs-ctl> " % gConfigSection)
480 if cmd and cmd != "":
481 args = cmd.split(" ")
482 if args[0] == "vsctl" or args[0] == "ovs-vsctl":
483 vsctl(args[1:])
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700484 elif args[0] == "ofctl" or args[0] == "ovs-ofctl":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700485 ofctl(args[1:])
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700486 elif args[0] == "exit" or args[0] == "quit":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700487 break;
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700488 elif args[0] == "kill":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700489 gArgs.teardown = True
490 break
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700491 else:
492 print "unknown command '%s'" % args[0]
493
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700494
495if gArgs.teardown:
496 print "Killing OVS"
497 killall()
498
499
500
501
502
503