blob: f3a1e1729c71e8ab481961984a57150ee282734e [file] [log] [blame]
mblighc6b01ed2006-10-13 17:03:26 +00001#!/usr/bin/python
mbligh26b992b2008-02-19 15:46:21 +00002import os, re, md5, sys, email.Message, smtplib, datetime
mblighc2514542008-02-19 15:54:26 +00003
mbligh532cb272007-11-26 18:54:20 +00004client_bin = os.path.join(os.path.dirname(__file__), '../client/bin')
5sys.path.insert(0, os.path.abspath(client_bin))
6from autotest_utils import read_keyval
mblighbb7b8912006-10-08 03:59:02 +00007
mblighb10a60f2007-10-17 00:03:32 +00008user = re.compile(r'user(\s*)=')
9label = re.compile(r'label(\s*)=')
mblighb85e6b02006-10-08 17:20:56 +000010
mbligh006f2302007-09-13 20:46:46 +000011debug = True
12
mbligh74fc0462007-11-05 20:24:17 +000013# XXX: these mail bits came almost verbatim from mirror/mirror and this should
14# probably be refactored into another file and used by both.
15def mail(from_address, to_addresses, cc_addresses, subject, message_text):
mbligh08415d82007-11-24 19:23:34 +000016 # if passed a string for the to_addresses convert it to a tuple
17 if type(to_addresses) is str:
18 to_addresses = (to_addresses,)
mbligh74fc0462007-11-05 20:24:17 +000019
mbligh08415d82007-11-24 19:23:34 +000020 message = email.Message.Message()
21 message["To"] = ", ".join(to_addresses)
22 message["Cc"] = ", ".join(cc_addresses)
23 message["From"] = from_address
24 message["Subject"] = subject
25 message.set_payload(message_text)
mbligh74fc0462007-11-05 20:24:17 +000026
mbligh08415d82007-11-24 19:23:34 +000027 try:
28 sendmail(message.as_string())
29 except SendmailException, e:
30 server = smtplib.SMTP("localhost")
31 server.sendmail(from_address, to_addresses, cc_addresses, message.as_string())
32 server.quit()
mbligh74fc0462007-11-05 20:24:17 +000033
34
35MAIL = "sendmail"
36
37class SendmailException(Exception):
mbligh08415d82007-11-24 19:23:34 +000038 pass
mbligh74fc0462007-11-05 20:24:17 +000039
40def sendmail(message):
mbligh08415d82007-11-24 19:23:34 +000041 """Send an email using sendmail"""
42 # open a pipe to the mail program and
43 # write the data to the pipe
44 p = os.popen("%s -t" % MAIL, 'w')
45 p.write(message)
46 exitcode = p.close()
47 if exitcode:
48 raise SendmailException("Exit code: %s" % exitcode)
mbligh74fc0462007-11-05 20:24:17 +000049
50# XXX: End of code from mirror/mirror
51
52
mblighb85e6b02006-10-08 17:20:56 +000053def shorten_patch(long):
mbligh340c9682006-10-08 18:26:08 +000054 short = os.path.basename(long)
mblighb85e6b02006-10-08 17:20:56 +000055 short = re.sub(r'^patch-', '', short)
56 short = re.sub(r'\.(bz2|gz)$', '', short)
57 short = re.sub(r'\.patch$', '', short)
58 short = re.sub(r'\+', '_', short)
59 return short
60
mblighbb7b8912006-10-08 03:59:02 +000061
mbligh006f2302007-09-13 20:46:46 +000062def dprint(info):
63 if debug:
64 sys.stderr.write(str(info) + '\n')
65
66
mblighc2514542008-02-19 15:54:26 +000067def get_timestamp(mapping, field):
68 val = mapping.get(field, None)
mbligh26b992b2008-02-19 15:46:21 +000069 if val is not None:
70 val = datetime.datetime.fromtimestamp(int(val))
71 return val
72
73
mbligh71d340d2008-03-05 15:51:16 +000074class status_stack:
75 def __init__(self, job):
76 self.job = job
77 self.status_stack = [self.job.statuses[-1]]
78
79
80 def current_status(self):
81 return self.status_stack[-1]
82
83
84 def update(self, new_status):
85 if new_status not in self.job.statuses:
86 return
87 old = self.job.statuses.index(self.current_status())
88 new = self.job.statuses.index(new_status)
89 if new < old:
90 self.status_stack[-1] = new_status
91
92
93 def start(self):
94 self.status_stack.append(self.job.statuses[-1])
95
96
97 def end(self):
98 result = self.status_stack.pop()
99 if len(self.status_stack) == 0:
100 self.status_stack.append(self.job.statuses[-1])
101 return result
102
103
104
mblighe9cf9d42007-08-31 08:56:00 +0000105class job:
mbligh71d340d2008-03-05 15:51:16 +0000106 statuses = ['NOSTATUS', 'ERROR', 'ABORT', 'FAIL', 'WARN', 'GOOD',
107 'ALERT']
108
mbligh532cb272007-11-26 18:54:20 +0000109 def __init__(self, dir):
mblighe9cf9d42007-08-31 08:56:00 +0000110 self.dir = dir
mblighe9cf9d42007-08-31 08:56:00 +0000111 self.control = os.path.join(dir, "control")
mblighe8b37a92007-12-19 15:54:11 +0000112 self.status = os.path.join(dir, "status.log")
113 if not os.path.exists(self.status):
114 self.status = os.path.join(dir, "status")
mblighbb7b8912006-10-08 03:59:02 +0000115 self.variables = {}
mblighe9cf9d42007-08-31 08:56:00 +0000116 self.tests = []
mblighbb7b8912006-10-08 03:59:02 +0000117 self.kernel = None
118
mblighb10a60f2007-10-17 00:03:32 +0000119 # Get the user + tag info from the keyval file.
mbligh8e1ab172007-09-13 17:29:56 +0000120 try:
mbligh532cb272007-11-26 18:54:20 +0000121 keyval = read_keyval(dir)
122 print keyval
mbligh8e1ab172007-09-13 17:29:56 +0000123 except:
mbligh532cb272007-11-26 18:54:20 +0000124 keyval = {}
125 self.user = keyval.get('user', None)
126 self.label = keyval.get('label', None)
127 self.machine = keyval.get('hostname', None)
mblighe8b37a92007-12-19 15:54:11 +0000128 if self.machine:
129 assert ',' not in self.machine
mblighc2514542008-02-19 15:54:26 +0000130 self.queued_time = get_timestamp(keyval, 'job_queued')
131 self.started_time = get_timestamp(keyval, 'job_started')
132 self.finished_time = get_timestamp(keyval, 'job_finished')
mbligh7a41a862007-11-30 17:44:24 +0000133 self.machine_owner = keyval.get('owner', None)
mbligh532cb272007-11-26 18:54:20 +0000134
135 if not self.machine:
136 self.get_machine()
137
138 print 'MACHINE NAME: ' + self.machine
139 if not os.path.exists(self.status):
mbligh8e1ab172007-09-13 17:29:56 +0000140 return None
141
mbligh529f2f32007-08-30 11:22:50 +0000142 self.grope_status()
mblighbb7b8912006-10-08 03:59:02 +0000143
144
mbligh532cb272007-11-26 18:54:20 +0000145 def get_machine(self):
146 try:
mbligh7a41a862007-11-30 17:44:24 +0000147 hostname = os.path.join(self.dir, "sysinfo/hostname")
mbligh532cb272007-11-26 18:54:20 +0000148 self.machine = open(hostname, 'r').readline().rstrip()
149 return
150 except:
151 pass
152 try:
mbligh7a41a862007-11-30 17:44:24 +0000153 uname = os.path.join(self.dir, "sysinfo/uname_-a")
mbligh532cb272007-11-26 18:54:20 +0000154 self.machine = open(uname, 'r').readline().split()[1]
155 return
156 except:
157 pass
mbligh215a1662007-11-29 19:16:58 +0000158 raise "Could not figure out machine name"
mbligh71d340d2008-03-05 15:51:16 +0000159
mbligh532cb272007-11-26 18:54:20 +0000160
mblighd5c33db2006-10-08 21:34:16 +0000161 def grope_status(self):
mbligh215a1662007-11-29 19:16:58 +0000162 """
163 Note that what we're looking for here is level 1 groups
164 (ie end markers with 1 tab in front)
165
166 For back-compatiblity, we also count level 0 groups that
167 are not job-level events, if there's no start/end job level
168 markers: "START ---- ----"
169 """
mblighde7335d2007-09-26 16:53:20 +0000170 dprint('=====================================================')
171 dprint(self.dir)
172 dprint('=====================================================')
mbligh049f5e62007-09-30 02:00:14 +0000173 self.kernel = kernel(self.dir)
mbligh237bed32007-09-05 13:05:57 +0000174
mbligh40d021b2007-12-03 17:35:51 +0000175 reboot_inprogress = 0 # Saw reboot start and not finish
mbligh9bcfef72008-02-12 20:55:34 +0000176 boot_count = 0
apw569f4782007-12-13 19:32:53 +0000177 alert_pending = None # Saw an ALERT for this test
mblighde7335d2007-09-26 16:53:20 +0000178 group_subdir = None
mbligh71d340d2008-03-05 15:51:16 +0000179 group_status = status_stack(self)
mbligh215a1662007-11-29 19:16:58 +0000180 sought_level = 0 # we log events at indent level 0
mbligh237bed32007-09-05 13:05:57 +0000181 for line in open(self.status, 'r').readlines():
mbligh215a1662007-11-29 19:16:58 +0000182 dprint('\nSTATUS: ' + line.rstrip())
183 if not re.search(r'^\t*\S', line):
184 dprint('Continuation line, ignoring')
mblighde7335d2007-09-26 16:53:20 +0000185 continue # ignore continuation lines
mbligh215a1662007-11-29 19:16:58 +0000186 if re.search(r'^START\t----\t----', line):
187 sought_level = 1
188 # we now log events at indent level 1
189 dprint('Found job level start marker. Looking for level 1 groups now')
190 continue
191 indent = re.search('^(\t*)', line).group(0).count('\t')
mblighc2514542008-02-19 15:54:26 +0000192 line = line.lstrip()
193 line = line.rstrip('\n')
mbligh215a1662007-11-29 19:16:58 +0000194 if line.startswith('START\t'):
mblighde7335d2007-09-26 16:53:20 +0000195 group_subdir = None
mbligh71d340d2008-03-05 15:51:16 +0000196 group_status.start()
mbligh215a1662007-11-29 19:16:58 +0000197 dprint('start line, ignoring')
mblighde7335d2007-09-26 16:53:20 +0000198 continue # ignore start lines
199 reason = None
mblighc2514542008-02-19 15:54:26 +0000200 if line.startswith('END '):
201 elements = line.split('\t')
202 elements[0] = elements[0][4:] # remove 'END '
mblighbafa6ef2008-01-10 16:36:52 +0000203 end = True
mblighde7335d2007-09-26 16:53:20 +0000204 else:
mblighc2514542008-02-19 15:54:26 +0000205 elements = line.split('\t')
mblighbafa6ef2008-01-10 16:36:52 +0000206 end = False
mblighde7335d2007-09-26 16:53:20 +0000207 (status, subdir, testname, reason) = elements[0:4]
mblighc2514542008-02-19 15:54:26 +0000208 status, subdir, testname = elements[:3]
209 reason = elements[-1]
210 optional_fields = dict(element.split('=', 1)
211 for element in elements[3:-1])
mbligh71d340d2008-03-05 15:51:16 +0000212 group_status.update(status)
mblighc2514542008-02-19 15:54:26 +0000213 dprint('GROPE_STATUS: ' +
mbligh71d340d2008-03-05 15:51:16 +0000214 str([group_status.current_status(), status,
215 subdir, testname, reason]))
apw569f4782007-12-13 19:32:53 +0000216 if status == 'ALERT':
217 dprint('job level alert, recording')
218 alert_pending = reason
219 continue
mblighbafa6ef2008-01-10 16:36:52 +0000220 if testname == 'Autotest.install' and status == 'GOOD':
221 dprint('Sucessful autotest install, ignoring')
mbligh59a479f2007-11-24 19:24:05 +0000222 continue
mblighbafa6ef2008-01-10 16:36:52 +0000223 if testname == '----':
224 if status == 'ABORT' and not end:
225 testname = 'JOB'
226 else:
227 dprint('job level event, ignoring')
228 # This is a job level event, not a test
229 continue
mblighde7335d2007-09-26 16:53:20 +0000230 ################################################
231 # REMOVE THIS SECTION ONCE OLD FORMAT JOBS ARE GONE
232 ################################################
mbligh215a1662007-11-29 19:16:58 +0000233 if re.search(r'^(GOOD|FAIL|WARN) ', line):
mbligh71d340d2008-03-05 15:51:16 +0000234 status, testname, reason = line.split(None, 2)
235
mblighde7335d2007-09-26 16:53:20 +0000236 if testname.startswith('kernel.'):
237 subdir = 'build'
238 else:
239 subdir = testname
240 if testname.startswith('completed'):
241 raise 'testname is crap'
242 ################################################
243 if subdir == '----':
244 subdir = None
245 if line.startswith('END'):
246 subdir = group_subdir
mblighbafa6ef2008-01-10 16:36:52 +0000247 if indent != sought_level and status != 'ABORT':
248 # we're in a block group
mblighde7335d2007-09-26 16:53:20 +0000249 if subdir:
mbligh215a1662007-11-29 19:16:58 +0000250 dprint('set group_subdir: %s' % subdir)
mblighde7335d2007-09-26 16:53:20 +0000251 group_subdir = subdir
mbligh215a1662007-11-29 19:16:58 +0000252 dprint('incorrect indent level %d != %d, ignoring' % (indent, sought_level))
mblighde7335d2007-09-26 16:53:20 +0000253 continue
mblighe0968462008-02-27 16:29:31 +0000254 if not re.search(r'^(boot(\.\d+)?$|kernel\.)', testname):
mblighde7335d2007-09-26 16:53:20 +0000255 # This is a real test
256 if subdir and subdir.count('.'):
257 # eg dbench.ext3
258 testname = subdir
mbligh0e21c872008-01-03 16:36:56 +0000259 if testname == 'reboot.start':
260 dprint('reboot start event, ignoring')
261 reboot_inprogress = 1
262 continue
mbligh215a1662007-11-29 19:16:58 +0000263 if testname == 'reboot.verify':
mbligh9bcfef72008-02-12 20:55:34 +0000264 testname = 'boot.%d' % boot_count
mbligh0e21c872008-01-03 16:36:56 +0000265 dprint('reboot verified')
mbligh40d021b2007-12-03 17:35:51 +0000266 reboot_inprogress = 0
mbligh9bcfef72008-02-12 20:55:34 +0000267 boot_count += 1
apw569f4782007-12-13 19:32:53 +0000268 if alert_pending:
269 status = 'ALERT'
270 reason = alert_pending
271 alert_pending = None
mbligh71d340d2008-03-05 15:51:16 +0000272 if status in self.statuses:
273 dprint('Adding: %s\nSubdir:%s\nTestname:%s\n%s'
274 % (group_status.current_status(),
275 subdir, testname, reason))
mbligh05d81212008-01-10 16:37:35 +0000276 else:
277 dprint('WARNING: Invalid status code. Ignoring')
278 continue
mblighc2514542008-02-19 15:54:26 +0000279
280 finished_time = get_timestamp(optional_fields,
281 'timestamp')
mbligh71d340d2008-03-05 15:51:16 +0000282 test_status = group_status.end()
283 self.tests.append(test(subdir, testname, test_status,
mblighc2514542008-02-19 15:54:26 +0000284 reason, self.kernel, self,
285 finished_time))
mblighde7335d2007-09-26 16:53:20 +0000286 dprint('')
mbligh05d81212008-01-10 16:37:35 +0000287
mbligh40d021b2007-12-03 17:35:51 +0000288 if reboot_inprogress:
mbligh02ec1ba2008-02-21 16:15:09 +0000289 testname = 'boot.%d' % boot_count
mbligh40d021b2007-12-03 17:35:51 +0000290 dprint('Adding: %s\nSubdir:%s\nTestname:%s\n%s' %
mbligh02ec1ba2008-02-21 16:15:09 +0000291 ('----', subdir, testname, reason))
292 self.tests.append(test('----', testname, 'ABORT',
mbligh40d021b2007-12-03 17:35:51 +0000293 'machine did not return from reboot',
294 self.kernel, self))
295 dprint('')
mblighe9cf9d42007-08-31 08:56:00 +0000296
297
298class kernel:
mbligh049f5e62007-09-30 02:00:14 +0000299 def __init__(self, topdir):
mblighe8b37a92007-12-19 15:54:11 +0000300 self.base = 'UNKNOWN'
mblighe9cf9d42007-08-31 08:56:00 +0000301 self.patches = []
mblighe9cf9d42007-08-31 08:56:00 +0000302 patch_hashes = []
mbligh049f5e62007-09-30 02:00:14 +0000303 # HACK. we don't have proper build tags in the status file yet
304 # so we hardcode build/ and do it at the start of the job
mbligh9c39d152007-11-24 19:11:58 +0000305 build_log = os.path.join(topdir, 'build/debug/build_log')
mbligh049f5e62007-09-30 02:00:14 +0000306
mbligh9c39d152007-11-24 19:11:58 +0000307 if os.path.exists(build_log):
308 for line in open(build_log, 'r'):
mbligh049f5e62007-09-30 02:00:14 +0000309 print line
310 (type, rest) = line.split(': ', 1)
311 words = rest.split()
312 if type == 'BASE':
313 self.base = words[0]
314 if type == 'PATCH':
315 print words
316 self.patches.append(patch(*words[0:]))
apw7a7316b2008-02-21 17:42:05 +0000317 patch_hashes.append(words[2])
mbligh9c39d152007-11-24 19:11:58 +0000318 else:
319 for sysinfo in ['sysinfo/reboot1', 'sysinfo']:
320 uname_file = os.path.join(topdir, sysinfo, 'uname_-a')
321 if not os.path.exists(uname_file):
322 continue
323 uname = open(uname_file, 'r').readline().split()
324 self.base = uname[2]
mbligh215a1662007-11-29 19:16:58 +0000325 re.sub(r'-autotest$', '', self.base)
mbligh9c39d152007-11-24 19:11:58 +0000326 break
327 print 'kernel.__init__() found kernel version %s' % self.base
mblighe8b37a92007-12-19 15:54:11 +0000328 if self.base == 'UNKNOWN':
329 self.kernel_hash = 'UNKNOWN'
330 else:
mbligh52f97442007-09-14 17:43:28 +0000331 self.kernel_hash = self.get_kver_hash(self.base, patch_hashes)
mblighe9cf9d42007-08-31 08:56:00 +0000332
333
mbligh237bed32007-09-05 13:05:57 +0000334 def get_kver_hash(self, base, patch_hashes):
mblighe9cf9d42007-08-31 08:56:00 +0000335 """\
336 Calculate a hash representing the unique combination of
337 the kernel base version plus
338 """
339 key_string = ','.join([base] + patch_hashes)
340 return md5.new(key_string).hexdigest()
341
342
mbligh237bed32007-09-05 13:05:57 +0000343class patch:
344 def __init__(self, spec, reference=None, hash=None):
345 # NEITHER OF THE ABOVE SHOULD HAVE DEFAULTS!!!! HACK HACK
346 if not reference:
347 reference = spec
348 print 'PATCH::%s %s %s' % (spec, reference, hash)
349 self.spec = spec
350 self.reference = reference
351 self.hash = hash
352
353
mblighe9cf9d42007-08-31 08:56:00 +0000354class test:
mblighc2514542008-02-19 15:54:26 +0000355 def __init__(self, subdir, testname, status, reason, kernel, job,
356 finished_time=None):
mblighde940942007-11-05 17:25:56 +0000357 # NOTE: subdir may be none here for lines that aren't an
358 # actual test
mbligh2bd48872007-09-20 18:32:25 +0000359 self.subdir = subdir
mblighde7335d2007-09-26 16:53:20 +0000360 self.testname = testname
mblighe9cf9d42007-08-31 08:56:00 +0000361 self.status = status
362 self.reason = reason
mblighde940942007-11-05 17:25:56 +0000363 self.version = None
364 self.keyval = None
mbligh994a23d2007-10-25 15:28:58 +0000365
mblighde7335d2007-09-26 16:53:20 +0000366 if subdir:
mblighde940942007-11-05 17:25:56 +0000367 keyval = os.path.join(job.dir, subdir, 'results/keyval')
368 if os.path.exists(keyval):
369 self.keyval = keyval
370 keyval2 = os.path.join(job.dir, subdir, 'keyval')
371 if os.path.exists(keyval2):
372 self.version = open(keyval2, 'r').readline().split('=')[1]
mblighde7335d2007-09-26 16:53:20 +0000373 else:
374 self.keyval = None
mblighe9cf9d42007-08-31 08:56:00 +0000375 self.iterations = []
mbligh237bed32007-09-05 13:05:57 +0000376 self.kernel = kernel
mbligh8e1ab172007-09-13 17:29:56 +0000377 self.machine = job.machine
mblighc2514542008-02-19 15:54:26 +0000378 self.finished_time = finished_time
mblighe9cf9d42007-08-31 08:56:00 +0000379
mblighde7335d2007-09-26 16:53:20 +0000380 dprint("PARSING TEST %s %s %s" % (subdir, testname, self.keyval))
mbligh994a23d2007-10-25 15:28:58 +0000381
mblighde7335d2007-09-26 16:53:20 +0000382 if not self.keyval:
mblighe9cf9d42007-08-31 08:56:00 +0000383 return
384 count = 1
385 lines = []
386 for line in open(self.keyval, 'r').readlines():
mbligh2bd48872007-09-20 18:32:25 +0000387 if not re.search('\S', line): # blank line
mblighe9cf9d42007-08-31 08:56:00 +0000388 self.iterations.append(iteration(count, lines))
389 lines = []
390 count += 1
mblighe9cf9d42007-08-31 08:56:00 +0000391 else:
392 lines.append(line)
393 if lines:
394 self.iterations.append(iteration(count, lines))
395
396
397class iteration:
398 def __init__(self, index, lines):
399 self.index = index
400 self.keyval = {}
401
mbligh2bd48872007-09-20 18:32:25 +0000402 dprint("ADDING ITERATION %d" % index)
mblighe9cf9d42007-08-31 08:56:00 +0000403 for line in lines:
mbligh4921b982007-12-03 17:47:45 +0000404 line = line.rstrip();
mblighe9cf9d42007-08-31 08:56:00 +0000405 (key, value) = line.split('=', 1)
406 self.keyval[key] = value