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