autoserv.py revision 75cdfee87bfaa3cf3f9860832b228a6d32aaed2f
1#!/usr/bin/python -u
2# Copyright 2007-2008 Martin J. Bligh <mbligh@google.com>, Google Inc.
3# Released under the GPL v2
4
5"""
6Run an control file through the server side engine
7"""
8
9import sys, os, re, traceback, signal, time, logging
10
11import common
12from autotest_lib.server import server_job, utils, autoserv_parser, autotest
13from autotest_lib.server import server_logging_config
14from autotest_lib.client.common_lib import pidfile, logging_manager
15
16def run_autoserv(pid_file_manager, results, parser):
17    # send stdin to /dev/null
18    dev_null = os.open(os.devnull, os.O_RDONLY)
19    os.dup2(dev_null, sys.stdin.fileno())
20    os.close(dev_null)
21
22    # Create separate process group
23    os.setpgrp()
24
25    # Implement SIGTERM handler
26    def handle_sigint(signum, frame):
27        if pid_file_manager:
28            pid_file_manager.close_file(1, signal.SIGTERM)
29        os.killpg(os.getpgrp(), signal.SIGKILL)
30
31    # Set signal handler
32    signal.signal(signal.SIGTERM, handle_sigint)
33
34    # Get a useful value for running 'USER'
35    realuser = os.environ.get('USER')
36    if not realuser:
37        realuser = 'anonymous'
38
39    if parser.options.machines:
40        machines = parser.options.machines.replace(',', ' ').strip().split()
41    else:
42        machines = []
43    machines_file = parser.options.machines_file
44    label = parser.options.label
45    group_name = parser.options.group_name
46    user = parser.options.user
47    client = parser.options.client
48    server = parser.options.server
49    install_before = parser.options.install_before
50    install_after = parser.options.install_after
51    verify = parser.options.verify
52    repair = parser.options.repair
53    cleanup = parser.options.cleanup
54    no_tee = parser.options.no_tee
55    parse_job = parser.options.parse_job
56    host_protection = parser.options.host_protection
57    ssh_user = parser.options.ssh_user
58    ssh_port = parser.options.ssh_port
59    ssh_pass = parser.options.ssh_pass
60    collect_crashinfo = parser.options.collect_crashinfo
61
62    # can't be both a client and a server side test
63    if client and server:
64        print "Can not specify a test as both server and client!"
65        sys.exit(1)
66
67    if len(parser.args) < 1 and not (verify or repair or cleanup
68                                     or collect_crashinfo):
69        print parser.parser.print_help()
70        sys.exit(1)
71
72    # We have a control file unless it's just a verify/repair/cleanup job
73    if len(parser.args) > 0:
74        control = parser.args[0]
75    else:
76        control = None
77
78    if machines_file:
79        machines = []
80        for m in open(machines_file, 'r').readlines():
81            # remove comments, spaces
82            m = re.sub('#.*', '', m).strip()
83            if m:
84                machines.append(m)
85        print "Read list of machines from file: %s" % machines_file
86        print ','.join(machines)
87
88    if machines:
89        for machine in machines:
90            if not machine or re.search('\s', machine):
91                print "Invalid machine %s" % str(machine)
92                sys.exit(1)
93        machines = list(set(machines))
94        machines.sort()
95
96    if group_name and len(machines) < 2:
97        print ("-G %r may only be supplied with more than one machine."
98               % group_name)
99        sys.exit(1)
100
101    job = server_job.server_job(control, parser.args[1:], results, label,
102                                user, machines, client, parse_job,
103                                ssh_user, ssh_port, ssh_pass,
104                                group_name=group_name)
105    job.logging.start_logging()
106
107    # perform checks
108    job.precheck()
109
110    # run the job
111    exit_code = 0
112    try:
113        try:
114            if repair:
115                job.repair(host_protection)
116            elif verify:
117                job.verify()
118            else:
119                job.run(cleanup, install_before, install_after,
120                        only_collect_crashinfo=collect_crashinfo)
121        finally:
122            while job.hosts:
123                host = job.hosts.pop()
124                host.close()
125    except:
126        exit_code = 1
127        traceback.print_exc()
128
129    if pid_file_manager:
130        pid_file_manager.num_tests_failed = job.num_tests_failed
131        pid_file_manager.close_file(exit_code)
132    job.cleanup_parser()
133
134    sys.exit(exit_code)
135
136
137def main():
138    # grab the parser
139    parser = autoserv_parser.autoserv_parser
140    parser.parse_args()
141
142    if len(sys.argv) == 1:
143        parser.parser.print_help()
144        sys.exit(1)
145
146    if parser.options.no_logging:
147        results = None
148    else:
149        results = parser.options.results
150        if not results:
151            results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S')
152        results  = os.path.abspath(results)
153        resultdir_exists = os.path.exists(os.path.join(results, 'control.srv'))
154        if not parser.options.collect_crashinfo and resultdir_exists:
155            error = "Error: results directory already exists: %s\n" % results
156            sys.stderr.write(error)
157            sys.exit(1)
158
159        # Now that we certified that there's no leftover results dir from
160        # previous jobs, lets create the result dir since the logging system
161        # needs to create the log file in there.
162        if not os.path.isdir(results):
163            os.makedirs(results)
164
165    logging_manager.configure_logging(
166            server_logging_config.ServerLoggingConfig(), results_dir=results,
167            use_console=not parser.options.no_tee)
168    if results:
169        logging.info("Results placed in %s" % results)
170
171    if parser.options.write_pidfile:
172        if parser.options.collect_crashinfo:
173            pidfile_label = 'collect_crashinfo'
174        else:
175            pidfile_label = 'autoserv'
176        pid_file_manager = pidfile.PidFileManager(pidfile_label, results)
177        pid_file_manager.open_file()
178    else:
179        pid_file_manager = None
180
181    autotest.BaseAutotest.set_install_in_tmpdir(
182        parser.options.install_in_tmpdir)
183
184    exit_code = 0
185    try:
186        try:
187            run_autoserv(pid_file_manager, results, parser)
188        except SystemExit, e:
189            exit_code = e.code
190        except:
191            traceback.print_exc()
192            # If we don't know what happened, we'll classify it as
193            # an 'abort' and return 1.
194            exit_code = 1
195    finally:
196        if pid_file_manager:
197            pid_file_manager.close_file(exit_code)
198    sys.exit(exit_code)
199
200
201if __name__ == '__main__':
202    main()
203