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