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