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