selftest.py: Finish conversion to python - now loads without syntax errors.
[samba.git] / selftest / selftest.py
1 #!/usr/bin/python -u
2 # Bootstrap Samba and run a number of tests against it.
3 # Copyright (C) 2005-2012 Jelmer Vernooij <jelmer@samba.org>
4 # Copyright (C) 2007-2009 Stefan Metzmacher <metze@samba.org>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 from cStringIO import StringIO
20 import os
21 import sys
22 import signal
23 import shutil
24 import subprocess
25 import tempfile
26 import time
27 import warnings
28
29 import optparse
30
31 sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
32
33 from selftest import (
34     socket_wrapper,
35     subunithelper,
36     testlist,
37     )
38 from selftest.target import EnvironmentManager
39
40 includes = ()
41 excludes = ()
42
43 def read_excludes(fn):
44     excludes.extend(testlist.read_test_regexes(fn))
45
46 def read_includes(fn):
47     includes.extend(testlist.read_test_regexes(fn))
48
49 parser = optparse.OptionParser("TEST-REGEXES")
50 parser.add_option("--target", type="choice", choices=["samba", "samba3"], default="samba", help="Samba version to target")
51 parser.add_option("--quick", help="run quick overall test")
52 parser.add_option("--verbose", help="be verbose")
53 parser.add_option("--list", help="list available tests")
54 parser.add_option("--socket-wrapper", help="enable socket wrapper")
55 parser.add_option("--socket-wrapper-pcap", help="save traffic to pcap directories", type="str")
56 parser.add_option("--socket-wrapper-keep-pcap", help="keep all pcap files, not just those for tests that failed")
57 parser.add_option("--one", help="abort when the first test fails")
58 parser.add_option("--exclude", action="callback", help="Add file to exclude files", callback=read_excludes)
59 parser.add_option("--include", action="callback", help="Add file to include files", callback=read_includes)
60 parser.add_option("--testenv", help="run a shell in the requested test environment")
61 parser.add_option("--resetup-environment", help="Re-setup environment")
62 parser.add_option("--binary-mapping", help="Map binaries to use", type=str)
63 parser.add_option("--load-list", help="Load list of tests to load from a file", type=str)
64 parser.add_option("--prefix", help="prefix to run tests in", type=str, default="./st")
65 parser.add_option("--srcdir", type=str, default=".", help="source directory")
66 parser.add_option("--bindir", type=str, default="./bin", help="binaries directory")
67 parser.add_option("--testlist", type=str, action="append", help="file to read available tests from")
68 parser.add_option("--ldap", help="back samba onto specified ldap server", choices=["openldap", "fedora-ds"], type="choice")
69
70 opts, args = parser.parse_args()
71
72 subunit_ops = subunithelper.SubunitOps(sys.stdout)
73
74 def pipe_handler(sig):
75     sys.stderr.write("Exiting early because of SIGPIPE.\n")
76     sys.exit(1)
77
78 signal.signal(signal.SIGPIPE, pipe_handler)
79
80 def skip(name):
81     return testlist.find_in_list(excludes, name)
82
83 def setup_pcap(name):
84     if (not opts.socket_wrapper_pcap or
85         not os.environ.get("SOCKET_WRAPPER_PCAP_DIR")):
86         return
87
88     fname = "".join([x for x in name if x.isalnum() or x == '-'])
89
90     pcap_file = os.path.join(
91         os.environ["SOCKET_WRAPPER_PCAP_DIR"], "%s.pcap" % fname)
92
93     socket_wrapper.setup_pcap(pcap_file)
94     return pcap_file
95
96
97 def cleanup_pcap(pcap_file, exit_code):
98     if not opts.socket_wrapper_pcap:
99         return
100     if opts.socket_wrapper_keep_pcap:
101         return
102     if exitcode == 0:
103         return
104     if pcap_file is None:
105         return
106
107     os.unlink(pcap_file)
108
109
110 # expand strings from %ENV
111 def expand_environment_strings(s):
112     # we use a reverse sort so we do the longer ones first
113     for k in sorted(os.environ.keys(), reverse=True):
114         v = os.environ[k]
115         s = s.replace("$%s" % k, v)
116     return s
117
118
119 def run_testsuite(envname, name, cmd, i, totalsuites):
120     pcap_file = setup_pcap(name)
121
122     subunit_ops.start_testsuite(name)
123     subunit_ops.progress_push()
124     subunit_ops.report_time(time.time())
125     try:
126         exitcode = subprocess.call(cmd)
127     except Exception, e:
128         subunit_ops.report_time(time.time())
129         subunit_ops.progress_pop()
130         subunit_ops.progress_pop()
131         subunit_ops.end_testsuite(name, "error", "Unable to run %s: %s" % (cmd, e))
132         sys.exit(1)
133
134     subunit_ops.report_time(time.time())
135     subunit_ops.progress_pop()
136
137     envlog = env_manager.getlog_env(envname)
138     if envlog != "":
139         sys.stdout.write("envlog: %s\n" % envlog)
140
141     sys.stdout.write("command: %s\n" % cmd)
142     sys.stdout.write("expanded command: %s\n" % expand_environment_strings(cmd))
143
144     if exitcode == 0:
145         subunit_ops.end_testsuite(name, "success")
146     else:
147         subunit_ops.end_testsuite(name, "failure", "Exit code was %d" % exitcode)
148
149     cleanup_pcap(pcap_file, exitcode)
150
151     if not opts.socket_wrapper_keep_pcap and pcap_file is not None:
152         sys.stdout.write("PCAP FILE: %s\n" % pcap_file)
153
154     if exitcode != 0 and opts.one:
155         sys.exit(1)
156
157     return exitcode
158
159 if opts.list and opts.testenv:
160     sys.stderr.write("--list and --testenv are mutually exclusive\n")
161     sys.exit(1)
162
163 tests = args
164
165 # quick hack to disable rpc validation when using valgrind - its way too slow
166 if not os.environ.get("VALGRIND"):
167     os.environ["VALIDATE"] = "validate"
168     os.environ["MALLOC_CHECK_"] = "2"
169
170 # make all our python scripts unbuffered
171 os.environ["PYTHONUNBUFFERED"] = "1"
172
173 bindir_abs = os.path.abspath(opts.bindir)
174
175 # Backwards compatibility:
176 if os.environ.get("TEST_LDAP") == "yes":
177     if os.environ.get("FEDORA_DS_ROOT"):
178         ldap = "fedora-ds"
179     else:
180         ldap = "openldap"
181
182 torture_maxtime = int(os.getenv("TORTURE_MAXTIME", "1200"))
183 if opts.ldap:
184     # LDAP is slow
185     torture_maxtime *= 2
186
187 prefix = os.path.normpath(opts.prefix)
188
189 if prefix == "":
190     raise Exception("using an empty prefix isn't allowed")
191
192 # Ensure we have the test prefix around.
193 #
194 # We need restrictive
195 # permissions on this as some subdirectories in this tree will have
196 # wider permissions (ie 0777) and this would allow other users on the
197 # host to subvert the test process.
198 if not os.path.isdir(prefix):
199     os.mkdir(prefix, 0700)
200 else:
201     os.chmod(prefix, 0700)
202
203 prefix_abs = os.path.abspath(prefix)
204 tmpdir_abs = os.path.abspath(os.path.join(prefix, "tmp"))
205 if not os.path.isdir(tmpdir_abs):
206     os.mkdir(tmpdir_abs, 0777)
207
208 srcdir_abs = os.path.abspath(opts.srcdir)
209
210 if prefix_abs == "":
211     raise Exception("using an empty absolute prefix isn't allowed")
212 if prefix_abs == "/":
213     raise Exception("using '/' as absolute prefix isn't allowed")
214
215 os.environ["PREFIX"] = prefix
216 os.environ["KRB5CCNAME"] = os.path.join(prefix, "krb5ticket")
217 os.environ["PREFIX_ABS"] = prefix_abs
218 os.environ["SRCDIR"] = opts.srcdir
219 os.environ["SRCDIR_ABS"] = srcdir_abs
220 os.environ["BINDIR"] = bindir_abs
221
222 tls_enabled = not opts.quick
223 if tls_enabled:
224     os.environ["TLS_ENABLED"] = "yes"
225 else:
226     os.environ["TLS_ENABLED"] = "no"
227
228 def prefix_pathvar(name, newpath):
229     if name in os.environ:
230         os.environ[name] = "%s:%s" % (newpath, os.environ[name])
231     else:
232         os.environ[name] = newpath
233 prefix_pathvar("PKG_CONFIG_PATH", os.path.join(bindir_abs, "pkgconfig"))
234 prefix_pathvar("PYTHONPATH", os.path.join(bindir_abs, "python"))
235
236 if opts.socket_wrapper_keep_pcap:
237     # Socket wrapper keep pcap implies socket wrapper pcap
238     opts.socket_wrapper_pcap = True
239
240 if opts.socket_wrapper_pcap:
241     # Socket wrapper pcap implies socket wrapper
242     opts.socket_wrapper = True
243
244 if opts.socket_wrapper:
245     socket_wrapper_dir = socket_wrapper.setup_dir(os.path.join(prefix_abs, "w"), opts.socket_wrapper_pcap)
246     sys.stdout.write("SOCKET_WRAPPER_DIR=%s\n" % socket_wrapper_dir)
247 elif not opts.list:
248     if os.getuid() != 0:
249         warnings.warn("not using socket wrapper, but also not running as root. Will not be able to listen on proper ports")
250
251 testenv_default = "none"
252
253 if opts.binary_mapping:
254     binary_mapping = dict([l.split(":") for l in opts.binary_mapping.split(",")])
255     os.environ["BINARY_MAPPING"] = opts.binary_mapping
256 else:
257     binary_mapping = {}
258     os.environ["BINARY_MAPPING"] = ""
259
260 # After this many seconds, the server will self-terminate.  All tests
261 # must terminate in this time, and testenv will only stay alive this
262 # long
263
264 server_maxtime = 7500
265 if os.environ.get("SMBD_MAXTIME", ""):
266     server_maxtime = int(os.environ["SMBD_MAXTIME"])
267
268
269 def has_socket_wrapper(bindir):
270     f = StringIO()
271     subprocess.check_call([os.path.join(bindir, "smbd"), "-b"], stdout=f)
272     for l in f.readlines():
273         if "SOCKET_WRAPPER" in l:
274             return True
275     return False
276
277
278 if not opts.list:
279     if opts.target == "samba":
280         if opts.socket_wrapper and not has_socket_wrapper(opts.bindir):
281             sys.stderr.write("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'.  Exiting....\n")
282             sys.exit(1)
283         testenv_default = "dc"
284         from selftest.target.samba import Samba
285         target = Samba(opts.bindir, binary_mapping, ldap, opts.srcdir, server_maxtime)
286     elif opts.target == "samba3":
287         if opts.socket_wrapper and not has_socket_wrapper(opts.bindir):
288             sys.stderr.write("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'.  Exiting....\n")
289             sys.exit(1)
290         testenv_default = "member"
291         from selftest.target.samba3 import Samba3
292         target = Samba3(opts.bindir, binary_mapping, srcdir_abs, server_maxtime)
293
294     env_manager = EnvironmentManager(target)
295
296 interfaces = ",".join([
297     "127.0.0.11/8",
298     "127.0.0.12/8",
299     "127.0.0.13/8",
300     "127.0.0.14/8",
301     "127.0.0.15/8",
302     "127.0.0.16/8"])
303
304 clientdir = os.path.join(prefix_abs, "client")
305
306 conffile = os.path.join(clientdir, "client.conf")
307 os.environ["SMB_CONF_PATH"] = conffile
308
309 def write_clientconf(conffile, clientdir, vars):
310     if not os.path.isdir(clientdir):
311         os.mkdir(clientdir, 0777)
312
313     for n in ["private", "lockdir", "statedir", "cachedir"]:
314         p = os.path.join(clientdir, n)
315         shutil.rmtree(p)
316         os.mkdir(p, 0777)
317
318     # this is ugly, but the ncalrpcdir needs exactly 0755
319     # otherwise tests fail.
320     mask = os.umask(0022)
321
322     for n in ["ncalrpcdir", "ncalrpcdir/np"]:
323         p = os.path.join(clientdir, n)
324         shutil.rmtree(p)
325         os.mkdir(p, 0777)
326     os.umask(mask)
327
328     settings = {
329         "netbios name": "client",
330         "private dir": os.path.join(clientdir, "private"),
331         "lock dir": os.path.join(clientdir, "lockdir"),
332         "state directory": os.path.join(clientdir, "statedir"),
333         "cache directory": os.path.join(clientdir, "cachedir"),
334         "ncalrpc dir": os.path.join(clientdir, "ncalrpcdir"),
335         "name resolve order": "file bcast",
336         "panic action": os.path.join(os.path.dirname(__file__), "gdb_backtrace \%d"),
337         "max xmit": "32K",
338         "notify:inotify": "false",
339         "ldb:nosync": "true",
340         "system:anonymous": "true",
341         "client lanman auth": "Yes",
342         "log level": "1",
343         "torture:basedir": clientdir,
344     #We don't want to pass our self-tests if the PAC code is wrong
345         "gensec:require_pac": "true",
346         "resolv:host file": os.path.join(prefix_abs, "dns_host_file"),
347     #We don't want to run 'speed' tests for very long
348         "torture:timelimit": "1",
349         }
350
351     if "DOMAIN" in vars:
352         settings["workgroup"] = vars["DOMAIN"]
353     if "REALM" in vars:
354         settings["realm"] = vars["REALM"]
355     if opts.socket_wrapper:
356         settings["interfaces"] = interfaces
357
358     f = open(conffile, 'w')
359     try:
360         f.write("[global]\n")
361         for item in settings.iteritems():
362             f.write("\t%s = %s\n" % item)
363     finally:
364         f.close()
365
366 todo = []
367
368 if testlists == []:
369     sys.stderr.write("No testlists specified\n")
370     sys.exit(1)
371
372 os.environ["SELFTEST_PREFIX"] = prefix_abs
373 os.environ["SELFTEST_TMPDIR"] = tmpdir_abs
374 os.environ["TEST_DATA_PREFIX"] = tmpdir_abs
375 if opts.socket_wrapper:
376     os.environ["SELFTEST_INTERFACES"] = interfaces
377 else:
378     os.environ["SELFTEST_INTERFACES"] = ""
379 if opts.verbose:
380     os.environ["SELFTEST_VERBOSE"] = "1"
381 else:
382     os.environ["SELFTEST_VERBOSE"] = ""
383 if opts.quick:
384     os.environ["SELFTEST_QUICK"] = "1"
385 else:
386     os.environ["SELFTEST_QUICK"] = ""
387 os.environ["SELFTEST_MAXTIME"] = str(torture_maxtime)
388
389 available = []
390 for fn in testlists:
391     for testsuite in testlist.read_testlist(fn):
392         if not should_run_test(tests, testsuite):
393             continue
394         name = testsuite[0]
395         if includes is not None and testlist.find_in_list(includes, name) is not None:
396             continue
397         available.append(testsuite)
398
399 if opts.load_list:
400     restricted_mgr = testlist.RestrictedTestManager.from_path(opts.load_list)
401 else:
402     restricted_mgr = None
403
404
405 for testsuite in available:
406     name = testsuite[0]
407     skipreason = skip(name)
408     if restricted_mgr is not None:
409         match = restricted_mgr.should_run_testsuite(name)
410         if match == []:
411             continue
412     else:
413         match = None
414     if skipreason is not None:
415         if not opts.list:
416             subunit_ops.skip_testsuite(name, skipreason)
417     else:
418         todo.append(testsuite + (match,))
419
420 if restricted_mgr is not None:
421     for name in restricted_mgr.iter_unused():
422         sys.stdout.write("No test or testsuite found matching %s\n" % name)
423 if todo == []:
424     sys.stderr.write("No tests to run\n")
425     sys.exit(1)
426
427 suitestotal = len(todo)
428
429 if not opts.list:
430     subunit_ops.progress(suitestotal)
431     subunit_ops.report_time(time.time())
432
433 i = 0
434
435 exported_envvars = [
436     # domain stuff
437     "DOMAIN",
438     "REALM",
439
440     # domain controller stuff
441     "DC_SERVER",
442     "DC_SERVER_IP",
443     "DC_NETBIOSNAME",
444     "DC_NETBIOSALIAS",
445
446     # domain member
447     "MEMBER_SERVER",
448     "MEMBER_SERVER_IP",
449     "MEMBER_NETBIOSNAME",
450     "MEMBER_NETBIOSALIAS",
451
452     # rpc proxy controller stuff
453     "RPC_PROXY_SERVER",
454     "RPC_PROXY_SERVER_IP",
455     "RPC_PROXY_NETBIOSNAME",
456     "RPC_PROXY_NETBIOSALIAS",
457
458     # domain controller stuff for Vampired DC
459     "VAMPIRE_DC_SERVER",
460     "VAMPIRE_DC_SERVER_IP",
461     "VAMPIRE_DC_NETBIOSNAME",
462     "VAMPIRE_DC_NETBIOSALIAS",
463
464     # server stuff
465     "SERVER",
466     "SERVER_IP",
467     "NETBIOSNAME",
468     "NETBIOSALIAS",
469
470     # user stuff
471     "USERNAME",
472     "USERID",
473     "PASSWORD",
474     "DC_USERNAME",
475     "DC_PASSWORD",
476
477     # misc stuff
478     "KRB5_CONFIG",
479     "WINBINDD_SOCKET_DIR",
480     "WINBINDD_PRIV_PIPE_DIR",
481     "NMBD_SOCKET_DIR",
482     "LOCAL_PATH"
483 ]
484
485 def handle_sigdie(signame):
486     env_manager.teardown_all()
487     sys.stderr.write("Received signal %s" % signame)
488     sys.exit(1)
489
490 signal.signal(signal.SIGINT, handle_sigdie)
491 signal.signal(signal.SIGQUIT, handle_sigdie)
492 signal.signal(signal.SIGTERM, handle_sigdie)
493
494 def exported_envvars_str(testenv_vars):
495     out = ""
496
497     for n in exported_envvars:
498         if not n in testenv_vars:
499             continue
500         out += "%s=%s\n" % (n, testenv_vars[n])
501
502     return out
503
504
505 def switch_env(name, prefix):
506     if ":" in name:
507         (envname, option) = name.split(":", 1)
508     else:
509         envname = name
510         option = "client"
511
512     env = env_manager.setup_env(envname, prefix)
513
514     testenv_vars = env.get_vars()
515
516     if option == "local":
517         socket_wrapper.set_default_iface(testenv_vars["SOCKET_WRAPPER_DEFAULT_IFACE"])
518         os.environ["SMB_CONF_PATH"] = testenv_vars["SERVERCONFFILE"]
519     elif option == "client":
520         socket_wrapper.set_default_iface(11)
521         write_clientconf(conffile, clientdir, testenv_vars)
522         os.environ["SMB_CONF_PATH"] = conffile
523     else:
524         raise Exception("Unknown option[%s] for envname[%s]" % (option,
525             envname))
526
527     for name in exported_envvars:
528         if name in testenv_vars:
529             os.environ[name] = testenv_vars[name]
530         else:
531             del os.environ[name]
532
533     return testenv_vars
534
535 # This 'global' file needs to be empty when we start
536 os.unlink(os.path.join(prefix_abs, "dns_host_file"))
537
538 if opts.testenv:
539     testenv_name = os.environ.get("SELFTEST_TESTENV", testenv_default)
540
541     testenv_vars = switch_env(testenv_name, prefix)
542
543     os.environ["PIDDIR"] = testenv_vars["PIDDIR"]
544     os.environ["ENVNAME"] = testenv_name
545
546     envvarstr = exported_envvars_str(testenv_vars)
547
548     term = os.environ.get("TERMINAL", "xterm -e")
549     cmd = """'echo -e "
550 Welcome to the Samba4 Test environment '%(testenv_name)'
551
552 This matches the client environment used in make test
553 server is pid `cat \$PIDDIR/samba.pid`
554
555 Some useful environment variables:
556 TORTURE_OPTIONS=\$TORTURE_OPTIONS
557 SMB_CONF_PATH=\$SMB_CONF_PATH
558
559 $envvarstr
560 \" && LD_LIBRARY_PATH=%(LD_LIBRARY_PATH)s $(SHELL)'""" % {
561         "testenv_name": testenv_name,
562         "LD_LIBRARY_PATH": os.environ["LD_LIBRARY_PATH"]}
563     subprocess.call(term + ' ' + cmd)
564     env_manager.teardown_env(testenv_name)
565 elif opts.list:
566     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
567         if not "$LISTOPT" in cmd:
568             warnings.warn("Unable to list tests in %s" % name)
569             continue
570
571         cmd = cmd.replace("$LISTOPT", "--list")
572
573         exitcode = subprocess.call(cmd)
574
575         if exitcode != 0:
576             sys.stderr.write("%s exited with exit code %s\n" % (cmd, exitcode))
577             sys.exit(1)
578 else:
579     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
580         try:
581             envvars = switch_env(envname, prefix)
582         except Exception:
583             subunit_ops.start_testsuite(name);
584             subunit_ops.end_testsuite(name, "error",
585                 "unable to set up environment %s" % envname);
586             continue
587         if envvars is None:
588             subunit_ops.start_testsuite(name);
589             subunit_ops.end_testsuite(name, "skip",
590                 "environment is unknown in this test backend - skipping" % envname)
591             continue
592
593         # Generate a file with the individual tests to run, if the
594         # test runner for this test suite supports it.
595         if subtests is not None:
596             if supports_loadfile:
597                 (fd, listid_file) = tempfile.mkstemp()
598                 # FIXME: Remove tempfile afterwards
599                 f = os.fdopen(fd)
600                 try:
601                     for test in subtests:
602                         f.write(test+"\n")
603                 finally:
604                     f.close()
605                 cmd = cmd.replace("$LOADLIST", "--load-list=%s" % listid_file)
606             elif supports_idlist:
607                 cmd += " ".join(subtests)
608
609         run_testsuite(envname, name, cmd, i, suitestotal)
610
611         if opts.resetup_env:
612             env_manager.teardown_env(envname)
613
614 sys.stdout.write("\n")
615
616 env_manager.teardown_all()
617
618 # if there were any valgrind failures, show them
619 for fn in os.listdir(prefix):
620     if fn.startswith("valgrind.log"):
621         sys.stdout.write("VALGRIND FAILURE\n")
622         f = open(os.path.join(prefix, fn), 'r')
623         try:
624             sys.stdout.write(f.read())
625         finally:
626             f.close()
627
628 sys.exit(0)