autoserv.py revision 4608b005f15444d2ec4601b8274828ad52b5ea51
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, getpass 10 11import common 12 13from autotest_lib.client.common_lib.global_config import global_config 14require_atfork = global_config.get_config_value( 15 'AUTOSERV', 'require_atfork_module', type=bool, default=True) 16 17try: 18 import atfork 19 atfork.monkeypatch_os_fork_functions() 20 import atfork.stdlib_fixer 21 # Fix the Python standard library for threading+fork safety with its 22 # internal locks. http://code.google.com/p/python-atfork/ 23 import warnings 24 warnings.filterwarnings('ignore', 'logging module already imported') 25 atfork.stdlib_fixer.fix_logging_module() 26except ImportError, e: 27 from autotest_lib.client.common_lib import global_config 28 if global_config.global_config.get_config_value( 29 'AUTOSERV', 'require_atfork_module', type=bool, default=False): 30 print >>sys.stderr, 'Please run utils/build_externals.py' 31 print e 32 sys.exit(1) 33 34from autotest_lib.server import server_logging_config 35from autotest_lib.server import server_job, utils, autoserv_parser, autotest 36from autotest_lib.client.common_lib import pidfile, logging_manager 37 38def run_autoserv(pid_file_manager, results, parser): 39 # send stdin to /dev/null 40 dev_null = os.open(os.devnull, os.O_RDONLY) 41 os.dup2(dev_null, sys.stdin.fileno()) 42 os.close(dev_null) 43 44 # Create separate process group 45 os.setpgrp() 46 47 # Implement SIGTERM handler 48 def handle_sigterm(signum, frame): 49 if pid_file_manager: 50 pid_file_manager.close_file(1, signal.SIGTERM) 51 os.killpg(os.getpgrp(), signal.SIGKILL) 52 53 # Set signal handler 54 signal.signal(signal.SIGTERM, handle_sigterm) 55 56 # Server side tests that call shell scripts often depend on $USER being set 57 # but depending on how you launch your autotest scheduler it may not be set. 58 os.environ['USER'] = getpass.getuser() 59 60 if parser.options.machines: 61 machines = parser.options.machines.replace(',', ' ').strip().split() 62 else: 63 machines = [] 64 machines_file = parser.options.machines_file 65 label = parser.options.label 66 group_name = parser.options.group_name 67 user = parser.options.user 68 client = parser.options.client 69 server = parser.options.server 70 install_before = parser.options.install_before 71 install_after = parser.options.install_after 72 verify = parser.options.verify 73 repair = parser.options.repair 74 cleanup = parser.options.cleanup 75 no_tee = parser.options.no_tee 76 parse_job = parser.options.parse_job 77 execution_tag = parser.options.execution_tag 78 if not execution_tag: 79 execution_tag = parse_job 80 host_protection = parser.options.host_protection 81 ssh_user = parser.options.ssh_user 82 ssh_port = parser.options.ssh_port 83 ssh_pass = parser.options.ssh_pass 84 collect_crashinfo = parser.options.collect_crashinfo 85 86 # can't be both a client and a server side test 87 if client and server: 88 print "Can not specify a test as both server and client!" 89 sys.exit(1) 90 91 if len(parser.args) < 1 and not (verify or repair or cleanup 92 or collect_crashinfo): 93 print parser.parser.print_help() 94 sys.exit(1) 95 96 # We have a control file unless it's just a verify/repair/cleanup job 97 if len(parser.args) > 0: 98 control = parser.args[0] 99 else: 100 control = None 101 102 if machines_file: 103 machines = [] 104 for m in open(machines_file, 'r').readlines(): 105 # remove comments, spaces 106 m = re.sub('#.*', '', m).strip() 107 if m: 108 machines.append(m) 109 print "Read list of machines from file: %s" % machines_file 110 print ','.join(machines) 111 112 if machines: 113 for machine in machines: 114 if not machine or re.search('\s', machine): 115 print "Invalid machine %s" % str(machine) 116 sys.exit(1) 117 machines = list(set(machines)) 118 machines.sort() 119 120 if group_name and len(machines) < 2: 121 print ("-G %r may only be supplied with more than one machine." 122 % group_name) 123 sys.exit(1) 124 125 job = server_job.server_job(control, parser.args[1:], results, label, 126 user, machines, client, parse_job, 127 ssh_user, ssh_port, ssh_pass, 128 group_name=group_name, tag=execution_tag) 129 job.logging.start_logging() 130 job.init_parser() 131 132 # perform checks 133 job.precheck() 134 135 # run the job 136 exit_code = 0 137 try: 138 try: 139 if repair: 140 job.repair(host_protection) 141 elif verify: 142 job.verify() 143 else: 144 job.run(cleanup, install_before, install_after, 145 only_collect_crashinfo=collect_crashinfo) 146 finally: 147 while job.hosts: 148 host = job.hosts.pop() 149 host.close() 150 except: 151 exit_code = 1 152 traceback.print_exc() 153 154 if pid_file_manager: 155 pid_file_manager.num_tests_failed = job.num_tests_failed 156 pid_file_manager.close_file(exit_code) 157 job.cleanup_parser() 158 159 sys.exit(exit_code) 160 161 162def main(): 163 # grab the parser 164 parser = autoserv_parser.autoserv_parser 165 parser.parse_args() 166 167 if len(sys.argv) == 1: 168 parser.parser.print_help() 169 sys.exit(1) 170 171 if parser.options.no_logging: 172 results = None 173 else: 174 results = parser.options.results 175 if not results: 176 results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S') 177 results = os.path.abspath(results) 178 resultdir_exists = os.path.exists(os.path.join(results, 'control.srv')) 179 if not parser.options.use_existing_results and resultdir_exists: 180 error = "Error: results directory already exists: %s\n" % results 181 sys.stderr.write(error) 182 sys.exit(1) 183 184 # Now that we certified that there's no leftover results dir from 185 # previous jobs, lets create the result dir since the logging system 186 # needs to create the log file in there. 187 if not os.path.isdir(results): 188 os.makedirs(results) 189 190 logging_manager.configure_logging( 191 server_logging_config.ServerLoggingConfig(), results_dir=results, 192 use_console=not parser.options.no_tee, 193 verbose=parser.options.verbose, 194 no_console_prefix=parser.options.no_console_prefix) 195 if results: 196 logging.info("Results placed in %s" % results) 197 198 # wait until now to perform this check, so it get properly logged 199 if parser.options.use_existing_results and not resultdir_exists: 200 logging.error("No existing results directory found: %s", results) 201 sys.exit(1) 202 203 204 if parser.options.write_pidfile: 205 pid_file_manager = pidfile.PidFileManager(parser.options.pidfile_label, 206 results) 207 pid_file_manager.open_file() 208 else: 209 pid_file_manager = None 210 211 autotest.BaseAutotest.set_install_in_tmpdir( 212 parser.options.install_in_tmpdir) 213 214 exit_code = 0 215 try: 216 try: 217 run_autoserv(pid_file_manager, results, parser) 218 except SystemExit, e: 219 exit_code = e.code 220 except: 221 traceback.print_exc() 222 # If we don't know what happened, we'll classify it as 223 # an 'abort' and return 1. 224 exit_code = 1 225 finally: 226 if pid_file_manager: 227 pid_file_manager.close_file(exit_code) 228 sys.exit(exit_code) 229 230 231if __name__ == '__main__': 232 main() 233