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