ovs-dev.py: add --user option
[cascardo/ovs.git] / utilities / ovs-dev.py
1 #!/usr/bin/python
2 # Copyright (c) 2013, 2014, 2015 Nicira, Inc.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import optparse
17 import os
18 import shutil
19 import subprocess
20 import sys
21 import tempfile
22
23 ENV = os.environ
24 HOME = ENV["HOME"]
25 PWD = os.getcwd()
26 OVS_SRC = HOME + "/ovs"
27 if os.path.exists(PWD + "/WHY-OVS.md"):
28     OVS_SRC = PWD  # Use current directory as OVS source tree
29 RUNDIR = OVS_SRC + "/_run"
30 BUILD_GCC = OVS_SRC + "/_build-gcc"
31 BUILD_CLANG = OVS_SRC + "/_build-clang"
32
33 options = None
34 parser = None
35 commands = []
36
37 def set_path(build):
38     PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": build}
39
40     ENV["PATH"] = PATH + ":" + ENV["PATH"]
41
42 def _sh(*args, **kwargs):
43     print "------> " + " ".join(args)
44     shell = len(args) == 1
45     if kwargs.get("capture", False):
46         proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell)
47         return proc.stdout.readlines()
48     elif kwargs.get("check", True):
49         subprocess.check_call(args, shell=shell)
50     else:
51         subprocess.call(args, shell=shell)
52
53
54 def uname():
55     return _sh("uname", "-r", capture=True)[0].strip()
56
57
58 def sudo():
59     if os.geteuid() != 0:
60         _sh(" ".join(["sudo"] + sys.argv), check=True)
61         sys.exit(0)
62
63 def conf():
64     tag()
65
66     try:
67         os.remove(OVS_SRC + "/Makefile")
68     except OSError:
69         pass
70
71     configure = ["../configure",
72                  "--prefix=" + RUNDIR, "--localstatedir=" + RUNDIR,
73                  "--with-logdir=%s/log" % RUNDIR,
74                  "--with-rundir=%s/run" % RUNDIR,
75                  "--enable-silent-rules", "--with-dbdir=" + RUNDIR, "--silent"]
76
77     cflags = "-g -fno-omit-frame-pointer"
78
79     if options.werror:
80         configure.append("--enable-Werror")
81
82     if options.cache_time:
83         configure.append("--enable-cache-time")
84
85     if options.mandir:
86         configure.append("--mandir=" + options.mandir)
87
88     if options.with_dpdk:
89         configure.append("--with-dpdk=" + options.with_dpdk)
90         cflags += " -Wno-cast-align -Wno-bad-function-cast" # DPDK warnings.
91
92     if options.optimize is None:
93         options.optimize = 0
94
95     cflags += " -O%s" % str(options.optimize)
96
97     ENV["CFLAGS"] = cflags
98
99     _sh("./boot.sh")
100
101     try:
102         os.mkdir(BUILD_GCC)
103     except OSError:
104         pass # Directory exists.
105
106     os.chdir(BUILD_GCC)
107     _sh(*(configure + ["--with-linux=/lib/modules/%s/build" % uname()]))
108
109     try:
110         _sh("clang --version", check=True)
111         clang = True
112     except subprocess.CalledProcessError:
113         clang = False
114
115     try:
116         _sh("sparse --version", check=True)
117         sparse = True
118     except subprocess.CalledProcessError:
119         sparse = False
120
121     if clang:
122         try:
123             os.mkdir(BUILD_CLANG)
124         except OSError:
125             pass # Directory exists.
126
127         ENV["CC"] = "clang"
128         os.chdir(BUILD_CLANG)
129         _sh(*configure)
130
131     if sparse:
132         c1 = "C=1"
133     else:
134         c1 = ""
135
136     os.chdir(OVS_SRC)
137
138     make_str = "\t$(MAKE) -C %s $@\n"
139
140     mf = open(OVS_SRC + "/Makefile", "w")
141     mf.write("all:\n%:\n")
142     if clang:
143         mf.write(make_str % BUILD_CLANG)
144     mf.write("\t$(MAKE) -C %s %s $@\n" % (BUILD_GCC, c1))
145     mf.write("\ncheck-valgrind:\n")
146     mf.write("\ncheck:\n")
147     mf.write(make_str % BUILD_GCC)
148     mf.close()
149 commands.append(conf)
150
151
152 def make(args=""):
153     make = "make -s -j 8 " + args
154     _sh(make)
155 commands.append(make)
156
157
158 def check():
159     flags = ""
160     if options.jobs:
161         flags += "-j%d " % options.jobs
162     else:
163         flags += "-j8 "
164     if options.tests:
165         for arg in str.split(options.tests):
166             if arg[0].isdigit():
167                 flags += "%s " % arg
168             else:
169                 flags += "-k %s " % arg
170     ENV["TESTSUITEFLAGS"] = flags
171     make("check")
172 commands.append(check)
173
174
175 def tag():
176     ctags = ['ctags', '-R', '-f', '.tags']
177
178     try:
179         _sh(*(ctags + ['--exclude="datapath/"']))
180     except:
181         try:
182             _sh(*ctags)  # Some versions of ctags don't have --exclude
183         except:
184             pass
185
186     try:
187         _sh('cscope', '-R', '-b')
188     except:
189         pass
190 commands.append(tag)
191
192
193 def kill():
194     sudo()
195     for proc in ["ovs-vswitchd", "ovsdb-server"]:
196         if os.path.exists("%s/run/openvswitch/%s.pid" % (RUNDIR, proc)):
197             _sh("ovs-appctl", "-t", proc, "exit", check=False)
198             time.sleep(.1)
199         _sh("killall", "-q", "-2", proc, check=False)
200 commands.append(kill)
201
202
203 def reset():
204     sudo()
205     kill()
206     if os.path.exists(RUNDIR):
207         shutil.rmtree(RUNDIR)
208     for dp in _sh("ovs-dpctl dump-dps", capture=True):
209         _sh("ovs-dpctl", "del-dp", dp.strip())
210 commands.append(reset)
211
212
213 def run():
214     sudo()
215     kill()
216     for d in ["log", "run"]:
217         d = "%s/%s" % (RUNDIR, d)
218         shutil.rmtree(d, ignore_errors=True)
219         os.makedirs(d)
220
221     pki_dir = RUNDIR + "/pki"
222     if not os.path.exists(pki_dir):
223         os.mkdir(pki_dir)
224         os.chdir(pki_dir)
225         _sh("ovs-pki init")
226         _sh("ovs-pki req+sign ovsclient")
227         os.chdir(OVS_SRC)
228
229     if not os.path.exists(RUNDIR + "/conf.db"):
230         _sh("ovsdb-tool", "create", RUNDIR + "/conf.db",
231             OVS_SRC + "/vswitchd/vswitch.ovsschema")
232
233     opts = ["--pidfile", "--log-file"]
234
235     if (options.user == "") or (options.user == "root:root"):
236         _sh("chown", "root:root", "-R", RUNDIR)
237         if '--user' in sys.argv:
238            sys.argv.remove("--user")
239     else:
240         _sh("chown", options.user, "-R", RUNDIR);
241         opts = ["--user", options.user] + opts
242
243     _sh(*(["ovsdb-server",
244            "--remote=punix:%s/run/db.sock" % RUNDIR,
245            "--remote=db:Open_vSwitch,Open_vSwitch,manager_options",
246            "--private-key=db:Open_vSwitch,SSL,private_key",
247            "--certificate=db:Open_vSwitch,SSL,certificate",
248            "--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert",
249            "--detach", "-vconsole:off"] + opts))
250
251     _sh("ovs-vsctl --no-wait --bootstrap set-ssl %s/ovsclient-privkey.pem" \
252         " %s/ovsclient-cert.pem %s/vswitchd.cacert"
253         % (pki_dir, pki_dir, pki_dir))
254     version = _sh("ovs-vsctl --no-wait --version", capture=True)
255     version = version[0].strip().split()[3]
256     root_uuid = _sh("ovs-vsctl --no-wait --bare list Open_vSwitch",
257                     capture=True)[0].strip()
258     _sh("ovs-vsctl --no-wait set Open_vSwitch %s ovs_version=%s"
259         % (root_uuid, version))
260
261     build = BUILD_CLANG if options.clang else BUILD_GCC
262     cmd = [build + "/vswitchd/ovs-vswitchd"]
263
264     if options.dpdk:
265         cmd.append("--dpdk")
266         cmd.extend(options.dpdk)
267         cmd.append("--")
268
269     if options.gdb:
270         cmd = ["gdb", "--args"] + cmd
271     elif options.valgrind:
272         cmd = ["valgrind", "--track-origins=yes", "--leak-check=full",
273                "--suppressions=%s/tests/glibc.supp" % OVS_SRC,
274                "--suppressions=%s/tests/openssl.supp" % OVS_SRC] + cmd
275     else:
276         opts = opts + ["-vconsole:off", "--detach", "--enable-dummy"]
277     _sh(*(cmd + opts))
278 commands.append(run)
279
280
281 def modinst():
282     if not os.path.exists("/lib/modules"):
283         print "Missing modules directory.  Is this a Linux system?"
284         sys.exit(1)
285
286     sudo()
287     try:
288         _sh("rmmod", "openvswitch")
289     except subprocess.CalledProcessError, e:
290         pass  # Module isn't loaded
291
292     try:
293         _sh("rm -f /lib/modules/%s/extra/openvswitch.ko" % uname())
294         _sh("rm -f /lib/modules/%s/extra/vport-*.ko" % uname())
295     except subprocess.CalledProcessError, e:
296         pass  # Module isn't installed
297
298     conf()
299     make()
300     make("modules_install")
301
302     _sh("modprobe", "openvswitch")
303     _sh("dmesg | grep openvswitch | tail -1")
304     _sh("find /lib/modules/%s/ -iname vport-*.ko -exec insmod '{}' \;" % uname())
305 commands.append(modinst)
306
307
308 def env():
309     print "export PATH=" + ENV["PATH"]
310 commands.append(env)
311
312
313 def doc():
314     parser.print_help()
315     print \
316 """
317 This program is designed to help developers build and run Open vSwitch without
318 necessarily needing to know the gory details. Given some basic requirements
319 (described below), it can be used to build and run Open vSwitch, keeping
320 runtime files in the user's home directory.
321
322 Basic Configuration:
323     # This section can be run as a script on ubuntu systems.
324
325     # First install the basic requirements needed to build Open vSwitch.
326     sudo apt-get install git build-essential libtool autoconf pkg-config \\
327             libssl-dev gdb libcap-ng linux-headers-`uname -r`
328
329     # Next clone the Open vSwitch source.
330     git clone https://github.com/openvswitch/ovs.git %(ovs)s
331
332     # Setup environment variables.
333     `%(v)s env`
334
335     # Build the switch.
336     %(v)s conf make
337
338     # Install the kernel module
339     sudo insmod %(ovs)s/datapath/linux/openvswitch.ko
340
341     # If needed, manually load all required vport modules:
342     sudo insmod %(ovs)s/datapath/linux/vport-vxlan.ko
343     sudo insmod %(ovs)s/datapath/linux/vport-geneve.ko
344     [...]
345
346     # Run the switch.
347     %(v)s run
348
349 Commands:
350     conf    - Configure the ovs source.
351     make    - Build the source (must have been configured).
352     check   - Run the unit tests.
353     tag     - Run ctags and cscope over the source.
354     kill    - Kill all running instances of ovs.
355     reset   - Reset any runtime configuration in %(run)s.
356     run     - Run ovs.
357     modinst - Build ovs and install the kernel module.
358     env     - Print the required path environment variable.
359     doc     - Print this message.
360
361 Note:
362     If running as non-root user, "kill", "reset", "run" and "modinst"
363     will always run as the root user, by rerun the commands with "sudo".
364 """ % {"ovs": OVS_SRC, "v": sys.argv[0], "run": RUNDIR}
365     sys.exit(0)
366 commands.append(doc)
367
368 def parse_subargs(option, opt_str, value, parser):
369     subopts = []
370
371     while parser.rargs:
372         dpdkarg = parser.rargs.pop(0)
373         if dpdkarg == "--":
374             break
375         subopts.append(dpdkarg)
376
377     setattr(parser.values, option.dest, subopts)
378
379 def main():
380     global options
381     global parser
382
383     description = "Open vSwitch developer configuration. Try `%prog doc`."
384     cmd_names = [c.__name__ for c in commands]
385     parser = optparse.OptionParser(usage="usage: %prog"
386                                    + " [options] [%s] ..."
387                                    % "|".join(cmd_names),
388                                    description=description)
389
390     group = optparse.OptionGroup(parser, "conf")
391     group.add_option("--disable-Werror", dest="werror", action="store_false",
392                      default=True, help="compile without the Werror flag")
393     group.add_option("--cache-time", dest="cache_time",
394                      action="store_true", help="configure with cached timing")
395     group.add_option("--mandir", dest="mandir", metavar="MANDIR",
396                      help="configure the man documentation install directory")
397     group.add_option("--with-dpdk", dest="with_dpdk", metavar="DPDK_BUILD",
398                      help="built with dpdk libraries located at DPDK_BUILD");
399     parser.add_option_group(group)
400
401     group = optparse.OptionGroup(parser, "Optimization Flags")
402     for i in ["s", "g"] + range(4) + ["fast"]:
403         group.add_option("--O%s" % str(i), dest="optimize",
404                          action="store_const", const=i,
405                          help="compile with -O%s" % str(i))
406     parser.add_option_group(group)
407
408     group = optparse.OptionGroup(parser, "check")
409     group.add_option("-j", "--jobs", dest="jobs", metavar="N", type="int",
410                      help="Run N tests in parallel")
411     group.add_option("--tests", dest="tests", metavar="FILTER",
412                      help="""run specific tests and/or a test category
413                           eg, --tests=\"1-10 megaflow\"""")
414     parser.add_option_group(group)
415
416     group = optparse.OptionGroup(parser, "run")
417     group.add_option("-g", "--gdb", dest="gdb", action="store_true",
418                      help="run ovs-vswitchd under gdb")
419     group.add_option("--valgrind", dest="valgrind", action="store_true",
420                      help="run ovs-vswitchd under valgrind")
421     group.add_option("--dpdk", dest="dpdk", action="callback",
422                      callback=parse_subargs,
423                      help="run ovs-vswitchd with dpdk subopts (ended by --)")
424     group.add_option("--clang", dest="clang", action="store_true",
425                      help="Use binaries built by clang")
426     group.add_option("--user", dest="user", action="store", default="",
427                      help="run all daemons as a non root user")
428
429     parser.add_option_group(group)
430
431     options, args = parser.parse_args()
432
433     for arg in args:
434         if arg not in cmd_names:
435             print "Unknown argument " + arg
436             doc()
437
438     if options.clang:
439         set_path(BUILD_CLANG)
440     else:
441         set_path(BUILD_GCC)
442
443     try:
444         os.chdir(OVS_SRC)
445     except OSError:
446         print "Missing %s." % OVS_SRC
447         doc()
448
449     for arg in args:
450         for cmd in commands:
451             if arg == cmd.__name__:
452                 cmd()
453
454
455 if __name__ == '__main__':
456     main()