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