blob: 9e4fadbfb38a74d9f6acddd05292d1ac89f29ab7 [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)
94 self.tag = tag
95 self.user = user
96 self.args = args
97 self.client = client
98 self.record_prefix = ''
99
100 job_data = { 'tag' : tag, 'user' : user}
101 write_keyval(self.resultdir, job_data)
102
103
104 def run(self, machines, reboot = False, namespace = {}):
105 namespace['machines'] = machines
106 namespace['args'] = self.args
107 namespace['job'] = self
108
109 os.chdir(self.resultdir)
110 try:
111 if self.client:
112 namespace['control'] = self.control
113 open('control', 'w').write(self.control)
114 open('control.srv', 'w').write(client_wrapper)
115 server_control = client_wrapper
116 else:
117 open('control.srv', 'w').write(self.control)
118 server_control = self.control
mblighf1c52842007-10-16 15:21:38 +0000119 exec(preamble + server_control, namespace, namespace)
120
121 finally:
122 if reboot and machines:
123 exec(preamble + cleanup, namespace, namespace)
124
125
126 def run_test(self, url, *args, **dargs):
127 """Summon a test object and run it.
128
129 tag
130 tag to add to testname
131 url
132 url of the test to run
133 """
134
mblighf1c52842007-10-16 15:21:38 +0000135 (group, testname) = test.testname(url)
136 tag = None
137 subdir = testname
mbligh43ac5222007-10-16 15:55:01 +0000138
mblighf1c52842007-10-16 15:21:38 +0000139 if dargs.has_key('tag'):
140 tag = dargs['tag']
141 del dargs['tag']
142 if tag:
143 subdir += '.' + tag
mblighf1c52842007-10-16 15:21:38 +0000144
mbligh43ac5222007-10-16 15:55:01 +0000145 try:
146 test.runtest(self, url, tag, args, dargs)
147 self.record('GOOD', subdir, testname, 'completed successfully')
148 except Exception, detail:
mbligh05269362007-10-16 16:58:11 +0000149 self.record('FAIL', subdir, testname, format_error())
mblighf1c52842007-10-16 15:21:38 +0000150
151
152 def run_group(self, function, *args, **dargs):
153 """\
154 function:
155 subroutine to run
156 *args:
157 arguments for the function
158 """
159
160 result = None
161 name = function.__name__
162
163 # Allow the tag for the group to be specified.
164 if dargs.has_key('tag'):
165 tag = dargs['tag']
166 del dargs['tag']
167 if tag:
168 name = tag
169
170 # if tag:
171 # name += '.' + tag
172 old_record_prefix = self.record_prefix
173 try:
174 try:
175 self.record('START', None, name)
176 self.record_prefix += '\t'
177 result = function(*args, **dargs)
178 self.record_prefix = old_record_prefix
179 self.record('END GOOD', None, name)
180 except:
181 self.record_prefix = old_record_prefix
182 self.record('END FAIL', None, name, format_error())
183 # We don't want to raise up an error higher if it's just
184 # a TestError - we want to carry on to other tests. Hence
185 # this outer try/except block.
186 except TestError:
187 pass
188 except:
189 raise TestError(name + ' failed\n' + format_error())
190
191 return result
192
193
194 def record(self, status_code, subdir, operation, status = ''):
195 """
196 Record job-level status
197
198 The intent is to make this file both machine parseable and
199 human readable. That involves a little more complexity, but
200 really isn't all that bad ;-)
201
202 Format is <status code>\t<subdir>\t<operation>\t<status>
203
204 status code: (GOOD|WARN|FAIL|ABORT)
205 or START
206 or END (GOOD|WARN|FAIL|ABORT)
207
208 subdir: MUST be a relevant subdirectory in the results,
209 or None, which will be represented as '----'
210
211 operation: description of what you ran (e.g. "dbench", or
212 "mkfs -t foobar /dev/sda9")
213
214 status: error message or "completed sucessfully"
215
216 ------------------------------------------------------------
217
218 Initial tabs indicate indent levels for grouping, and is
219 governed by self.record_prefix
220
221 multiline messages have secondary lines prefaced by a double
222 space (' ')
223 """
224
225 if subdir:
226 if re.match(r'[\n\t]', subdir):
227 raise "Invalid character in subdir string"
228 substr = subdir
229 else:
230 substr = '----'
231
232 if not re.match(r'(START|(END )?(GOOD|WARN|FAIL|ABORT))$', \
233 status_code):
234 raise "Invalid status code supplied: %s" % status_code
235 if re.match(r'[\n\t]', operation):
236 raise "Invalid character in operation string"
237 operation = operation.rstrip()
238 status = status.rstrip()
239 status = re.sub(r"\t", " ", status)
240 # Ensure any continuation lines are marked so we can
241 # detect them in the status file to ensure it is parsable.
242 status = re.sub(r"\n", "\n" + self.record_prefix + " ", status)
243
244 msg = '%s\t%s\t%s\t%s' %(status_code, substr, operation, status)
245
246 status_file = os.path.join(self.resultdir, 'status')
mblighf1c52842007-10-16 15:21:38 +0000247 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