blob: 7448c4fd28575083e1a0aee83ce57c37f569c999 [file] [log] [blame]
jadmanskif7fa2cc2008-10-01 14:13:23 +00001import os, re, time
jadmanskicc549172008-05-21 18:11:51 +00002
jadmanski6e8bf752008-05-14 00:17:48 +00003from autotest_lib.tko import models, status_lib, utils as tko_utils
4from autotest_lib.tko.parsers import base, version_0
5
6
jadmanski6f6299a2008-12-02 15:22:21 +00007class job(version_0.job):
8 def exit_status(self):
jadmanski165fe4c2009-01-07 17:06:00 +00009 # find the .autoserv_execute path
10 top_dir = tko_utils.find_toplevel_job_dir(self.dir)
11 if not top_dir:
12 return "ABORT"
13 execute_path = os.path.join(top_dir, ".autoserv_execute")
14
jadmanski6f6299a2008-12-02 15:22:21 +000015 # if for some reason we can't read the status code, assume disaster
jadmanski6f6299a2008-12-02 15:22:21 +000016 if not os.path.exists(execute_path):
17 return "ABORT"
18 lines = open(execute_path).readlines()
19 if len(lines) < 2:
20 return "ABORT"
21 try:
22 status_code = int(lines[1])
23 except ValueError:
24 return "ABORT"
25
26 if not os.WIFEXITED(status_code):
27 # looks like a signal - an ABORT
28 return "ABORT"
29 elif os.WEXITSTATUS(status_code) != 0:
30 # looks like a non-zero exit - a failure
31 return "FAIL"
32 else:
33 # looks like exit code == 0
34 return "GOOD"
35
36
jadmanski6e8bf752008-05-14 00:17:48 +000037class kernel(models.kernel):
jadmanski0afbb632008-06-06 21:10:57 +000038 def __init__(self, base, patches):
39 if base:
40 patches = [patch(*p.split()) for p in patches]
41 hashes = [p.hash for p in patches]
42 kernel_hash = self.compute_hash(base, hashes)
43 else:
44 base = "UNKNOWN"
45 patches = []
46 kernel_hash = "UNKNOWN"
47 super(kernel, self).__init__(base, patches, kernel_hash)
jadmanski6e8bf752008-05-14 00:17:48 +000048
49
jadmanskicc549172008-05-21 18:11:51 +000050class test(models.test):
jadmanski0afbb632008-06-06 21:10:57 +000051 @staticmethod
52 def load_iterations(keyval_path):
53 return iteration.load_from_keyval(keyval_path)
jadmanskicc549172008-05-21 18:11:51 +000054
55
56class iteration(models.iteration):
jadmanski0afbb632008-06-06 21:10:57 +000057 @staticmethod
58 def parse_line_into_dicts(line, attr_dict, perf_dict):
jadmanskid2e0f0c2009-07-20 17:52:57 +000059 key, val_type, value = "", "", ""
60
mbligh1ef218d2009-08-03 16:57:56 +000061 # figure out what the key, value and keyval type are
jadmanski0afbb632008-06-06 21:10:57 +000062 typed_match = re.search("^([^=]*)\{(\w*)\}=(.*)$", line)
63 if typed_match:
64 key, val_type, value = typed_match.groups()
jadmanski0afbb632008-06-06 21:10:57 +000065 else:
66 # old-fashioned untyped match, assume perf
67 untyped_match = re.search("^([^=]*)=(.*)$", line)
jadmanskid2e0f0c2009-07-20 17:52:57 +000068 if untyped_match:
69 key, value = untyped_match.groups()
70 val_type = "perf"
71
72 # parse the actual value into a dict
Eric Li861b2d52011-02-04 14:50:35 -080073 try:
74 if val_type == "attr":
75 attr_dict[key] = value
76 elif val_type == "perf":
77 perf_dict[key] = float(value)
78 else:
79 raise ValueError
80 except ValueError:
jadmanskid2e0f0c2009-07-20 17:52:57 +000081 msg = ("WARNING: line '%s' found in test "
82 "iteration keyval could not be parsed")
83 msg %= line
84 tko_utils.dprint(msg)
jadmanskicc549172008-05-21 18:11:51 +000085
86
jadmanski6e8bf752008-05-14 00:17:48 +000087class status_line(version_0.status_line):
jadmanski807490c2008-09-15 19:15:02 +000088 def __init__(self, indent, status, subdir, testname, reason,
89 optional_fields):
90 # handle INFO fields
91 if status == "INFO":
92 self.type = "INFO"
93 self.indent = indent
94 self.status = self.subdir = self.testname = self.reason = None
95 self.optional_fields = optional_fields
96 else:
97 # everything else is backwards compatible
98 super(status_line, self).__init__(indent, status, subdir,
99 testname, reason,
100 optional_fields)
101
102
jadmanski0afbb632008-06-06 21:10:57 +0000103 def is_successful_reboot(self, current_status):
104 # make sure this is a reboot line
105 if self.testname != "reboot":
106 return False
jadmanski6e8bf752008-05-14 00:17:48 +0000107
jadmanski0afbb632008-06-06 21:10:57 +0000108 # make sure this was not a failure
jadmanskiba1fa662008-07-11 21:18:30 +0000109 if status_lib.is_worse_than_or_equal_to(current_status, "FAIL"):
jadmanski0afbb632008-06-06 21:10:57 +0000110 return False
jadmanski6e8bf752008-05-14 00:17:48 +0000111
jadmanski0afbb632008-06-06 21:10:57 +0000112 # it must have been a successful reboot
113 return True
jadmanski6e8bf752008-05-14 00:17:48 +0000114
115
jadmanski0afbb632008-06-06 21:10:57 +0000116 def get_kernel(self):
117 # get the base kernel version
118 fields = self.optional_fields
mblighabe869a2009-03-25 22:09:59 +0000119 base = re.sub("-autotest$", "", fields.get("kernel", ""))
jadmanski0afbb632008-06-06 21:10:57 +0000120 # get a list of patches
121 patches = []
122 patch_index = 0
123 while ("patch%d" % patch_index) in fields:
124 patches.append(fields["patch%d" % patch_index])
125 patch_index += 1
126 # create a new kernel instance
127 return kernel(base, patches)
jadmanski6e8bf752008-05-14 00:17:48 +0000128
129
jadmanski0afbb632008-06-06 21:10:57 +0000130 def get_timestamp(self):
131 return tko_utils.get_timestamp(self.optional_fields,
132 "timestamp")
jadmanski6e8bf752008-05-14 00:17:48 +0000133
134
135# the default implementations from version 0 will do for now
jadmanski6e8bf752008-05-14 00:17:48 +0000136patch = version_0.patch
jadmanski6e8bf752008-05-14 00:17:48 +0000137
138
139class parser(base.parser):
jadmanski0afbb632008-06-06 21:10:57 +0000140 @staticmethod
141 def make_job(dir):
142 return job(dir)
jadmanski6e8bf752008-05-14 00:17:48 +0000143
144
jadmanski0afbb632008-06-06 21:10:57 +0000145 @staticmethod
mblighb22c21f2008-11-27 00:40:38 +0000146 def make_dummy_abort(indent, subdir, testname, timestamp, reason):
jadmanski0afbb632008-06-06 21:10:57 +0000147 indent = "\t" * indent
jadmanskifd3ba2b2008-07-28 19:30:54 +0000148 if not subdir:
149 subdir = "----"
150 if not testname:
151 testname = "----"
mblighb22c21f2008-11-27 00:40:38 +0000152
153 # There is no guarantee that this will be set.
154 timestamp_field = ''
155 if timestamp:
156 timestamp_field = '\ttimestamp=%s' % timestamp
157
158 msg = indent + "END ABORT\t%s\t%s%s\t%s"
159 return msg % (subdir, testname, timestamp_field, reason)
jadmanski6e8bf752008-05-14 00:17:48 +0000160
161
jadmanskib69f2502008-08-27 19:49:30 +0000162 @staticmethod
mblighb22c21f2008-11-27 00:40:38 +0000163 def put_back_line_and_abort(
164 line_buffer, line, indent, subdir, timestamp, reason):
jadmanskib69f2502008-08-27 19:49:30 +0000165 tko_utils.dprint("Unexpected indent regression, aborting")
166 line_buffer.put_back(line)
mblighb22c21f2008-11-27 00:40:38 +0000167 abort = parser.make_dummy_abort(
168 indent, subdir, subdir, timestamp, reason)
jadmanskib69f2502008-08-27 19:49:30 +0000169 line_buffer.put_back(abort)
170
171
jadmanski0afbb632008-06-06 21:10:57 +0000172 def state_iterator(self, buffer):
jadmanskifd3ba2b2008-07-28 19:30:54 +0000173 line = None
jadmanski0afbb632008-06-06 21:10:57 +0000174 new_tests = []
jadmanskiba1fa662008-07-11 21:18:30 +0000175 job_count, boot_count = 0, 0
jadmanski0afbb632008-06-06 21:10:57 +0000176 min_stack_size = 0
177 stack = status_lib.status_stack()
178 current_kernel = kernel("", []) # UNKNOWN
jadmanskic69138f2008-12-17 15:44:22 +0000179 current_status = status_lib.statuses[-1]
180 current_reason = None
jadmanski0afbb632008-06-06 21:10:57 +0000181 started_time_stack = [None]
182 subdir_stack = [None]
jadmanski74eebf32008-07-15 20:04:42 +0000183 running_test = None
jadmanskiaaac45e2009-05-06 20:25:46 +0000184 running_reasons = set()
jadmanski6f6299a2008-12-02 15:22:21 +0000185 yield [] # we're ready to start running
186
187 # create a RUNNING SERVER_JOB entry to represent the entire test
188 running_job = test.parse_partial_test(self.job, "----", "SERVER_JOB",
189 "", current_kernel,
190 self.job.started_time)
191 new_tests.append(running_job)
jadmanski6e8bf752008-05-14 00:17:48 +0000192
jadmanski0afbb632008-06-06 21:10:57 +0000193 while True:
194 # are we finished with parsing?
195 if buffer.size() == 0 and self.finished:
196 if stack.size() == 0:
197 break
198 # we have status lines left on the stack,
199 # we need to implicitly abort them first
jadmanskifd3ba2b2008-07-28 19:30:54 +0000200 tko_utils.dprint('\nUnexpected end of job, aborting')
201 abort_subdir_stack = list(subdir_stack)
jadmanskif7fa2cc2008-10-01 14:13:23 +0000202 if self.job.aborted_by:
203 reason = "Job aborted by %s" % self.job.aborted_by
204 reason += self.job.aborted_on.strftime(
205 " at %b %d %H:%M:%S")
206 else:
207 reason = "Job aborted unexpectedly"
mblighb22c21f2008-11-27 00:40:38 +0000208
209 timestamp = line.optional_fields.get('timestamp')
jadmanski0afbb632008-06-06 21:10:57 +0000210 for i in reversed(xrange(stack.size())):
jadmanskifd3ba2b2008-07-28 19:30:54 +0000211 if abort_subdir_stack:
212 subdir = abort_subdir_stack.pop()
213 else:
214 subdir = None
mblighb22c21f2008-11-27 00:40:38 +0000215 abort = self.make_dummy_abort(
216 i, subdir, subdir, timestamp, reason)
jadmanskifd3ba2b2008-07-28 19:30:54 +0000217 buffer.put(abort)
jadmanski6e8bf752008-05-14 00:17:48 +0000218
jadmanski0afbb632008-06-06 21:10:57 +0000219 # stop processing once the buffer is empty
220 if buffer.size() == 0:
221 yield new_tests
222 new_tests = []
223 continue
jadmanski6e8bf752008-05-14 00:17:48 +0000224
jadmanski0afbb632008-06-06 21:10:57 +0000225 # reinitialize the per-iteration state
226 started_time = None
227 finished_time = None
jadmanski6e8bf752008-05-14 00:17:48 +0000228
jadmanski0afbb632008-06-06 21:10:57 +0000229 # get the next line
mbligh51ee7c72008-11-24 17:10:14 +0000230 raw_line = status_lib.clean_raw_line(buffer.get())
jadmanski0afbb632008-06-06 21:10:57 +0000231 tko_utils.dprint('\nSTATUS: ' + raw_line.strip())
232 line = status_line.parse_line(raw_line)
233 if line is None:
234 tko_utils.dprint('non-status line, ignoring')
235 continue
jadmanski6e8bf752008-05-14 00:17:48 +0000236
jadmanskib82cb142009-02-04 18:33:08 +0000237 # do an initial sanity check of the indentation
238 expected_indent = stack.size()
239 if line.type == "END":
240 expected_indent -= 1
241 if line.indent < expected_indent:
242 # ABORT the current level if indentation was unexpectedly low
243 self.put_back_line_and_abort(
244 buffer, raw_line, stack.size() - 1, subdir_stack[-1],
245 line.optional_fields.get("timestamp"), line.reason)
246 continue
247 elif line.indent > expected_indent:
248 # ignore the log if the indent was unexpectedly high
249 tko_utils.dprint("unexpected extra indentation, ignoring")
250 continue
251
252
jadmanski0afbb632008-06-06 21:10:57 +0000253 # initial line processing
254 if line.type == "START":
255 stack.start()
jadmanski74eebf32008-07-15 20:04:42 +0000256 started_time = line.get_timestamp()
jadmanski717bb992008-10-08 14:29:23 +0000257 if (line.testname is None and line.subdir is None
258 and not running_test):
jadmanski74eebf32008-07-15 20:04:42 +0000259 # we just started a client, all tests are relative to here
jadmanski0afbb632008-06-06 21:10:57 +0000260 min_stack_size = stack.size()
jadmanski43b72102010-04-30 21:22:28 +0000261 # start a "RUNNING" CLIENT_JOB entry
262 job_name = "CLIENT_JOB.%d" % job_count
jadmanskid1b0a132010-05-04 19:50:30 +0000263 running_client = test.parse_partial_test(self.job, None,
264 job_name,
265 "", current_kernel,
266 started_time)
jadmanski43b72102010-04-30 21:22:28 +0000267 msg = "RUNNING: %s\n%s\n"
jadmanskid1b0a132010-05-04 19:50:30 +0000268 msg %= (running_client.status, running_client.testname)
jadmanski43b72102010-04-30 21:22:28 +0000269 tko_utils.dprint(msg)
jadmanskid1b0a132010-05-04 19:50:30 +0000270 new_tests.append(running_client)
jadmanski717bb992008-10-08 14:29:23 +0000271 elif stack.size() == min_stack_size + 1 and not running_test:
jadmanski74eebf32008-07-15 20:04:42 +0000272 # we just started a new test, insert a running record
jadmanskiaaac45e2009-05-06 20:25:46 +0000273 running_reasons = set()
jadmanski78395482009-03-10 04:21:03 +0000274 if line.reason:
jadmanskiaaac45e2009-05-06 20:25:46 +0000275 running_reasons.add(line.reason)
jadmanski74eebf32008-07-15 20:04:42 +0000276 running_test = test.parse_partial_test(self.job,
277 line.subdir,
278 line.testname,
279 line.reason,
280 current_kernel,
281 started_time)
282 msg = "RUNNING: %s\nSubdir: %s\nTestname: %s\n%s"
283 msg %= (running_test.status, running_test.subdir,
284 running_test.testname, running_test.reason)
285 tko_utils.dprint(msg)
286 new_tests.append(running_test)
287 started_time_stack.append(started_time)
jadmanski0afbb632008-06-06 21:10:57 +0000288 subdir_stack.append(line.subdir)
289 continue
jadmanski807490c2008-09-15 19:15:02 +0000290 elif line.type == "INFO":
Eric Li6f27d4f2010-09-29 10:55:17 -0700291 fields = line.optional_fields
jadmanski807490c2008-09-15 19:15:02 +0000292 # update the current kernel if one is defined in the info
Eric Li6f27d4f2010-09-29 10:55:17 -0700293 if "kernel" in fields:
jadmanski807490c2008-09-15 19:15:02 +0000294 current_kernel = line.get_kernel()
Eric Li6f27d4f2010-09-29 10:55:17 -0700295 # update the SERVER_JOB reason if one was logged for an abort
296 if "job_abort_reason" in fields:
297 running_job.reason = fields["job_abort_reason"]
298 new_tests.append(running_job)
jadmanski807490c2008-09-15 19:15:02 +0000299 continue
jadmanski0afbb632008-06-06 21:10:57 +0000300 elif line.type == "STATUS":
jadmanskib69f2502008-08-27 19:49:30 +0000301 # update the stacks
302 if line.subdir and stack.size() > min_stack_size:
jadmanski0afbb632008-06-06 21:10:57 +0000303 subdir_stack[-1] = line.subdir
jadmanskic66e93c2008-07-29 21:25:22 +0000304 # update the status, start and finished times
305 stack.update(line.status)
jadmanskib6837962009-04-21 14:21:00 +0000306 if status_lib.is_worse_than_or_equal_to(line.status,
jadmanski262e1ab2009-04-01 18:27:38 +0000307 current_status):
jadmanskic69138f2008-12-17 15:44:22 +0000308 if line.reason:
jadmanski78395482009-03-10 04:21:03 +0000309 # update the status of a currently running test
310 if running_test:
jadmanskiaaac45e2009-05-06 20:25:46 +0000311 running_reasons.add(line.reason)
jadmanski1f99f672009-07-01 16:23:09 +0000312 running_reasons = tko_utils.drop_redundant_messages(
313 running_reasons)
jadmanskiaaac45e2009-05-06 20:25:46 +0000314 sorted_reasons = sorted(running_reasons)
315 running_test.reason = ", ".join(sorted_reasons)
jadmanskib6837962009-04-21 14:21:00 +0000316 current_reason = running_test.reason
jadmanski78395482009-03-10 04:21:03 +0000317 new_tests.append(running_test)
318 msg = "update RUNNING reason: %s" % line.reason
319 tko_utils.dprint(msg)
jadmanskib6837962009-04-21 14:21:00 +0000320 else:
321 current_reason = line.reason
jadmanskic69138f2008-12-17 15:44:22 +0000322 current_status = stack.current_status()
jadmanskic66e93c2008-07-29 21:25:22 +0000323 started_time = None
324 finished_time = line.get_timestamp()
jadmanski0986b252009-04-01 18:26:59 +0000325 # if this is a non-test entry there's nothing else to do
326 if line.testname is None and line.subdir is None:
327 continue
jadmanski0afbb632008-06-06 21:10:57 +0000328 elif line.type == "END":
jadmanskic66e93c2008-07-29 21:25:22 +0000329 # grab the current subdir off of the subdir stack, or, if this
330 # is the end of a job, just pop it off
jadmanski717bb992008-10-08 14:29:23 +0000331 if (line.testname is None and line.subdir is None
332 and not running_test):
jadmanski0afbb632008-06-06 21:10:57 +0000333 min_stack_size = stack.size() - 1
334 subdir_stack.pop()
335 else:
336 line.subdir = subdir_stack.pop()
jadmanski64e63452009-06-10 17:22:01 +0000337 if not subdir_stack[-1] and stack.size() > min_stack_size:
338 subdir_stack[-1] = line.subdir
jadmanskic66e93c2008-07-29 21:25:22 +0000339 # update the status, start and finished times
jadmanski0afbb632008-06-06 21:10:57 +0000340 stack.update(line.status)
jadmanski0afbb632008-06-06 21:10:57 +0000341 current_status = stack.end()
jadmanski29e61ce2008-08-22 17:38:28 +0000342 if stack.size() > min_stack_size:
343 stack.update(current_status)
jadmanskic69138f2008-12-17 15:44:22 +0000344 current_status = stack.current_status()
jadmanskic66e93c2008-07-29 21:25:22 +0000345 started_time = started_time_stack.pop()
346 finished_time = line.get_timestamp()
347 # update the current kernel
jadmanski0afbb632008-06-06 21:10:57 +0000348 if line.is_successful_reboot(current_status):
349 current_kernel = line.get_kernel()
jadmanskic66e93c2008-07-29 21:25:22 +0000350 # adjust the testname if this is a reboot
jadmanskic8034f02008-07-14 19:10:47 +0000351 if line.testname == "reboot" and line.subdir is None:
jadmanski0afbb632008-06-06 21:10:57 +0000352 line.testname = "boot.%d" % boot_count
jadmanskic66e93c2008-07-29 21:25:22 +0000353 else:
354 assert False
jadmanski6e8bf752008-05-14 00:17:48 +0000355
jadmanski0afbb632008-06-06 21:10:57 +0000356 # have we just finished a test?
357 if stack.size() <= min_stack_size:
358 # if there was no testname, just use the subdir
359 if line.testname is None:
360 line.testname = line.subdir
jadmanskib14dc7a2008-07-11 23:14:01 +0000361 # if there was no testname or subdir, use 'CLIENT_JOB'
jadmanski0afbb632008-06-06 21:10:57 +0000362 if line.testname is None:
jadmanskib14dc7a2008-07-11 23:14:01 +0000363 line.testname = "CLIENT_JOB.%d" % job_count
jadmanskid1b0a132010-05-04 19:50:30 +0000364 running_test = running_client
jadmanskiba1fa662008-07-11 21:18:30 +0000365 job_count += 1
366 if not status_lib.is_worse_than_or_equal_to(
367 current_status, "ABORT"):
368 # a job hasn't really failed just because some of the
369 # tests it ran have
370 current_status = "GOOD"
jadmanski6e8bf752008-05-14 00:17:48 +0000371
jadmanskic69138f2008-12-17 15:44:22 +0000372 if not current_reason:
373 current_reason = line.reason
jadmanski0afbb632008-06-06 21:10:57 +0000374 new_test = test.parse_test(self.job,
375 line.subdir,
376 line.testname,
377 current_status,
jadmanskic69138f2008-12-17 15:44:22 +0000378 current_reason,
jadmanski0afbb632008-06-06 21:10:57 +0000379 current_kernel,
380 started_time,
jadmanski74eebf32008-07-15 20:04:42 +0000381 finished_time,
382 running_test)
383 running_test = None
jadmanskid6f5c592009-01-10 00:26:21 +0000384 current_status = status_lib.statuses[-1]
jadmanskic69138f2008-12-17 15:44:22 +0000385 current_reason = None
jadmanski1ce9fbb2008-10-21 16:25:05 +0000386 if new_test.testname == ("boot.%d" % boot_count):
387 boot_count += 1
jadmanski0afbb632008-06-06 21:10:57 +0000388 msg = "ADD: %s\nSubdir: %s\nTestname: %s\n%s"
389 msg %= (new_test.status, new_test.subdir,
390 new_test.testname, new_test.reason)
391 tko_utils.dprint(msg)
392 new_tests.append(new_test)
jadmanski6e8bf752008-05-14 00:17:48 +0000393
jadmanski6f6299a2008-12-02 15:22:21 +0000394 # the job is finished, produce the final SERVER_JOB entry and exit
395 final_job = test.parse_test(self.job, "----", "SERVER_JOB",
Eric Li6f27d4f2010-09-29 10:55:17 -0700396 self.job.exit_status(), running_job.reason,
jadmanski6f6299a2008-12-02 15:22:21 +0000397 current_kernel,
398 self.job.started_time,
399 self.job.finished_time,
400 running_job)
401 new_tests.append(final_job)
jadmanski0afbb632008-06-06 21:10:57 +0000402 yield new_tests