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