autoserv revision c229956e546e1398cce5ca80203363b303b8fbe7
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_logging_config
13from autotest_lib.server import server_job, utils, autoserv_parser, autotest
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_sigterm(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_sigterm)
33
34    if parser.options.machines:
35        machines = parser.options.machines.replace(',', ' ').strip().split()
36    else:
37        machines = []
38    machines_file = parser.options.machines_file
39    label = parser.options.label
40    group_name = parser.options.group_name
41    user = parser.options.user
42    client = parser.options.client
43    server = parser.options.server
44    install_before = parser.options.install_before
45    install_after = parser.options.install_after
46    verify = parser.options.verify
47    repair = parser.options.repair
48    cleanup = parser.options.cleanup
49    no_tee = parser.options.no_tee
50    parse_job = parser.options.parse_job
51    host_protection = parser.options.host_protection
52    ssh_user = parser.options.ssh_user
53    ssh_port = parser.options.ssh_port
54    ssh_pass = parser.options.ssh_pass
55    collect_crashinfo = parser.options.collect_crashinfo
56
57    # can't be both a client and a server side test
58    if client and server:
59        print "Can not specify a test as both server and client!"
60        sys.exit(1)
61
62    if len(parser.args) < 1 and not (verify or repair or cleanup
63                                     or collect_crashinfo):
64        print parser.parser.print_help()
65        sys.exit(1)
66
67    # We have a control file unless it's just a verify/repair/cleanup job
68    if len(parser.args) > 0:
69        control = parser.args[0]
70    else:
71        control = None
72
73    if machines_file:
74        machines = []
75        for m in open(machines_file, 'r').readlines():
76            # remove comments, spaces
77            m = re.sub('#.*', '', m).strip()
78            if m:
79                machines.append(m)
80        print "Read list of machines from file: %s" % machines_file
81        print ','.join(machines)
82
83    if machines:
84        for machine in machines:
85            if not machine or re.search('\s', machine):
86                print "Invalid machine %s" % str(machine)
87                sys.exit(1)
88        machines = list(set(machines))
89        machines.sort()
90
91    if group_name and len(machines) < 2:
92        print ("-G %r may only be supplied with more than one machine."
93               % group_name)
94        sys.exit(1)
95
96    job = server_job.server_job(control, parser.args[1:], results, label,
97                                user, machines, client, parse_job,
98                                ssh_user, ssh_port, ssh_pass,
99                                group_name=group_name)
100    job.logging.start_logging()
101
102    # perform checks
103    job.precheck()
104
105    # run the job
106    exit_code = 0
107    try:
108        try:
109            if repair:
110                job.repair(host_protection)
111            elif verify:
112                job.verify()
113            else:
114                job.run(cleanup, install_before, install_after,
115                        only_collect_crashinfo=collect_crashinfo)
116        finally:
117            while job.hosts:
118                host = job.hosts.pop()
119                host.close()
120    except:
121        exit_code = 1
122        traceback.print_exc()
123
124    if pid_file_manager:
125        pid_file_manager.num_tests_failed = job.num_tests_failed
126        pid_file_manager.close_file(exit_code)
127    job.cleanup_parser()
128
129    sys.exit(exit_code)
130
131
132def main():
133    # grab the parser
134    parser = autoserv_parser.autoserv_parser
135    parser.parse_args()
136
137    if len(sys.argv) == 1:
138        parser.parser.print_help()
139        sys.exit(1)
140
141    if parser.options.no_logging:
142        results = None
143    else:
144        results = parser.options.results
145        if not results:
146            results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S')
147        results  = os.path.abspath(results)
148        resultdir_exists = os.path.exists(os.path.join(results, 'control.srv'))
149        if not parser.options.collect_crashinfo and resultdir_exists:
150            error = "Error: results directory already exists: %s\n" % results
151            sys.stderr.write(error)
152            sys.exit(1)
153
154        # Now that we certified that there's no leftover results dir from
155        # previous jobs, lets create the result dir since the logging system
156        # needs to create the log file in there.
157        if not os.path.isdir(results):
158            os.makedirs(results)
159
160    logging_manager.configure_logging(
161            server_logging_config.ServerLoggingConfig(), results_dir=results,
162            use_console=not parser.options.no_tee,
163            verbose=parser.options.verbose,
164            no_console_prefix=parser.options.no_console_prefix)
165    if results:
166        logging.info("Results placed in %s" % results)
167
168    if parser.options.write_pidfile:
169        if parser.options.collect_crashinfo:
170            pidfile_label = 'collect_crashinfo'
171        else:
172            pidfile_label = 'autoserv'
173        pid_file_manager = pidfile.PidFileManager(pidfile_label, results)
174        pid_file_manager.open_file()
175    else:
176        pid_file_manager = None
177
178    autotest.BaseAutotest.set_install_in_tmpdir(
179        parser.options.install_in_tmpdir)
180
181    exit_code = 0
182    try:
183        try:
184            run_autoserv(pid_file_manager, results, parser)
185        except SystemExit, e:
186            exit_code = e.code
187        except:
188            traceback.print_exc()
189            # If we don't know what happened, we'll classify it as
190            # an 'abort' and return 1.
191            exit_code = 1
192    finally:
193        if pid_file_manager:
194            pid_file_manager.close_file(exit_code)
195    sys.exit(exit_code)
196
197
198if __name__ == '__main__':
199    main()
200