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