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