blob: 9ac59baeced71267245006a692f46f4894a4ce46 [file] [log] [blame]
mblighf1c52842007-10-16 15:21:38 +00001"""
2The main job wrapper for the server side.
3
4This is the core infrastructure. Derived from the client side job.py
5
6Copyright Martin J. Bligh, Andy Whitcroft 2007
7"""
8
9__author__ = """
10Martin J. Bligh <mbligh@google.com>
11Andy Whitcroft <apw@shadowen.org>
12"""
13
14import os, sys, re
15from utils import *
16
17preamble = """\
18import os, sys
19
20import errors, hosts, autotest, kvm
21import source_kernel, rpm_kernel, deb_kernel
22from subcommand import *
23from utils import run, get_tmp_dir, sh_escape
24
25"""
26
27client_wrapper = """
28at = autotest.Autotest()
29
30def run_client(machine):
31 host = hosts.SSHHost(machine)
32 at.run(control, host=host)
33
34if len(machines) > 1:
35 parallel_simple(run_client, machines)
36else:
37 run_client(machines[0])
38"""
39
40cleanup="""\
41def cleanup(machine):
42 host = hosts.SSHHost(machine, initialize=False)
43 host.reboot()
44
45parallel_simple(cleanup, machines)
46"""
47
48class server_job:
49 """The actual job against which we do everything.
50
51 Properties:
52 autodir
53 The top level autotest directory (/usr/local/autotest).
54 serverdir
55 <autodir>/server/
56 clientdir
57 <autodir>/client/
58 conmuxdir
59 <autodir>/conmux/
60 testdir
61 <autodir>/server/tests/
62 control
63 the control file for this job
64 """
65
66 def __init__(self, control, args, resultdir, tag, user, client=False):
67 """
68 control
69 The control file (pathname of)
70 args
71 args to pass to the control file
72 resultdir
73 where to throw the results
74 tag
75 tag for the job
76 user
77 Username for the job (email address)
78 client
79 True if a client-side control file
80 """
81 path = sys.modules['server_job'].__file__
82 self.autodir = os.path.abspath(os.path.join(path, '..'))
83 self.serverdir = os.path.join(self.autodir, 'server')
84 self.testdir = os.path.join(self.autodir, 'tests')
85 self.conmuxdir = os.path.join(self.autodir, 'conmux')
86 self.clientdir = os.path.join(self.autodir, 'client')
87 self.control = re.sub('\r\n', '\n', open(control, 'r').read())
88 self.resultdir = resultdir
89 if not os.path.exists(resultdir):
90 os.mkdir(resultdir)
91 self.tag = tag
92 self.user = user
93 self.args = args
94 self.client = client
95 self.record_prefix = ''
96
97 job_data = { 'tag' : tag, 'user' : user}
98 write_keyval(self.resultdir, job_data)
99
100
101 def run(self, machines, reboot = False, namespace = {}):
102 namespace['machines'] = machines
103 namespace['args'] = self.args
104 namespace['job'] = self
105
106 os.chdir(self.resultdir)
107 try:
108 if self.client:
109 namespace['control'] = self.control
110 open('control', 'w').write(self.control)
111 open('control.srv', 'w').write(client_wrapper)
112 server_control = client_wrapper
113 else:
114 open('control.srv', 'w').write(self.control)
115 server_control = self.control
116 print preamble
117 print server_control
118 exec(preamble + server_control, namespace, namespace)
119
120 finally:
121 if reboot and machines:
122 exec(preamble + cleanup, namespace, namespace)
123
124
125 def run_test(self, url, *args, **dargs):
126 """Summon a test object and run it.
127
128 tag
129 tag to add to testname
130 url
131 url of the test to run
132 """
133
mblighf1c52842007-10-16 15:21:38 +0000134 (group, testname) = test.testname(url)
135 tag = None
136 subdir = testname
mbligh43ac5222007-10-16 15:55:01 +0000137
mblighf1c52842007-10-16 15:21:38 +0000138 if dargs.has_key('tag'):
139 tag = dargs['tag']
140 del dargs['tag']
141 if tag:
142 subdir += '.' + tag
mblighf1c52842007-10-16 15:21:38 +0000143
mbligh43ac5222007-10-16 15:55:01 +0000144 try:
145 test.runtest(self, url, tag, args, dargs)
146 self.record('GOOD', subdir, testname, 'completed successfully')
147 except Exception, detail:
148 self.record('FAIL', subdir, testname, detail.__str__())
mblighf1c52842007-10-16 15:21:38 +0000149
150
151 def run_group(self, function, *args, **dargs):
152 """\
153 function:
154 subroutine to run
155 *args:
156 arguments for the function
157 """
158
159 result = None
160 name = function.__name__
161
162 # Allow the tag for the group to be specified.
163 if dargs.has_key('tag'):
164 tag = dargs['tag']
165 del dargs['tag']
166 if tag:
167 name = tag
168
169 # if tag:
170 # name += '.' + tag
171 old_record_prefix = self.record_prefix
172 try:
173 try:
174 self.record('START', None, name)
175 self.record_prefix += '\t'
176 result = function(*args, **dargs)
177 self.record_prefix = old_record_prefix
178 self.record('END GOOD', None, name)
179 except:
180 self.record_prefix = old_record_prefix
181 self.record('END FAIL', None, name, format_error())
182 # We don't want to raise up an error higher if it's just
183 # a TestError - we want to carry on to other tests. Hence
184 # this outer try/except block.
185 except TestError:
186 pass
187 except:
188 raise TestError(name + ' failed\n' + format_error())
189
190 return result
191
192
193 def record(self, status_code, subdir, operation, status = ''):
194 """
195 Record job-level status
196
197 The intent is to make this file both machine parseable and
198 human readable. That involves a little more complexity, but
199 really isn't all that bad ;-)
200
201 Format is <status code>\t<subdir>\t<operation>\t<status>
202
203 status code: (GOOD|WARN|FAIL|ABORT)
204 or START
205 or END (GOOD|WARN|FAIL|ABORT)
206
207 subdir: MUST be a relevant subdirectory in the results,
208 or None, which will be represented as '----'
209
210 operation: description of what you ran (e.g. "dbench", or
211 "mkfs -t foobar /dev/sda9")
212
213 status: error message or "completed sucessfully"
214
215 ------------------------------------------------------------
216
217 Initial tabs indicate indent levels for grouping, and is
218 governed by self.record_prefix
219
220 multiline messages have secondary lines prefaced by a double
221 space (' ')
222 """
223
224 if subdir:
225 if re.match(r'[\n\t]', subdir):
226 raise "Invalid character in subdir string"
227 substr = subdir
228 else:
229 substr = '----'
230
231 if not re.match(r'(START|(END )?(GOOD|WARN|FAIL|ABORT))$', \
232 status_code):
233 raise "Invalid status code supplied: %s" % status_code
234 if re.match(r'[\n\t]', operation):
235 raise "Invalid character in operation string"
236 operation = operation.rstrip()
237 status = status.rstrip()
238 status = re.sub(r"\t", " ", status)
239 # Ensure any continuation lines are marked so we can
240 # detect them in the status file to ensure it is parsable.
241 status = re.sub(r"\n", "\n" + self.record_prefix + " ", status)
242
243 msg = '%s\t%s\t%s\t%s' %(status_code, substr, operation, status)
244
245 status_file = os.path.join(self.resultdir, 'status')
246 print status_file
247 print msg
248 open(status_file, "a").write(self.record_prefix + msg + "\n")
249 if subdir:
250 status_file = os.path.join(self.resultdir, subdir, 'status')
251 open(status_file, "a").write(msg + "\n")
252