blob: 14bcbfbcdc749336dbb1db893fd546dcca2b3e1a [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""")
Rich Lanebd186802012-12-22 17:46:24 -080088gParser.add_argument('--ovs-vswitchd-verbosity',
89 help="""Set vswitchd logging level (off/emer/err/warn/info/dbg)""",
90 default="dbg")
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -070091gParser.add_argument('--ovs-vswitchd',
92 help="""Path to the target ovs-vswitchd binary""")
93gParser.add_argument('--ovs-vsctl',
94 help="""Path to the target ovs-vsctl binary""")
95gParser.add_argument('--ovs-ofctl',
96 help="""Path to the target ovs-ofctl binary""")
97gParser.add_argument('--ovsdb-tool',
98 help="""Path to the target ovsdb-tool binary""")
99gParser.add_argument('--ovsdb-server',
100 help="""Path to the target ovsdb-server binary""")
101gParser.add_argument('--ovs-kmod',
102 help="""Path to the OVS kernel module""")
103gParser.add_argument('--ovs-src-dir',
104 help="""Directory for the OVS Source Files""")
105
106gParser.add_argument('--ovs-log-dir',
107 help="""Directory for the OVS Runtime Log Files""")
108
109gParser.add_argument('--ovs-version')
110
111gParser.add_argument("--ovs-base-dir", help="OVS Base Installation Directory")
112
113gParser.add_argument("--ovs-runtime-dir",
114 help="OVS Runtime Directory",
115 default="/var/run/ovs")
116
117gParser.add_argument('--ovs-db-sock',
118 help="Domain Socket Location")
119
120gParser.add_argument('--ovs-db-file',
121 help="Location for the OVS database file")
122
123
124#
125# Logging/Debugging/Testing options
126#
127gParser.add_argument('--dry',
128 help="""Dry run only. Don't actually do anything""",
129 action='store_true', default=False)
130
131gParser.add_argument('--log',
132 help='Enable logging',
133 action='store_true', default=False)
134
135gParser.add_argument('--verbose',
136 help='Enable verbose output information',
137 action='store_true', default=False)
138
139gParser.add_argument("--kill", help="Kill running OVS",
140 default=False, action='store_true')
141
142gParser.add_argument("--keep-veths",
143 help="""By default, all existing veths will be destroyed and
144the veth module removed before initializing. If you don't want the veth module
145removed, specify this argument. Your mileage may vary if you use this.""",
146 default=False, action='store_true')
147
148gParser.add_argument("--no-kmod",
149 help="""Do not attempt to insert or remove the OVS kernel module.
150Your mileage may vary.""",
151 default=False, action='store_true')
152
153gParser.add_argument("--vlog",
154 help="""Tail the running vswitch.log file in a new xterm""",
155 default=False, action='store_true')
156
157#
158# Runtime and setup arguments
159#
160gParser.add_argument('-p', '--port-count', type=int,
161 help="Number of veth ports to connect.",
162 default='4')
163
164
165gParser.add_argument("--bridge", help="Name of OF OVS Bridge",
166 default="ofbr0")
167
168gParser.add_argument("--cip", help="Controller Connection",
169 default="127.0.0.1")
170
171gParser.add_argument("--cport", type=int, help="Controller Port",
172 default=6633)
Jeffrey Townsend28dd03d2012-07-16 11:53:11 -0700173gParser.add_argument("--dpid", help="DPID")
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700174
175gParser.add_argument("--max_backoff", help="VSwitchD max backoff value",
176 default=1000, type=int)
177
178
179gParser.add_argument("--keep-db",
180 help="""By default, a new database is initialized at each
181execution. If you want to keep and use the old database (if it exists),
182use this option""",
183 action='store_true', default=False)
184
Dan Talayco7b08e402012-04-12 22:30:16 -0700185gParser.add_argument('-lb', '--loopback',
186 help="Create a loopback pair. The port numbers are port_count+1 and port_count+2.",
187 default=False, action='store_true')
188
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700189
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700190gParser.add_argument("--cli",
191 help="Run the ovs-ctl cli after initialization",
192 action='store_true', default=False)
193
194gParser.add_argument("--teardown",
195 help="Kill OVS instance after CLI exits",
196 action='store_true', default=False)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700197
198#
199# Reset defaults based on config files and override
200#
201# Anything in the "Defaults" section gets slurped first:
202defaults = {}
203if gConfig.has_section('Defaults'):
204 defaults = dict(gConfig.items('Defaults'))
205 gParser.set_defaults(**defaults)
206
207#
208# The configuration to execute might be specified in on the command line, or in the Defaults section(s)
209# of the config files.
210#
211# If its specified on the command line, it will be present in configArgs.config
212# If its specified in the section, it will only be present in the defaults dict.
213# Need to check both. Command line takes precedence.
214#
215gConfigSection = None
216if configArgs.config != None:
217 gConfigSection = configArgs.config
218elif defaults.has_key('config'):
219 gConfigSection = defaults['config']
220
221
222if gConfigSection != None:
223 if gConfig.has_section(gConfigSection):
224 section = dict(gConfig.items(gConfigSection))
225 gParser.set_defaults(**section)
226 else:
227 print >>sys.stderr, "Requestion configuration '%s' does not exist" % (configArgs.config)
228 sys.exit()
229
230
231###############################################################################
232#
233# Done with argument setup. Parser remaining arguments
234#
235###############################################################################
236gArgs = gParser.parse_args(remainingArgs)
237
238
239#
240# At the end of all of this, we need the following things to be defined
241# or derived:
242#
243gRequiredOptions = [
244 [ 'ovs_vswitchd_schema', 'ovs_src_dir', '/vswitchd/vswitch.ovsschema', True, True ],
245 [ 'ovs_vswitchd', 'ovs_base_dir', '/sbin/ovs-vswitchd', True, True ],
246 [ 'ovs_vsctl', 'ovs_base_dir', '/bin/ovs-vsctl', True, True ],
247 [ 'ovs_ofctl', 'ovs_base_dir', '/bin/ovs-ofctl', True, True ],
248 [ 'ovsdb_tool', 'ovs_base_dir', '/bin/ovsdb-tool', True, True, ],
249 [ 'ovsdb_server', 'ovs_base_dir', '/sbin/ovsdb-server', True, True, ],
250 [ 'ovs_db_file', 'ovs_base_dir', '/ovs.db', False, True, ],
251 [ 'ovs_db_sock', 'ovs_runtime_dir', '/db.sock', False, True, ],
252 [ 'ovs_kmod', 'ovs_base_dir', '/sbin/openvswitch_mod.ko', True, not gArgs.no_kmod ],
253]
254
255def __require_option(key, basedir, path, exists=True):
256 value = getattr(gArgs, key)
257 if value is None:
258 # Unspecified -- try to default based on given path
259 value = getattr(gArgs, basedir)
260
261 if value is None:
262 return False
263
264 value += path
265
266 if exists and not os.path.exists(value):
267 return False
268
269 if gArgs.verbose:
270 print '--v-- option: %s @ %s' % (key, value)
271
272 setattr(gArgs, key, value)
273
274
275Validated = True
276# Try to validate as many things as we can
277for option in gRequiredOptions:
278 if option[4]:
279 if __require_option(option[0], option[1], option[2], option[3]) == False:
280 Validated = False
281
282# If any of them failed, try to be as helpful as possible
283if Validated == False:
284 print >>sys.stdout, "\nConfiguration problem. One or more required settings are missing or could not be derived:\n"
285 for option in gRequiredOptions:
286 if option[4] is False:
287 continue
288
289 value = getattr(gArgs, option[0])
290 if value:
291 print >>sys.stdout, " %s: %s" % (option[0], value),
292 if option[3] and not os.path.exists(value):
293 print >>sys.stdout, "-- does not exist"
294 else:
295 print "(OK)"
296 else:
297 print >>sys.stdout, " %s: Unknown. " % (option[0]),
298 base = getattr(gArgs, option[1])
299 if base:
300 print >>sys.stdout, "Search path was '%s'." % (base + option[2])
301 else:
302 print >>sys.stdout, "Could not derive path because '%s' was also unspecified." % (option[1])
303 print >>sys.stdout
304 sys.exit()
305
306
307
308
309#
310# If we aren't in a dry run, you must execute as root to accompish anything
311#
312if not os.geteuid() == 0 and gArgs.dry == False:
313 print >>sys.stderr, "Must run as root to accomplish any of this."
314 sys.exit()
315
316
317###############################################################################
318#
319# Helpers
320#
321###############################################################################
322
323def createVeths(count):
324 for idx in range(0, count):
325 lcall(["/sbin/ip", "link", "add", "type", "veth"])
326
327def vethsUp(count):
328 for idx in range(0, count*2):
329 lcall(["/sbin/ifconfig", 'veth%s' % (idx), "up"])
330
331def lcall(cmd, log=gArgs.log, dry=gArgs.dry, popen=False,
332 pidFile=None):
333
334 if log or gArgs.verbose:
335 print "%s: %s" % ('popen' if popen else 'call', cmd)
336
337 if not dry:
338 if not popen:
339 subprocess.call(cmd)
340 else:
341 p = subprocess.Popen(cmd)
342 if pidFile != None:
343 pidf = open(pidFile, "w");
344 print >>pidf, p.pid
345 pidf.close()
346
347
348
349def vsctl(argsList):
350 argsList.insert(0, "--db=unix:%s" % (gArgs.ovs_db_sock))
351 argsList.insert(0, gArgs.ovs_vsctl)
352 lcall(argsList)
353
354def ofctl(argsList):
355 argsList.insert(0, gArgs.ovs_ofctl)
356 lcall(argsList)
357
358def killpid(pid):
359 try:
360 os.kill(pid, signal.SIGTERM)
361 return False
362 except OSError, e:
363 return True
364
365
366def killp(pidfile):
367 if os.path.exists(pidfile):
368 pid=int(open(pidfile).read())
369 print "Killing %s, pid=%s..." % (pidfile, pid),
370 if not gArgs.dry:
371 while not killpid(pid):
372 time.sleep(1)
373 pass
374 print "dead"
375
376
377###############################################################################
378#
379# main
380#
381###############################################################################
382
383gServerPid = gArgs.ovs_runtime_dir + "/ovsdb-server.pid"
384gSwitchPid = gArgs.ovs_runtime_dir + "/ovs-vswitchd.pid"
385gLogPid= gArgs.ovs_runtime_dir + "/ovs-vswitchd-tail.pid"
386
387# Kill previous execution based on contents of the runtime dir
388if os.path.exists(gServerPid):
389 print gServerPid
390 vsctl(["del-br", gArgs.bridge])
391
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700392
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700393def killall():
394 # Kill existing DB/vswitchd
395 killp(gSwitchPid)
396 killp(gServerPid)
397 killp(gLogPid)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700398
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700399 # Remove old logpid file, since this does not happen automagically
400 if os.path.exists(gLogPid):
401 os.remove(gLogPid)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700402
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700403 if gArgs.keep_veths == False:
404 lcall(['/sbin/rmmod', 'veth'])
405 lcall(['/sbin/modprobe', 'veth'])
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700406
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700407 # Remove kmod
408 lcall(['/sbin/rmmod', gArgs.ovs_kmod])
409
410
411killall()
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700412if gArgs.kill == True:
413 # Don't do anything else
414 sys.exit()
415
416
417# Remove bridge module
418lcall(['/sbin/rmmod', 'bridge'])
419# Insert openvswitch module
420lcall(['/sbin/insmod', gArgs.ovs_kmod])
421
Dan Talayco7b08e402012-04-12 22:30:16 -0700422port_count = gArgs.port_count
423if gArgs.loopback:
424 port_count += 1
425createVeths(port_count)
426vethsUp(port_count)
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700427
428if not os.path.exists(gArgs.ovs_db_file) or gArgs.keep_db == False:
429 print "Initializing DB @ %s" % (gArgs.ovs_db_file)
430 if os.path.exists(gArgs.ovs_db_file) and not gArgs.dry:
431 os.remove(gArgs.ovs_db_file)
432
433 lcall([gArgs.ovsdb_tool, "create", gArgs.ovs_db_file,
434 gArgs.ovs_vswitchd_schema])
435else:
436 print "Keeping existing DB @ %s" % (gArgs.ovs_db_file)
437
438
439if not os.path.exists(gArgs.ovs_runtime_dir):
440 os.makedirs(gArgs.ovs_runtime_dir)
441
442# Start dbserver
443lcall([gArgs.ovsdb_server, gArgs.ovs_db_file,
444 "--remote=punix:%s" % (gArgs.ovs_db_sock),
445 "--detach", "--pidfile=%s" % (gServerPid)])
446
447# Init db
448vsctl(["--no-wait", "init"])
449
450# Start vswitchd
451startV = [ gArgs.ovs_vswitchd,
452 "unix:%s" % (gArgs.ovs_db_sock),
Rich Lanebd186802012-12-22 17:46:24 -0800453 "--verbose=%s" % gArgs.ovs_vswitchd_verbosity,
454 "--detach",
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700455 "--pidfile=%s" % (gSwitchPid) ]
456
457if gArgs.ovs_vswitchd_log:
458 startV.append("--log-file=%s" % (gArgs.ovs_vswitchd_log))
459
460lcall(startV)
461
462if gArgs.vlog:
463 lcall(["xterm", "-T", "vswitchd-log", "-e", "tail", "-f",
464 gArgs.ovs_vswitchd_log],
465 popen=True, pidFile=gLogPid)
466
467
468# Add a bridge
469vsctl(["add-br", gArgs.bridge])
470ofctl(["show", gArgs.bridge])
471
472# Add Veths to bridge
473for idx in range(0, gArgs.port_count):
474 vsctl(["add-port", gArgs.bridge, "veth%s" % (idx*2)])
475
Dan Talayco7b08e402012-04-12 22:30:16 -0700476# Check if loopback port added
477if gArgs.loopback:
478 lb_idx = gArgs.port_count * 2
479 vsctl(["add-port", gArgs.bridge, "veth%d" % (lb_idx)])
480 vsctl(["add-port", gArgs.bridge, "veth%d" % (lb_idx+1)])
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700481
Jeffrey Townsend28dd03d2012-07-16 11:53:11 -0700482if gArgs.dpid:
483 vsctl(["set", "bridge", gArgs.bridge, "other-config:datapath-id=%s" % (
484 gArgs.dpid)])
485
Jeffrey Townsend3bb8b1b2012-03-22 11:04:09 -0700486# Set controller
487vsctl(["set-controller", gArgs.bridge, "tcp:%s:%s" % (
488 gArgs.cip, gArgs.cport)])
489
490# Minimize default backoff for controller connections
491vsctl(["set", "Controller", gArgs.bridge,
492 "max_backoff=%s" % (gArgs.max_backoff)])
493
494
495ofctl(["show", gArgs.bridge])
496
497
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700498if gArgs.cli:
499 while True:
500 cmd = raw_input("[%s] ovs-ctl> " % gConfigSection)
501 if cmd and cmd != "":
502 args = cmd.split(" ")
503 if args[0] == "vsctl" or args[0] == "ovs-vsctl":
504 vsctl(args[1:])
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700505 elif args[0] == "ofctl" or args[0] == "ovs-ofctl":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700506 ofctl(args[1:])
Jeffrey Townsend28dd03d2012-07-16 11:53:11 -0700507 elif args[0] == "dumpflows" or args[0] == "flowtable":
508 ofctl(["dump-flows", "%s" % gArgs.bridge])
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700509 elif args[0] == "exit" or args[0] == "quit":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700510 break;
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700511 elif args[0] == "kill":
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700512 gArgs.teardown = True
513 break
Jeffrey Townsend3e28dea2012-04-12 12:40:33 -0700514 else:
515 print "unknown command '%s'" % args[0]
516
Jeffrey Townsend3ed8fb72012-04-12 12:30:59 -0700517
518if gArgs.teardown:
519 print "Killing OVS"
520 killall()
521
522
523
524
525
526