autoserv revision 332000a0c766fb910f64e95b515ee0bef9a9e9f2
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, logging.config 10 11import common 12from autotest_lib.server import server_job, utils, autoserv_parser, autotest 13from autotest_lib.client.common_lib import pidfile 14 15def run_autoserv(pid_file_manager, results, parser): 16 # send stdin to /dev/null 17 dev_null = os.open(os.devnull, os.O_RDONLY) 18 os.dup2(dev_null, sys.stdin.fileno()) 19 os.close(dev_null) 20 21 # Create separate process group 22 os.setpgrp() 23 24 # Implement SIGTERM handler 25 def handle_sigint(signum, frame): 26 if pid_file_manager: 27 pid_file_manager.close_file(1, signal.SIGTERM) 28 os.killpg(os.getpgrp(), signal.SIGKILL) 29 30 # Set signal handler 31 signal.signal(signal.SIGTERM, handle_sigint) 32 33 # Get a useful value for running 'USER' 34 realuser = os.environ.get('USER') 35 if not realuser: 36 realuser = 'anonymous' 37 38 if parser.options.machines: 39 machines = parser.options.machines.replace(',', ' ').strip().split() 40 else: 41 machines = [] 42 machines_file = parser.options.machines_file 43 label = parser.options.label 44 group_name = parser.options.group_name 45 user = parser.options.user 46 client = parser.options.client 47 server = parser.options.server 48 install_before = parser.options.install_before 49 install_after = parser.options.install_after 50 verify = parser.options.verify 51 repair = parser.options.repair 52 cleanup = parser.options.cleanup 53 no_tee = parser.options.no_tee 54 parse_job = parser.options.parse_job 55 host_protection = parser.options.host_protection 56 ssh_user = parser.options.ssh_user 57 ssh_port = parser.options.ssh_port 58 ssh_pass = parser.options.ssh_pass 59 collect_crashinfo = parser.options.collect_crashinfo 60 61 # can't be both a client and a server side test 62 if client and server: 63 print "Can not specify a test as both server and client!" 64 sys.exit(1) 65 66 if len(parser.args) < 1 and not (verify or repair or cleanup 67 or collect_crashinfo): 68 print parser.parser.print_help() 69 sys.exit(1) 70 71 # We have a control file unless it's just a verify/repair/cleanup job 72 if len(parser.args) > 0: 73 control = parser.args[0] 74 else: 75 control = None 76 77 if machines_file: 78 machines = [] 79 for m in open(machines_file, 'r').readlines(): 80 # remove comments, spaces 81 m = re.sub('#.*', '', m).strip() 82 if m: 83 machines.append(m) 84 print "Read list of machines from file: %s" % machines_file 85 print ','.join(machines) 86 87 if machines: 88 for machine in machines: 89 if not machine or re.search('\s', machine): 90 print "Invalid machine %s" % str(machine) 91 sys.exit(1) 92 machines = list(set(machines)) 93 machines.sort() 94 95 if group_name and len(machines) < 2: 96 print ("-G %r may only be supplied with more than one machine." 97 % group_name) 98 sys.exit(1) 99 100 job = server_job.server_job(control, parser.args[1:], results, label, 101 user, machines, client, parse_job, 102 ssh_user, ssh_port, ssh_pass, 103 group_name=group_name) 104 if results: 105 debug_dir = os.path.join(results, 'debug') 106 stdout = os.path.join(debug_dir, 'autoserv.stdout') 107 stderr = os.path.join(debug_dir, 'autoserv.stderr') 108 if no_tee: 109 job.stdout.redirect(stdout) 110 job.stderr.redirect(stderr) 111 else: 112 job.stdout.tee_redirect(stdout) 113 job.stderr.tee_redirect(stderr) 114 115 # perform checks 116 job.precheck() 117 118 # run the job 119 exit_code = 0 120 try: 121 try: 122 if repair: 123 job.repair(host_protection) 124 elif verify: 125 job.verify() 126 else: 127 job.run(cleanup, install_before, install_after, 128 only_collect_crashinfo=collect_crashinfo) 129 finally: 130 while job.hosts: 131 host = job.hosts.pop() 132 host.close() 133 except: 134 exit_code = 1 135 traceback.print_exc() 136 137 if pid_file_manager: 138 pid_file_manager.num_tests_failed = job.num_tests_failed 139 pid_file_manager.close_file(exit_code) 140 job.cleanup_parser() 141 142 sys.exit(exit_code) 143 144 145def main(): 146 # grab the parser 147 parser = autoserv_parser.autoserv_parser 148 parser.parse_args() 149 150 if len(sys.argv) == 1: 151 parser.parser.print_help() 152 sys.exit(1) 153 154 results = parser.options.results 155 if not parser.options.no_logging: 156 if not results: 157 results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S') 158 results = os.path.abspath(results) 159 resultdir_exists = os.path.exists(os.path.join(results, 'control.srv')) 160 if not parser.options.collect_crashinfo and resultdir_exists: 161 error = "Error: results directory already exists: %s\n" % results 162 sys.stderr.write(error) 163 sys.exit(1) 164 165 # Now that we certified that there's no leftover results dir from 166 # previous jobs, lets create the result dir since the logging system 167 # needs to create the log file in there. 168 if not os.path.isdir(results): 169 os.makedirs(results) 170 os.environ['AUTOSERV_RESULTS'] = results 171 serverdir = os.path.dirname(__file__) 172 logging.config.fileConfig('%s/debug_server.ini' % serverdir) 173 logging.info("Results placed in %s" % results) 174 else: 175 # If we supply -N, no results dir will be generated, so 176 # we'll configure the logging system on code. 177 stamp = '[%(asctime)s - %(levelname)-8s] %(message)s' 178 root_logger = logging.getLogger() 179 formatter = logging.Formatter(stamp, datefmt='%H:%M:%S') 180 # Let's verify if we already have handlers for the root logger 181 # at this point. 182 if len(root_logger.handlers) == 0: 183 autoserv_handler = logging.StreamHandler(sys.stdout,) 184 autoserv_handler.setFormatter(formatter) 185 root_logger.addHandler(autoserv_handler) 186 else: 187 # If we already have any handlers up at this point, let's 188 # just configure this one we already have. 189 root_logger.handlers[0].setFormatter(formatter) 190 191 # When the -N flag is being used, we are assuming DEBUG level for the 192 # execution. We could read the level from the configuration file, 193 # but I am not sure if this is the right way to go, since we are doing 194 # all the configuration on code (lmr). 195 root_logger.setLevel(logging.DEBUG) 196 197 198 if parser.options.write_pidfile: 199 if parser.options.collect_crashinfo: 200 pidfile_label = 'collect_crashinfo' 201 else: 202 pidfile_label = 'autoserv' 203 pid_file_manager = pidfile.PidFileManager(pidfile_label, results) 204 pid_file_manager.open_file() 205 else: 206 pid_file_manager = None 207 208 autotest.BaseAutotest.set_install_in_tmpdir( 209 parser.options.install_in_tmpdir) 210 211 exit_code = 0 212 try: 213 try: 214 run_autoserv(pid_file_manager, results, parser) 215 except SystemExit, e: 216 exit_code = e.code 217 except: 218 traceback.print_exc() 219 # If we don't know what happened, we'll classify it as 220 # an 'abort' and return 1. 221 exit_code = 1 222 finally: 223 if pid_file_manager: 224 pid_file_manager.close_file(exit_code) 225 sys.exit(exit_code) 226 227 228if __name__ == '__main__': 229 main() 230