autoserv.py revision 6437ff5bb28a0cc97582766826f866a39affae2a
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
14
15import common
16from autotest_lib.server import server_job, utils
17
18# send stdin to /dev/null
19dev_null = os.open(os.devnull, os.O_RDONLY)
20os.dup2(dev_null, sys.stdin.fileno())
21os.close(dev_null)
22
23
24class PidFileManager(object):
25	pid_file = None
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.close()
41
42
43pid_file_manager = PidFileManager()
44
45
46# Create separate process group
47os.setpgrp()
48
49# Implement SIGTERM handler
50def handle_sigint(signum, frame):
51	pid_file_manager.close_pid_file(1, signal.SIGTERM)
52	os.killpg(os.getpgrp(), signal.SIGKILL)
53
54# Set signal handler
55signal.signal(signal.SIGTERM, handle_sigint)
56
57
58usage = """\
59usage: autoserv
60	[-h, --help]               # This help message
61	[-m machine,[machine,...]] # list of machines to pass to control file
62	[-M machines_file]         # list of machines (from a file)
63	[-c]                       # control file is a client side control
64	[-r resultsdir]            # specify results directory (default '.')
65	[-i]                       # reinstall machines before running the job
66	[-I]                       # reinstall machines after running the job
67	[-b]                       # reboot all specified machines after the job
68	[-l label]                 # label for the job (arbitrary string)
69	[-u user]                  # username for the job (email address)
70	[-v]                       # verify the machines only
71	[-R]                       # repair the machines
72	[-n]                       # no teeing the status to stdout/stderr
73	[-p]                       # write pidfile (.autoserv_execute)
74	[-P jobname]               # parse the results of the job
75	<control file>             # name of the control file to run
76	[args ...]                 # args to pass through to the control file
77"""
78
79args = sys.argv[1:]
80parser = utils.AutoservOptionParser(args)
81
82# Get a useful value for running 'USER'
83realuser = os.environ.get('USER')
84if not realuser:
85	realuser = 'anonymous'
86
87machines = parser.parse_opts_param('-m', None, split = ',')
88machines_file = parser.parse_opts_param('-M', None)
89results  = parser.parse_opts_param('-r', os.path.abspath('.'))
90results  = os.path.abspath(results)
91label    = parser.parse_opts_param('-l', '')
92user     = parser.parse_opts_param('-u', realuser)
93client   = parser.parse_opts('-c')
94reboot   = parser.parse_opts('-b')
95install_before = parser.parse_opts('-i')
96install_after  = parser.parse_opts('-I')
97verify   = parser.parse_opts('-v')
98repair   = parser.parse_opts('-R')
99no_tee   = parser.parse_opts('-n')
100help     = parser.parse_opts('-h') or parser.parse_opts('--help')
101write_pidfile = parser.parse_opts('-p')
102parse_job = parser.parse_opts_param('-P', '')
103
104if help is True:
105	print usage
106	sys.exit(0)
107
108if len(parser.args) < 1 and not verify and not repair:
109	print usage
110	sys.exit(-1)
111
112if machines_file:
113	machines = []
114	for m in open(machines_file, 'r').readlines():
115		m = re.sub('#.*', '', m).strip()   # remove comments, spaces
116		if m:
117			machines.append(m)
118	print "Read list of machines from file: %s" % machines_file
119	print ','.join(machines)
120
121if machines:
122	for machine in machines:
123		if not machine or re.search('\s', machine):
124			print "Invalid machine %s" % str(machine)
125			sys.exit(1)
126	machines = list(set(machines))
127	machines.sort()
128
129# We have a control file unless it's just a verify/repair job
130if len(parser.args) > 0:
131	control = parser.args[0]
132else:
133	control = None
134
135job = server_job.server_job(control, parser.args[1:], results, label,
136			    user, machines, client, parse_job)
137debug_dir = os.path.join(results, 'debug')
138if no_tee:
139	job.stdout.redirect(os.path.join(debug_dir, 'autoserv.stdout'))
140	job.stderr.redirect(os.path.join(debug_dir, 'autoserv.stderr'))
141else:
142	job.stdout.tee_redirect(os.path.join(debug_dir, 'autoserv.stdout'))
143	job.stderr.tee_redirect(os.path.join(debug_dir, 'autoserv.stderr'))
144
145if write_pidfile:
146	pid_file_manager.open_pid_file(results)
147
148# run the job
149exit_code = 0
150try:
151	if repair:
152		job.repair()
153	elif verify:
154		job.verify()
155	else:
156		try:
157			job.run(reboot, install_before, install_after)
158		finally:
159			job._cleanup()
160except:
161	job.aborted = True
162	traceback.print_exc()
163
164if getattr(job, 'aborted', False):
165	exit_code = 1
166pid_file_manager.close_pid_file(exit_code)
167
168sys.exit(exit_code)
169