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