winbindd: keep client list sorted by access time
[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 import atexit
20 from cStringIO import StringIO
21 import os
22 import sys
23 import signal
24 import subprocess
25 from samba import subunit
26 import traceback
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.client import write_clientconf
39 from selftest.run import (
40     expand_command_list,
41     expand_command_run,
42     exported_envvars_str,
43     now,
44     run_testsuite_command,
45     )
46 from selftest.target import (
47     EnvironmentManager,
48     NoneTarget,
49     UnsupportedEnvironment,
50     )
51
52 includes = ()
53 excludes = ()
54
55 def read_excludes(fn):
56     excludes.extend(testlist.read_test_regexes(fn))
57
58 def read_includes(fn):
59     includes.extend(testlist.read_test_regexes(fn))
60
61 parser = optparse.OptionParser("TEST-REGEXES")
62 parser.add_option("--target", type="choice", choices=["samba", "samba3", "none"], default="samba", help="Samba version to target")
63 parser.add_option("--quick", help="run quick overall test", action="store_true", default=False)
64 parser.add_option("--list", help="list available tests", action="store_true", default=False)
65 parser.add_option("--socket-wrapper", help="enable socket wrapper", action="store_true", default=False)
66 parser.add_option("--socket-wrapper-pcap", help="save traffic to pcap directories", type="str")
67 parser.add_option("--socket-wrapper-keep-pcap", help="keep all pcap files, not just those for tests that failed", action="store_true", default=False)
68 parser.add_option("--one", help="abort when the first test fails", action="store_true", default=False)
69 parser.add_option("--exclude", action="callback", help="Add file to exclude files", callback=read_excludes)
70 parser.add_option("--include", action="callback", help="Add file to include files", callback=read_includes)
71 parser.add_option("--testenv", help="run a shell in the requested test environment", action="store_true", default=False)
72 parser.add_option("--resetup-environment", help="Re-setup environment", action="store_true", default=False)
73 parser.add_option("--load-list", help="Load list of tests to load from a file", type=str)
74 parser.add_option("--prefix", help="prefix to run tests in", type=str, default="./st")
75 parser.add_option("--srcdir", type=str, default=".", help="source directory")
76 parser.add_option("--bindir", type=str, default="./bin", help="binaries directory")
77 parser.add_option("--testlist", type=str, action="append", help="file to read available tests from")
78 parser.add_option("--ldap", help="back samba onto specified ldap server", choices=["openldap", "fedora-ds"], type="choice")
79
80 opts, args = parser.parse_args()
81
82 subunit_ops = subunithelper.SubunitOps(sys.stdout)
83
84 def handle_signal(sig, frame):
85     sys.stderr.write("Exiting early because of signal %s.\n" % sig)
86     sys.exit(1)
87
88 for sig in (signal.SIGINT, signal.SIGQUIT, signal.SIGTERM, signal.SIGPIPE):
89     signal.signal(sig, handle_signal)
90
91 def skip(name):
92     return testlist.find_in_list(excludes, name)
93
94 def setup_pcap(name):
95     if (not opts.socket_wrapper_pcap or
96         not os.environ.get("SOCKET_WRAPPER_PCAP_DIR")):
97         return
98
99     fname = "".join([x for x in name if x.isalnum() or x == '-'])
100
101     pcap_file = os.path.join(
102         os.environ["SOCKET_WRAPPER_PCAP_DIR"], "%s.pcap" % fname)
103
104     socket_wrapper.setup_pcap(pcap_file)
105     return pcap_file
106
107
108 def cleanup_pcap(pcap_file, exit_code):
109     if not opts.socket_wrapper_pcap:
110         return
111     if opts.socket_wrapper_keep_pcap:
112         return
113     if exitcode == 0:
114         return
115     if pcap_file is None:
116         return
117
118     os.unlink(pcap_file)
119
120
121 def run_testsuite(name, cmd, subunit_ops, env=None):
122     """Run a single testsuite.
123
124     :param env: Environment to run in
125     :param name: Name of the testsuite
126     :param cmd: Name of the (fully expanded) command to run
127     :return: exitcode of the command
128     """
129     pcap_file = setup_pcap(name)
130
131     exitcode = run_testsuite_command(name, cmd, subunit_ops, env)
132     if exitcode is None:
133         sys.exit(1)
134
135     cleanup_pcap(pcap_file, exitcode)
136
137     if not opts.socket_wrapper_keep_pcap and pcap_file is not None:
138         sys.stdout.write("PCAP FILE: %s\n" % pcap_file)
139
140     if exitcode != 0 and opts.one:
141         sys.exit(1)
142
143     return exitcode
144
145
146 if opts.list and opts.testenv:
147     sys.stderr.write("--list and --testenv are mutually exclusive\n")
148     sys.exit(1)
149
150 tests = args
151
152 # quick hack to disable rpc validation when using valgrind - it is way too slow
153 if not os.environ.get("VALGRIND"):
154     os.environ["VALIDATE"] = "validate"
155     os.environ["MALLOC_CHECK_"] = "3"
156
157 # make all our python scripts unbuffered
158 os.environ["PYTHONUNBUFFERED"] = "1"
159
160 bindir_abs = os.path.abspath(opts.bindir)
161
162 # Backwards compatibility:
163 if os.environ.get("TEST_LDAP") == "yes":
164     if os.environ.get("FEDORA_DS_ROOT"):
165         ldap = "fedora-ds"
166     else:
167         ldap = "openldap"
168
169 torture_maxtime = int(os.getenv("TORTURE_MAXTIME", "1200"))
170 if opts.ldap:
171     # LDAP is slow
172     torture_maxtime *= 2
173
174 prefix = os.path.normpath(opts.prefix)
175
176 # Ensure we have the test prefix around.
177 #
178 # We need restrictive permissions on this as some subdirectories in this tree
179 # will have wider permissions (ie 0777) and this would allow other users on the
180 # host to subvert the test process.
181 if not os.path.isdir(prefix):
182     os.mkdir(prefix, 0700)
183 else:
184     os.chmod(prefix, 0700)
185
186 prefix_abs = os.path.abspath(prefix)
187 tmpdir_abs = os.path.abspath(os.path.join(prefix_abs, "tmp"))
188 if not os.path.isdir(tmpdir_abs):
189     os.mkdir(tmpdir_abs, 0777)
190
191 srcdir_abs = os.path.abspath(opts.srcdir)
192
193 if prefix_abs == "/":
194     raise Exception("using '/' as absolute prefix is a bad idea")
195
196 os.environ["PREFIX"] = prefix
197 os.environ["KRB5CCNAME"] = os.path.join(prefix, "krb5ticket")
198 os.environ["PREFIX_ABS"] = prefix_abs
199 os.environ["SRCDIR"] = opts.srcdir
200 os.environ["SRCDIR_ABS"] = srcdir_abs
201 os.environ["BINDIR"] = bindir_abs
202
203 tls_enabled = not opts.quick
204 if tls_enabled:
205     os.environ["TLS_ENABLED"] = "yes"
206 else:
207     os.environ["TLS_ENABLED"] = "no"
208
209 def prefix_pathvar(name, newpath):
210     if name in os.environ:
211         os.environ[name] = "%s:%s" % (newpath, os.environ[name])
212     else:
213         os.environ[name] = newpath
214 prefix_pathvar("PKG_CONFIG_PATH", os.path.join(bindir_abs, "pkgconfig"))
215 prefix_pathvar("PYTHONPATH", os.path.join(bindir_abs, "python"))
216
217 if opts.socket_wrapper_keep_pcap:
218     # Socket wrapper keep pcap implies socket wrapper pcap
219     opts.socket_wrapper_pcap = True
220
221 if opts.socket_wrapper_pcap:
222     # Socket wrapper pcap implies socket wrapper
223     opts.socket_wrapper = True
224
225 if opts.socket_wrapper:
226     socket_wrapper_dir = socket_wrapper.setup_dir(os.path.join(prefix_abs, "w"), opts.socket_wrapper_pcap)
227     sys.stdout.write("SOCKET_WRAPPER_DIR=%s\n" % socket_wrapper_dir)
228 elif not opts.list:
229     if os.getuid() != 0:
230         warnings.warn("not using socket wrapper, but also not running as root. Will not be able to listen on proper ports")
231
232 testenv_default = "none"
233
234 # After this many seconds, the server will self-terminate.  All tests
235 # must terminate in this time, and testenv will only stay alive this
236 # long
237
238 if os.environ.get("SMBD_MAXTIME", ""):
239     server_maxtime = int(os.environ["SMBD_MAXTIME"])
240 else:
241     server_maxtime = 7500
242
243
244 def has_socket_wrapper(bindir):
245     """Check if Samba has been built with socket wrapper support.
246     """
247     f = StringIO()
248     subprocess.check_call([os.path.join(bindir, "smbd"), "-b"], stdout=f)
249     for l in f.readlines():
250         if "SOCKET_WRAPPER" in l:
251             return True
252     return False
253
254
255 if not opts.list:
256     if opts.target == "samba":
257         if opts.socket_wrapper and not has_socket_wrapper(opts.bindir):
258             sys.stderr.write("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'.  Exiting....\n")
259             sys.exit(1)
260         testenv_default = "ad_dc_ntvfs"
261         from selftest.target.samba import Samba
262         target = Samba(opts.bindir, ldap, opts.srcdir, server_maxtime)
263     elif opts.target == "samba3":
264         if opts.socket_wrapper and not has_socket_wrapper(opts.bindir):
265             sys.stderr.write("You must include --enable-socket-wrapper when compiling Samba in order to execute 'make test'.  Exiting....\n")
266             sys.exit(1)
267         testenv_default = "member"
268         from selftest.target.samba3 import Samba3
269         target = Samba3(opts.bindir, srcdir_abs, server_maxtime)
270     elif opts.target == "none":
271         testenv_default = "none"
272         target = NoneTarget()
273
274     env_manager = EnvironmentManager(target)
275     atexit.register(env_manager.teardown_all)
276
277 interfaces = ",".join([
278     "127.0.0.11/8",
279     "127.0.0.12/8",
280     "127.0.0.13/8",
281     "127.0.0.14/8",
282     "127.0.0.15/8",
283     "127.0.0.16/8"])
284
285 clientdir = os.path.join(prefix_abs, "client")
286
287 conffile = os.path.join(clientdir, "client.conf")
288 os.environ["SMB_CONF_PATH"] = conffile
289
290 todo = []
291
292 if not opts.testlist:
293     sys.stderr.write("No testlists specified\n")
294     sys.exit(1)
295
296 os.environ["SELFTEST_PREFIX"] = prefix_abs
297 os.environ["SELFTEST_TMPDIR"] = tmpdir_abs
298 os.environ["TEST_DATA_PREFIX"] = tmpdir_abs
299 if opts.socket_wrapper:
300     os.environ["SELFTEST_INTERFACES"] = interfaces
301 else:
302     os.environ["SELFTEST_INTERFACES"] = ""
303 if opts.quick:
304     os.environ["SELFTEST_QUICK"] = "1"
305 else:
306     os.environ["SELFTEST_QUICK"] = ""
307 os.environ["SELFTEST_MAXTIME"] = str(torture_maxtime)
308
309
310 available = []
311 for fn in opts.testlist:
312     for testsuite in testlist.read_testlist_file(fn):
313         if not testlist.should_run_test(tests, testsuite):
314             continue
315         name = testsuite[0]
316         if (includes is not None and
317             testlist.find_in_list(includes, name) is not None):
318             continue
319         available.append(testsuite)
320
321 if opts.load_list:
322     restricted_mgr = testlist.RestrictedTestManager.from_path(opts.load_list)
323 else:
324     restricted_mgr = None
325
326 for testsuite in available:
327     name = testsuite[0]
328     skipreason = skip(name)
329     if restricted_mgr is not None:
330         match = restricted_mgr.should_run_testsuite(name)
331         if match == []:
332             continue
333     else:
334         match = None
335     if skipreason is not None:
336         if not opts.list:
337             subunit_ops.skip_testsuite(name, skipreason)
338     else:
339         todo.append(testsuite + (match,))
340
341 if restricted_mgr is not None:
342     for name in restricted_mgr.iter_unused():
343         sys.stdout.write("No test or testsuite found matching %s\n" % name)
344 if todo == []:
345     sys.stderr.write("No tests to run\n")
346     sys.exit(1)
347
348 suitestotal = len(todo)
349
350 if not opts.list:
351     subunit_ops.progress(suitestotal, subunit.PROGRESS_SET)
352     subunit_ops.time(now())
353
354 exported_envvars = [
355     # domain stuff
356     "DOMAIN",
357     "REALM",
358
359     # domain controller stuff
360     "DC_SERVER",
361     "DC_SERVER_IP",
362     "DC_NETBIOSNAME",
363     "DC_NETBIOSALIAS",
364
365     # domain member
366     "MEMBER_SERVER",
367     "MEMBER_SERVER_IP",
368     "MEMBER_NETBIOSNAME",
369     "MEMBER_NETBIOSALIAS",
370
371     # rpc proxy controller stuff
372     "RPC_PROXY_SERVER",
373     "RPC_PROXY_SERVER_IP",
374     "RPC_PROXY_NETBIOSNAME",
375     "RPC_PROXY_NETBIOSALIAS",
376
377     # domain controller stuff for Vampired DC
378     "VAMPIRE_DC_SERVER",
379     "VAMPIRE_DC_SERVER_IP",
380     "VAMPIRE_DC_NETBIOSNAME",
381     "VAMPIRE_DC_NETBIOSALIAS",
382
383     # domain controller stuff for Vampired DC
384     "PROMOTED_DC_SERVER",
385     "PROMOTED_DC_SERVER_IP",
386     "PROMOTED_DC_NETBIOSNAME",
387     "PROMOTED_DC_NETBIOSALIAS",
388
389     # server stuff
390     "SERVER",
391     "SERVER_IP",
392     "NETBIOSNAME",
393     "NETBIOSALIAS",
394
395     # user stuff
396     "USERNAME",
397     "USERID",
398     "PASSWORD",
399     "DC_USERNAME",
400     "DC_PASSWORD",
401
402     # misc stuff
403     "KRB5_CONFIG",
404     "WINBINDD_SOCKET_DIR",
405     "WINBINDD_PRIV_PIPE_DIR",
406     "NMBD_SOCKET_DIR",
407     "LOCAL_PATH"
408 ]
409
410 def switch_env(name, prefix):
411     if ":" in name:
412         (envname, option) = name.split(":", 1)
413     else:
414         envname = name
415         option = "client"
416
417     env = env_manager.setup_env(envname, prefix)
418
419     testenv_vars = env.get_vars()
420
421     if option == "local":
422         socket_wrapper.set_default_iface(testenv_vars["SOCKET_WRAPPER_DEFAULT_IFACE"])
423         os.environ["SMB_CONF_PATH"] = testenv_vars["SERVERCONFFILE"]
424     elif option == "client":
425         socket_wrapper.set_default_iface(11)
426         write_clientconf(conffile, clientdir, testenv_vars)
427         os.environ["SMB_CONF_PATH"] = conffile
428     else:
429         raise Exception("Unknown option[%s] for envname[%s]" % (option,
430             envname))
431
432     for name in exported_envvars:
433         if name in testenv_vars:
434             os.environ[name] = testenv_vars[name]
435         elif name in os.environ:
436             del os.environ[name]
437
438     return env
439
440 # This 'global' file needs to be empty when we start
441 dns_host_file_path = os.path.join(prefix_abs, "dns_host_file")
442 if os.path.exists(dns_host_file_path):
443     os.unlink(dns_host_file_path)
444
445 if opts.testenv:
446     testenv_name = os.environ.get("SELFTEST_TESTENV", testenv_default)
447
448     env = switch_env(testenv_name, prefix)
449     testenv_vars = env.get_vars()
450
451     os.environ["PIDDIR"] = testenv_vars["PIDDIR"]
452     os.environ["ENVNAME"] = testenv_name
453
454     envvarstr = exported_envvars_str(testenv_vars, exported_envvars)
455
456     term = os.environ.get("TERMINAL", "xterm -e")
457     cmd = """'echo -e "
458 Welcome to the Samba4 Test environment '%(testenv_name)'
459
460 This matches the client environment used in make test
461 server is pid `cat \$PIDDIR/samba.pid`
462
463 Some useful environment variables:
464 TORTURE_OPTIONS=\$TORTURE_OPTIONS
465 SMB_CONF_PATH=\$SMB_CONF_PATH
466
467 $envvarstr
468 \" && LD_LIBRARY_PATH=%(LD_LIBRARY_PATH)s $(SHELL)'""" % {
469         "testenv_name": testenv_name,
470         "LD_LIBRARY_PATH": os.environ["LD_LIBRARY_PATH"]}
471     subprocess.call(term + ' ' + cmd, shell=True)
472     env_manager.teardown_env(testenv_name)
473 elif opts.list:
474     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
475         cmd = expand_command_list(cmd)
476         if cmd is None:
477             warnings.warn("Unable to list tests in %s" % name)
478             continue
479
480         exitcode = subprocess.call(cmd, shell=True)
481
482         if exitcode != 0:
483             sys.stderr.write("%s exited with exit code %s\n" % (cmd, exitcode))
484             sys.exit(1)
485 else:
486     for (name, envname, cmd, supports_loadfile, supports_idlist, subtests) in todo:
487         try:
488             env = switch_env(envname, prefix)
489         except UnsupportedEnvironment:
490             subunit_ops.start_testsuite(name)
491             subunit_ops.end_testsuite(name, "skip",
492                 "environment %s is unknown in this test backend - skipping" % envname)
493             continue
494         except Exception, e:
495             subunit_ops.start_testsuite(name)
496             traceback.print_exc()
497             subunit_ops.end_testsuite(name, "error",
498                 "unable to set up environment %s: %s" % (envname, e))
499             continue
500
501         cmd, tmpf = expand_command_run(cmd, supports_loadfile, supports_idlist,
502             subtests)
503
504         run_testsuite(name, cmd, subunit_ops, env=env)
505
506         if tmpf is not None:
507             os.remove(tmpf)
508
509         if opts.resetup_environment:
510             env_manager.teardown_env(envname)
511     env_manager.teardown_all()
512
513 sys.stdout.write("\n")
514
515 # if there were any valgrind failures, show them
516 for fn in os.listdir(prefix):
517     if fn.startswith("valgrind.log"):
518         sys.stdout.write("VALGRIND FAILURE\n")
519         f = open(os.path.join(prefix, fn), 'r')
520         try:
521             sys.stdout.write(f.read())
522         finally:
523             f.close()
524
525 sys.exit(0)