#!/usr/bin/python -u # Copyright 2007-2008 Martin J. Bligh , Google Inc. # Released under the GPL v2 """ Run an control file through the server side engine """ import sys, os, re, traceback, signal, time, logging, logging.config import common from autotest_lib.server import server_job, utils, autoserv_parser, autotest from autotest_lib.client.common_lib import pidfile def run_autoserv(pid_file_manager, results, parser): # send stdin to /dev/null dev_null = os.open(os.devnull, os.O_RDONLY) os.dup2(dev_null, sys.stdin.fileno()) os.close(dev_null) # Create separate process group os.setpgrp() # Implement SIGTERM handler def handle_sigint(signum, frame): if pid_file_manager: pid_file_manager.close_file(1, signal.SIGTERM) os.killpg(os.getpgrp(), signal.SIGKILL) # Set signal handler signal.signal(signal.SIGTERM, handle_sigint) # Get a useful value for running 'USER' realuser = os.environ.get('USER') if not realuser: realuser = 'anonymous' if parser.options.machines: machines = parser.options.machines.replace(',', ' ').strip().split() else: machines = [] machines_file = parser.options.machines_file label = parser.options.label user = parser.options.user client = parser.options.client server = parser.options.server install_before = parser.options.install_before install_after = parser.options.install_after verify = parser.options.verify repair = parser.options.repair cleanup = parser.options.cleanup no_tee = parser.options.no_tee parse_job = parser.options.parse_job host_protection = parser.options.host_protection ssh_user = parser.options.ssh_user ssh_port = parser.options.ssh_port ssh_pass = parser.options.ssh_pass collect_crashinfo = parser.options.collect_crashinfo # can't be both a client and a server side test if client and server: print "Can not specify a test as both server and client!" sys.exit(1) if len(parser.args) < 1 and not (verify or repair or cleanup or collect_crashinfo): print parser.parser.print_help() sys.exit(1) # We have a control file unless it's just a verify/repair/cleanup job if len(parser.args) > 0: control = parser.args[0] else: control = None if machines_file: machines = [] for m in open(machines_file, 'r').readlines(): # remove comments, spaces m = re.sub('#.*', '', m).strip() if m: machines.append(m) print "Read list of machines from file: %s" % machines_file print ','.join(machines) if machines: for machine in machines: if not machine or re.search('\s', machine): print "Invalid machine %s" % str(machine) sys.exit(1) machines = list(set(machines)) machines.sort() job = server_job.server_job(control, parser.args[1:], results, label, user, machines, client, parse_job, ssh_user, ssh_port, ssh_pass) if results: debug_dir = os.path.join(results, 'debug') stdout = os.path.join(debug_dir, 'autoserv.stdout') stderr = os.path.join(debug_dir, 'autoserv.stderr') if no_tee: job.stdout.redirect(stdout) job.stderr.redirect(stderr) else: job.stdout.tee_redirect(stdout) job.stderr.tee_redirect(stderr) # perform checks job.precheck() # run the job exit_code = 0 try: if repair: job.repair(host_protection) elif verify: job.verify() else: try: job.run(cleanup, install_before, install_after, only_collect_crashinfo=collect_crashinfo) finally: while job.hosts: host = job.hosts.pop() host.close() except: exit_code = 1 traceback.print_exc() if pid_file_manager: pid_file_manager.num_tests_failed = job.num_tests_failed pid_file_manager.close_file(exit_code) job.cleanup_parser() sys.exit(exit_code) def main(): # grab the parser parser = autoserv_parser.autoserv_parser parser.parse_args() if len(sys.argv) == 1: parser.parser.print_help() sys.exit(1) results = parser.options.results if not parser.options.no_logging: if not results: results = 'results.' + time.strftime('%Y-%m-%d-%H.%M.%S') results = os.path.abspath(results) resultdir_exists = os.path.exists(os.path.join(results, 'control.srv')) if not parser.options.collect_crashinfo and resultdir_exists: error = "Error: results directory already exists: %s\n" % results sys.stderr.write(error) sys.exit(1) # Now that we certified that there's no leftover results dir from # previous jobs, lets create the result dir since the logging system # needs to create the log file in there. if not os.path.isdir(results): os.makedirs(results) os.environ['AUTOSERV_RESULTS'] = results serverdir = os.path.dirname(__file__) logging.config.fileConfig('%s/debug_server.ini' % serverdir) logging.info("Results placed in %s" % results) else: # If we supply -N, no results dir will be generated, so # we'll configure the logging system on code. stamp = '[%(asctime)s - %(levelname)-8s] %(message)s' root_logger = logging.getLogger() formatter = logging.Formatter(stamp, datefmt='%H:%M:%S') # Let's verify if we already have handlers for the root logger # at this point. if len(root_logger.handlers) == 0: autoserv_handler = logging.StreamHandler(sys.stdout,) autoserv_handler.setFormatter(formatter) root_logger.addHandler(autoserv_handler) else: # If we already have any handlers up at this point, let's # just configure this one we already have. root_logger.handlers[0].setFormatter(formatter) # When the -N flag is being used, we are assuming DEBUG level for the # execution. We could read the level from the configuration file, # but I am not sure if this is the right way to go, since we are doing # all the configuration on code (lmr). root_logger.setLevel(logging.DEBUG) if parser.options.write_pidfile: if parser.options.collect_crashinfo: pidfile_label = 'collect_crashinfo' else: pidfile_label = 'autoserv' pid_file_manager = pidfile.PidFileManager(pidfile_label, results) pid_file_manager.open_file() else: pid_file_manager = None autotest.BaseAutotest.set_install_in_tmpdir( parser.options.install_in_tmpdir) exit_code = 0 try: try: run_autoserv(pid_file_manager, results, parser) except SystemExit, e: exit_code = e.code except: traceback.print_exc() # If we don't know what happened, we'll classify it as # an 'abort' and return 1. exit_code = 1 finally: if pid_file_manager: pid_file_manager.close_file(exit_code) sys.exit(exit_code) if __name__ == '__main__': main()