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