blob: 9742cc226aa6228fe88da416517abb6b71a302b7 [file] [log] [blame]
mbligh6203ace2007-10-04 21:54:24 +00001#!/usr/bin/python -u
mblighdcd57a82007-07-11 23:06:47 +00002#
3# Copyright 2007 Google Inc. Released under the GPL v2
4
mblighc8949b82007-07-23 16:33:58 +00005"""
mblighf1c52842007-10-16 15:21:38 +00006Run an control file through the server side engine
mblighdcd57a82007-07-11 23:06:47 +00007"""
8
mblighf1c52842007-10-16 15:21:38 +00009__author__ = """\
10mbligh@google.com (Martin J. Bligh)
mblighc8949b82007-07-23 16:33:58 +000011"""
mblighdcd57a82007-07-11 23:06:47 +000012
mblighf5427bb2008-04-09 15:55:57 +000013import sys, os, re, traceback, signal
mbligh0c3548d2008-02-01 18:08:53 +000014
mblighf5427bb2008-04-09 15:55:57 +000015import common
16from autotest_lib.server import server_job, utils
mblighf7243e12008-03-05 16:01:21 +000017
mblighf1c52842007-10-16 15:21:38 +000018usage = """\
19usage: autoserv
mbligh9e618782008-03-13 15:35:09 +000020 [-h, --help] # This help message
mblighf36243d2007-10-30 15:36:16 +000021 [-m machine,[machine,...]] # list of machines to pass to control file
mbligh842c6592007-11-05 18:27:23 +000022 [-M machines_file] # list of machines (from a file)
mblighf1c52842007-10-16 15:21:38 +000023 [-c] # control file is a client side control
24 [-r resultsdir] # specify results directory (default '.')
mblighf36243d2007-10-30 15:36:16 +000025 [-i] # reinstall machines before running the job
26 [-I] # reinstall machines after running the job
27 [-b] # reboot all specified machines after the job
mbligh18420c22007-10-16 22:27:14 +000028 [-l label] # label for the job (arbitrary string)
mblighf1c52842007-10-16 15:21:38 +000029 [-u user] # username for the job (email address)
mbligh1d42d4e2007-11-05 22:42:00 +000030 [-v] # verify the machines only
31 [-R] # repair the machines
mblighe5cd0fd2008-01-26 23:07:31 +000032 [-n] # no teeing the status to stdout/stderr
mblighbb421852008-03-11 22:36:16 +000033 [-p] # write pidfile (.autoserv_execute)
mbligh6437ff52008-04-17 15:24:38 +000034 [-P jobname] # parse the results of the job
mblighf1c52842007-10-16 15:21:38 +000035 <control file> # name of the control file to run
36 [args ...] # args to pass through to the control file
mbligh29aa9702007-08-09 22:41:43 +000037"""
38
mbligh9d1c7302007-10-07 18:53:13 +000039
mbligha46678d2008-05-01 20:00:01 +000040class PidFileManager(object):
41 pid_file = None
mbligh74fc0462007-11-05 20:24:17 +000042
mbligha46678d2008-05-01 20:00:01 +000043 def open_pid_file(self, results_dir):
44 pid_file_path = os.path.join(results_dir, '.autoserv_execute')
45 assert not os.path.exists(pid_file_path)
46 self.pid_file = open(pid_file_path, 'w')
47 self.pid_file.write(str(os.getpid()) + '\n')
48 self.pid_file.flush()
mblighc99add62007-09-27 17:02:58 +000049
mblighc99add62007-09-27 17:02:58 +000050
mbligha46678d2008-05-01 20:00:01 +000051 def close_pid_file(self, exit_code, signal_code=0):
52 if not self.pid_file:
53 return
54 real_exit_code = (exit_code << 8) | (signal_code & 0xFF)
55 self.pid_file.write(str(real_exit_code) + '\n')
56 self.pid_file.close()
57 self.pid_file = None
mblighc99add62007-09-27 17:02:58 +000058
mbligh842c6592007-11-05 18:27:23 +000059
mbligha46678d2008-05-01 20:00:01 +000060def run_autoserv(pid_file_manager, results, parser):
61 # send stdin to /dev/null
62 dev_null = os.open(os.devnull, os.O_RDONLY)
63 os.dup2(dev_null, sys.stdin.fileno())
64 os.close(dev_null)
mblighdbf37612007-11-24 19:38:11 +000065
mbligha46678d2008-05-01 20:00:01 +000066 # Create separate process group
67 os.setpgrp()
mbligh1d42d4e2007-11-05 22:42:00 +000068
mbligha46678d2008-05-01 20:00:01 +000069 # Implement SIGTERM handler
70 def handle_sigint(signum, frame):
71 pid_file_manager.close_pid_file(1, signal.SIGTERM)
72 os.killpg(os.getpgrp(), signal.SIGKILL)
mblighfaf0cd42007-11-19 16:00:24 +000073
mbligha46678d2008-05-01 20:00:01 +000074 # Set signal handler
75 signal.signal(signal.SIGTERM, handle_sigint)
mblighe25fd5b2008-01-22 17:23:37 +000076
mbligha46678d2008-05-01 20:00:01 +000077 # Get a useful value for running 'USER'
78 realuser = os.environ.get('USER')
79 if not realuser:
80 realuser = 'anonymous'
81
82 machines = parser.parse_opts_param('-m', None, split = ',')
83 machines_file = parser.parse_opts_param('-M', None)
84 label = parser.parse_opts_param('-l', '')
85 user = parser.parse_opts_param('-u', realuser)
86 client = parser.parse_opts('-c')
87 reboot = parser.parse_opts('-b')
88 install_before = parser.parse_opts('-i')
89 install_after = parser.parse_opts('-I')
90 verify = parser.parse_opts('-v')
91 repair = parser.parse_opts('-R')
92 no_tee = parser.parse_opts('-n')
93 help = parser.parse_opts('-h') or parser.parse_opts('--help')
94 parse_job = parser.parse_opts_param('-P', '')
95
96 if help is True:
97 print usage
98 sys.exit(0)
99
100 if len(parser.args) < 1 and not verify and not repair:
101 print usage
102 sys.exit(1)
103
104 # We have a control file unless it's just a verify/repair job
105 if len(parser.args) > 0:
106 control = parser.args[0]
mblighbb421852008-03-11 22:36:16 +0000107 else:
mbligha46678d2008-05-01 20:00:01 +0000108 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 debug_dir = os.path.join(results, 'debug')
131 stdout = os.path.join(debug_dir, 'autoserv.stdout')
132 stderr = os.path.join(debug_dir, 'autoserv.stderr')
133 if no_tee:
134 job.stdout.redirect(stdout)
135 job.stderr.redirect(stderr)
136 else:
137 job.stdout.tee_redirect(stdout)
138 job.stderr.tee_redirect(stderr)
139
140 # run the job
141 exit_code = 0
142 try:
143 if repair:
144 job.repair()
145 elif verify:
146 job.verify()
147 else:
148 try:
149 job.run(reboot, install_before, install_after)
150 finally:
151 job.cleanup_parser()
152 except:
153 job.aborted = True
154 traceback.print_exc()
155
156 if getattr(job, 'aborted', False):
157 sys.exit(1)
158
159
160def main():
161 pid_file_manager = PidFileManager()
162
163 args = sys.argv[1:]
164 parser = utils.AutoservOptionParser(args)
165
166 results = parser.parse_opts_param('-r', '.')
167 results = os.path.abspath(results)
168 write_pidfile = parser.parse_opts('-p')
169 if write_pidfile:
170 pid_file_manager.open_pid_file(results)
171
172 exit_code = 0
173 try:
mbligh6437ff52008-04-17 15:24:38 +0000174 try:
mbligha46678d2008-05-01 20:00:01 +0000175 run_autoserv(pid_file_manager, results, parser)
176 except SystemExit, e:
177 exit_code = e.code
178 except:
179 # If we don't know what happened, we'll classify it as
180 # an 'abort' and return 1.
181 exit_code = 1
182 finally:
183 pid_file_manager.close_pid_file(exit_code)
184 sys.exit(exit_code)
mblighfaf0cd42007-11-19 16:00:24 +0000185
mblighbb421852008-03-11 22:36:16 +0000186
mbligha46678d2008-05-01 20:00:01 +0000187if __name__ == '__main__':
188 main()