1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# pylint: disable=missing-docstring 3*9c5db199SXin Liimport os 4*9c5db199SXin Liimport re 5*9c5db199SXin Li 6*9c5db199SXin Liimport common 7*9c5db199SXin Lifrom autotest_lib.tko import models 8*9c5db199SXin Lifrom autotest_lib.tko import status_lib 9*9c5db199SXin Lifrom autotest_lib.tko import utils as tko_utils 10*9c5db199SXin Lifrom autotest_lib.tko.parsers import base 11*9c5db199SXin Li 12*9c5db199SXin Liclass NoHostnameError(Exception): 13*9c5db199SXin Li pass 14*9c5db199SXin Li 15*9c5db199SXin Li 16*9c5db199SXin Liclass BoardLabelError(Exception): 17*9c5db199SXin Li pass 18*9c5db199SXin Li 19*9c5db199SXin Li 20*9c5db199SXin Liclass job(models.job): 21*9c5db199SXin Li def __init__(self, dir): 22*9c5db199SXin Li job_dict = job.load_from_dir(dir) 23*9c5db199SXin Li super(job, self).__init__(dir, **job_dict) 24*9c5db199SXin Li 25*9c5db199SXin Li 26*9c5db199SXin Li @classmethod 27*9c5db199SXin Li def load_from_dir(cls, dir): 28*9c5db199SXin Li keyval = cls.read_keyval(dir) 29*9c5db199SXin Li tko_utils.dprint(str(keyval)) 30*9c5db199SXin Li 31*9c5db199SXin Li user = keyval.get("user", None) 32*9c5db199SXin Li label = keyval.get("label", None) 33*9c5db199SXin Li queued_time = tko_utils.get_timestamp(keyval, "job_queued") 34*9c5db199SXin Li started_time = tko_utils.get_timestamp(keyval, "job_started") 35*9c5db199SXin Li finished_time = tko_utils.get_timestamp(keyval, "job_finished") 36*9c5db199SXin Li machine = cls.determine_hostname(keyval, dir) 37*9c5db199SXin Li machine_group = cls.determine_machine_group(machine, dir) 38*9c5db199SXin Li machine_owner = keyval.get("owner", None) 39*9c5db199SXin Li 40*9c5db199SXin Li aborted_by = keyval.get("aborted_by", None) 41*9c5db199SXin Li aborted_at = tko_utils.get_timestamp(keyval, "aborted_on") 42*9c5db199SXin Li 43*9c5db199SXin Li return {"user": user, "label": label, "machine": machine, 44*9c5db199SXin Li "queued_time": queued_time, "started_time": started_time, 45*9c5db199SXin Li "finished_time": finished_time, "machine_owner": machine_owner, 46*9c5db199SXin Li "machine_group": machine_group, "aborted_by": aborted_by, 47*9c5db199SXin Li "aborted_on": aborted_at, "keyval_dict": keyval} 48*9c5db199SXin Li 49*9c5db199SXin Li 50*9c5db199SXin Li @classmethod 51*9c5db199SXin Li def determine_hostname(cls, keyval, job_dir): 52*9c5db199SXin Li host_group_name = keyval.get("host_group_name", None) 53*9c5db199SXin Li machine = keyval.get("hostname", "") 54*9c5db199SXin Li is_multimachine = "," in machine 55*9c5db199SXin Li 56*9c5db199SXin Li # determine what hostname to use 57*9c5db199SXin Li if host_group_name: 58*9c5db199SXin Li if is_multimachine or not machine: 59*9c5db199SXin Li tko_utils.dprint("Using host_group_name %r instead of " 60*9c5db199SXin Li "machine name." % host_group_name) 61*9c5db199SXin Li machine = host_group_name 62*9c5db199SXin Li elif is_multimachine: 63*9c5db199SXin Li try: 64*9c5db199SXin Li machine = job.find_hostname(job_dir) # find a unique hostname 65*9c5db199SXin Li except NoHostnameError: 66*9c5db199SXin Li pass # just use the comma-separated name 67*9c5db199SXin Li 68*9c5db199SXin Li tko_utils.dprint("MACHINE NAME: %s" % machine) 69*9c5db199SXin Li return machine 70*9c5db199SXin Li 71*9c5db199SXin Li 72*9c5db199SXin Li @classmethod 73*9c5db199SXin Li def determine_machine_group(cls, hostname, job_dir): 74*9c5db199SXin Li machine_groups = set() 75*9c5db199SXin Li for individual_hostname in hostname.split(","): 76*9c5db199SXin Li host_keyval = models.test.parse_host_keyval(job_dir, 77*9c5db199SXin Li individual_hostname) 78*9c5db199SXin Li if not host_keyval: 79*9c5db199SXin Li tko_utils.dprint('Unable to parse host keyval for %s' 80*9c5db199SXin Li % individual_hostname) 81*9c5db199SXin Li elif 'labels' in host_keyval: 82*9c5db199SXin Li # Use `model` label as machine group. This is to avoid the 83*9c5db199SXin Li # confusion of multiple boards mapping to the same platform in 84*9c5db199SXin Li # wmatrix. With this change, wmatrix will group tests with the 85*9c5db199SXin Li # same model, rather than the same platform. 86*9c5db199SXin Li labels = host_keyval['labels'].split(',') 87*9c5db199SXin Li board_labels = [l[8:] for l in labels 88*9c5db199SXin Li if l.startswith('model%3A')] 89*9c5db199SXin Li # If the host doesn't have `model:` label, fall back to `board:` 90*9c5db199SXin Li # label. 91*9c5db199SXin Li if not board_labels: 92*9c5db199SXin Li board_labels = [l[8:] for l in labels 93*9c5db199SXin Li if l.startswith('board%3A')] 94*9c5db199SXin Li if board_labels: 95*9c5db199SXin Li # Multiple board/model labels aren't supposed to 96*9c5db199SXin Li # happen, but let's report something valid rather 97*9c5db199SXin Li # than just failing. 98*9c5db199SXin Li machine_groups.add(','.join(board_labels)) 99*9c5db199SXin Li else: 100*9c5db199SXin Li error = ('Failed to retrieve board label from host labels: ' 101*9c5db199SXin Li '%s' % host_keyval['labels']) 102*9c5db199SXin Li tko_utils.dprint(error) 103*9c5db199SXin Li raise BoardLabelError(error) 104*9c5db199SXin Li elif "platform" in host_keyval: 105*9c5db199SXin Li machine_groups.add(host_keyval["platform"]) 106*9c5db199SXin Li machine_group = ",".join(sorted(machine_groups)) 107*9c5db199SXin Li tko_utils.dprint("MACHINE GROUP: %s" % machine_group) 108*9c5db199SXin Li return machine_group 109*9c5db199SXin Li 110*9c5db199SXin Li 111*9c5db199SXin Li @staticmethod 112*9c5db199SXin Li def find_hostname(path): 113*9c5db199SXin Li hostname = os.path.join(path, "sysinfo", "hostname") 114*9c5db199SXin Li try: 115*9c5db199SXin Li with open(hostname) as rf: 116*9c5db199SXin Li machine = rf.readline().rstrip() 117*9c5db199SXin Li return machine 118*9c5db199SXin Li except Exception: 119*9c5db199SXin Li tko_utils.dprint("Could not read a hostname from " 120*9c5db199SXin Li "sysinfo/hostname") 121*9c5db199SXin Li 122*9c5db199SXin Li uname = os.path.join(path, "sysinfo", "uname_-a") 123*9c5db199SXin Li try: 124*9c5db199SXin Li machine = open(uname).readline().split()[1] 125*9c5db199SXin Li return machine 126*9c5db199SXin Li except Exception: 127*9c5db199SXin Li tko_utils.dprint("Could not read a hostname from " 128*9c5db199SXin Li "sysinfo/uname_-a") 129*9c5db199SXin Li 130*9c5db199SXin Li raise NoHostnameError("Unable to find a machine name") 131*9c5db199SXin Li 132*9c5db199SXin Li 133*9c5db199SXin Liclass kernel(models.kernel): 134*9c5db199SXin Li def __init__(self, job, verify_ident=None): 135*9c5db199SXin Li kernel_dict = kernel.load_from_dir(job.dir, verify_ident) 136*9c5db199SXin Li super(kernel, self).__init__(**kernel_dict) 137*9c5db199SXin Li 138*9c5db199SXin Li 139*9c5db199SXin Li @staticmethod 140*9c5db199SXin Li def load_from_dir(dir, verify_ident=None): 141*9c5db199SXin Li # try and load the booted kernel version 142*9c5db199SXin Li attributes = False 143*9c5db199SXin Li i = 1 144*9c5db199SXin Li build_dir = os.path.join(dir, "build") 145*9c5db199SXin Li while True: 146*9c5db199SXin Li if not os.path.exists(build_dir): 147*9c5db199SXin Li break 148*9c5db199SXin Li build_log = os.path.join(build_dir, "debug", "build_log") 149*9c5db199SXin Li attributes = kernel.load_from_build_log(build_log) 150*9c5db199SXin Li if attributes: 151*9c5db199SXin Li break 152*9c5db199SXin Li i += 1 153*9c5db199SXin Li build_dir = os.path.join(dir, "build.%d" % (i)) 154*9c5db199SXin Li 155*9c5db199SXin Li if not attributes: 156*9c5db199SXin Li if verify_ident: 157*9c5db199SXin Li base = verify_ident 158*9c5db199SXin Li else: 159*9c5db199SXin Li base = kernel.load_from_sysinfo(dir) 160*9c5db199SXin Li patches = [] 161*9c5db199SXin Li hashes = [] 162*9c5db199SXin Li else: 163*9c5db199SXin Li base, patches, hashes = attributes 164*9c5db199SXin Li tko_utils.dprint("kernel.__init__() found kernel version %s" 165*9c5db199SXin Li % base) 166*9c5db199SXin Li 167*9c5db199SXin Li # compute the kernel hash 168*9c5db199SXin Li if base == "UNKNOWN": 169*9c5db199SXin Li kernel_hash = "UNKNOWN" 170*9c5db199SXin Li else: 171*9c5db199SXin Li kernel_hash = kernel.compute_hash(base, hashes) 172*9c5db199SXin Li 173*9c5db199SXin Li return {"base": base, "patches": patches, 174*9c5db199SXin Li "kernel_hash": kernel_hash} 175*9c5db199SXin Li 176*9c5db199SXin Li 177*9c5db199SXin Li @staticmethod 178*9c5db199SXin Li def load_from_sysinfo(path): 179*9c5db199SXin Li for subdir in ("reboot1", ""): 180*9c5db199SXin Li uname_path = os.path.join(path, "sysinfo", subdir, 181*9c5db199SXin Li "uname_-a") 182*9c5db199SXin Li if not os.path.exists(uname_path): 183*9c5db199SXin Li continue 184*9c5db199SXin Li uname = open(uname_path).readline().split() 185*9c5db199SXin Li return re.sub("-autotest$", "", uname[2]) 186*9c5db199SXin Li return "UNKNOWN" 187*9c5db199SXin Li 188*9c5db199SXin Li 189*9c5db199SXin Li @staticmethod 190*9c5db199SXin Li def load_from_build_log(path): 191*9c5db199SXin Li if not os.path.exists(path): 192*9c5db199SXin Li return None 193*9c5db199SXin Li 194*9c5db199SXin Li base, patches, hashes = "UNKNOWN", [], [] 195*9c5db199SXin Li with open(path) as rf: 196*9c5db199SXin Li lines = rf.readlines() 197*9c5db199SXin Li for line in lines: 198*9c5db199SXin Li head, rest = line.split(": ", 1) 199*9c5db199SXin Li rest = rest.split() 200*9c5db199SXin Li if head == "BASE": 201*9c5db199SXin Li base = rest[0] 202*9c5db199SXin Li elif head == "PATCH": 203*9c5db199SXin Li patches.append(patch(*rest)) 204*9c5db199SXin Li hashes.append(rest[2]) 205*9c5db199SXin Li return base, patches, hashes 206*9c5db199SXin Li 207*9c5db199SXin Li 208*9c5db199SXin Liclass test(models.test): 209*9c5db199SXin Li def __init__(self, subdir, testname, status, reason, test_kernel, 210*9c5db199SXin Li machine, started_time, finished_time, iterations, 211*9c5db199SXin Li attributes, labels): 212*9c5db199SXin Li # for backwards compatibility with the original parser 213*9c5db199SXin Li # implementation, if there is no test version we need a NULL 214*9c5db199SXin Li # value to be used; also, if there is a version it should 215*9c5db199SXin Li # be terminated by a newline 216*9c5db199SXin Li if "version" in attributes: 217*9c5db199SXin Li attributes["version"] = str(attributes["version"]) 218*9c5db199SXin Li else: 219*9c5db199SXin Li attributes["version"] = None 220*9c5db199SXin Li 221*9c5db199SXin Li super(test, self).__init__(subdir, testname, status, reason, 222*9c5db199SXin Li test_kernel, machine, started_time, 223*9c5db199SXin Li finished_time, iterations, 224*9c5db199SXin Li attributes, labels) 225*9c5db199SXin Li 226*9c5db199SXin Li 227*9c5db199SXin Li @staticmethod 228*9c5db199SXin Li def load_iterations(keyval_path): 229*9c5db199SXin Li return iteration.load_from_keyval(keyval_path) 230*9c5db199SXin Li 231*9c5db199SXin Li 232*9c5db199SXin Liclass patch(models.patch): 233*9c5db199SXin Li def __init__(self, spec, reference, hash): 234*9c5db199SXin Li tko_utils.dprint("PATCH::%s %s %s" % (spec, reference, hash)) 235*9c5db199SXin Li super(patch, self).__init__(spec, reference, hash) 236*9c5db199SXin Li self.spec = spec 237*9c5db199SXin Li self.reference = reference 238*9c5db199SXin Li self.hash = hash 239*9c5db199SXin Li 240*9c5db199SXin Li 241*9c5db199SXin Liclass iteration(models.iteration): 242*9c5db199SXin Li @staticmethod 243*9c5db199SXin Li def parse_line_into_dicts(line, attr_dict, perf_dict): 244*9c5db199SXin Li key, value = line.split("=", 1) 245*9c5db199SXin Li perf_dict[key] = value 246*9c5db199SXin Li 247*9c5db199SXin Li 248*9c5db199SXin Liclass status_line(object): 249*9c5db199SXin Li def __init__(self, indent, status, subdir, testname, reason, 250*9c5db199SXin Li optional_fields): 251*9c5db199SXin Li # pull out the type & status of the line 252*9c5db199SXin Li if status == "START": 253*9c5db199SXin Li self.type = "START" 254*9c5db199SXin Li self.status = None 255*9c5db199SXin Li elif status.startswith("END "): 256*9c5db199SXin Li self.type = "END" 257*9c5db199SXin Li self.status = status[4:] 258*9c5db199SXin Li else: 259*9c5db199SXin Li self.type = "STATUS" 260*9c5db199SXin Li self.status = status 261*9c5db199SXin Li assert (self.status is None or 262*9c5db199SXin Li self.status in status_lib.statuses) 263*9c5db199SXin Li 264*9c5db199SXin Li # save all the other parameters 265*9c5db199SXin Li self.indent = indent 266*9c5db199SXin Li self.subdir = self.parse_name(subdir) 267*9c5db199SXin Li self.testname = self.parse_name(testname) 268*9c5db199SXin Li self.reason = reason 269*9c5db199SXin Li self.optional_fields = optional_fields 270*9c5db199SXin Li 271*9c5db199SXin Li 272*9c5db199SXin Li @staticmethod 273*9c5db199SXin Li def parse_name(name): 274*9c5db199SXin Li if name == "----": 275*9c5db199SXin Li return None 276*9c5db199SXin Li return name 277*9c5db199SXin Li 278*9c5db199SXin Li 279*9c5db199SXin Li @staticmethod 280*9c5db199SXin Li def is_status_line(line): 281*9c5db199SXin Li return re.search(r"^\t*(\S[^\t]*\t){3}", line) is not None 282*9c5db199SXin Li 283*9c5db199SXin Li 284*9c5db199SXin Li @classmethod 285*9c5db199SXin Li def parse_line(cls, line): 286*9c5db199SXin Li if not status_line.is_status_line(line): 287*9c5db199SXin Li return None 288*9c5db199SXin Li match = re.search(r"^(\t*)(.*)$", line, flags=re.DOTALL) 289*9c5db199SXin Li if not match: 290*9c5db199SXin Li # A more useful error message than: 291*9c5db199SXin Li # AttributeError: 'NoneType' object has no attribute 'groups' 292*9c5db199SXin Li # to help us debug what happens on occasion here. 293*9c5db199SXin Li raise RuntimeError("line %r could not be parsed." % line) 294*9c5db199SXin Li indent, line = match.groups() 295*9c5db199SXin Li indent = len(indent) 296*9c5db199SXin Li 297*9c5db199SXin Li # split the line into the fixed and optional fields 298*9c5db199SXin Li parts = line.rstrip("\n").split("\t") 299*9c5db199SXin Li 300*9c5db199SXin Li part_index = 3 301*9c5db199SXin Li status, subdir, testname = parts[0:part_index] 302*9c5db199SXin Li 303*9c5db199SXin Li # all optional parts should be of the form "key=value". once we've found 304*9c5db199SXin Li # a non-matching part, treat it and the rest of the parts as the reason. 305*9c5db199SXin Li optional_fields = {} 306*9c5db199SXin Li while part_index < len(parts): 307*9c5db199SXin Li kv = re.search(r"^(\w+)=(.+)", parts[part_index]) 308*9c5db199SXin Li if not kv: 309*9c5db199SXin Li break 310*9c5db199SXin Li 311*9c5db199SXin Li optional_fields[kv.group(1)] = kv.group(2) 312*9c5db199SXin Li part_index += 1 313*9c5db199SXin Li 314*9c5db199SXin Li reason = "\t".join(parts[part_index:]) 315*9c5db199SXin Li 316*9c5db199SXin Li # build up a new status_line and return it 317*9c5db199SXin Li return cls(indent, status, subdir, testname, reason, 318*9c5db199SXin Li optional_fields) 319*9c5db199SXin Li 320*9c5db199SXin Li 321*9c5db199SXin Liclass parser(base.parser): 322*9c5db199SXin Li @staticmethod 323*9c5db199SXin Li def make_job(dir): 324*9c5db199SXin Li return job(dir) 325*9c5db199SXin Li 326*9c5db199SXin Li 327*9c5db199SXin Li def state_iterator(self, buffer): 328*9c5db199SXin Li new_tests = [] 329*9c5db199SXin Li boot_count = 0 330*9c5db199SXin Li group_subdir = None 331*9c5db199SXin Li sought_level = 0 332*9c5db199SXin Li stack = status_lib.status_stack() 333*9c5db199SXin Li current_kernel = kernel(self.job) 334*9c5db199SXin Li boot_in_progress = False 335*9c5db199SXin Li alert_pending = None 336*9c5db199SXin Li started_time = None 337*9c5db199SXin Li 338*9c5db199SXin Li while not self.finished or buffer.size(): 339*9c5db199SXin Li # stop processing once the buffer is empty 340*9c5db199SXin Li if buffer.size() == 0: 341*9c5db199SXin Li yield new_tests 342*9c5db199SXin Li new_tests = [] 343*9c5db199SXin Li continue 344*9c5db199SXin Li 345*9c5db199SXin Li # parse the next line 346*9c5db199SXin Li line = buffer.get() 347*9c5db199SXin Li tko_utils.dprint('\nSTATUS: ' + line.strip()) 348*9c5db199SXin Li line = status_line.parse_line(line) 349*9c5db199SXin Li if line is None: 350*9c5db199SXin Li tko_utils.dprint('non-status line, ignoring') 351*9c5db199SXin Li continue # ignore non-status lines 352*9c5db199SXin Li 353*9c5db199SXin Li # have we hit the job start line? 354*9c5db199SXin Li if (line.type == "START" and not line.subdir and 355*9c5db199SXin Li not line.testname): 356*9c5db199SXin Li sought_level = 1 357*9c5db199SXin Li tko_utils.dprint("found job level start " 358*9c5db199SXin Li "marker, looking for level " 359*9c5db199SXin Li "1 groups now") 360*9c5db199SXin Li continue 361*9c5db199SXin Li 362*9c5db199SXin Li # have we hit the job end line? 363*9c5db199SXin Li if (line.type == "END" and not line.subdir and 364*9c5db199SXin Li not line.testname): 365*9c5db199SXin Li tko_utils.dprint("found job level end " 366*9c5db199SXin Li "marker, looking for level " 367*9c5db199SXin Li "0 lines now") 368*9c5db199SXin Li sought_level = 0 369*9c5db199SXin Li 370*9c5db199SXin Li # START line, just push another layer on to the stack 371*9c5db199SXin Li # and grab the start time if this is at the job level 372*9c5db199SXin Li # we're currently seeking 373*9c5db199SXin Li if line.type == "START": 374*9c5db199SXin Li group_subdir = None 375*9c5db199SXin Li stack.start() 376*9c5db199SXin Li if line.indent == sought_level: 377*9c5db199SXin Li started_time = \ 378*9c5db199SXin Li tko_utils.get_timestamp( 379*9c5db199SXin Li line.optional_fields, "timestamp") 380*9c5db199SXin Li tko_utils.dprint("start line, ignoring") 381*9c5db199SXin Li continue 382*9c5db199SXin Li # otherwise, update the status on the stack 383*9c5db199SXin Li else: 384*9c5db199SXin Li tko_utils.dprint("GROPE_STATUS: %s" % 385*9c5db199SXin Li [stack.current_status(), 386*9c5db199SXin Li line.status, line.subdir, 387*9c5db199SXin Li line.testname, line.reason]) 388*9c5db199SXin Li stack.update(line.status) 389*9c5db199SXin Li 390*9c5db199SXin Li if line.status == "ALERT": 391*9c5db199SXin Li tko_utils.dprint("job level alert, recording") 392*9c5db199SXin Li alert_pending = line.reason 393*9c5db199SXin Li continue 394*9c5db199SXin Li 395*9c5db199SXin Li # ignore Autotest.install => GOOD lines 396*9c5db199SXin Li if (line.testname == "Autotest.install" and 397*9c5db199SXin Li line.status == "GOOD"): 398*9c5db199SXin Li tko_utils.dprint("Successful Autotest " 399*9c5db199SXin Li "install, ignoring") 400*9c5db199SXin Li continue 401*9c5db199SXin Li 402*9c5db199SXin Li # ignore END lines for a reboot group 403*9c5db199SXin Li if (line.testname == "reboot" and line.type == "END"): 404*9c5db199SXin Li tko_utils.dprint("reboot group, ignoring") 405*9c5db199SXin Li continue 406*9c5db199SXin Li 407*9c5db199SXin Li # convert job-level ABORTs into a 'CLIENT_JOB' test, and 408*9c5db199SXin Li # ignore other job-level events 409*9c5db199SXin Li if line.testname is None: 410*9c5db199SXin Li if (line.status == "ABORT" and 411*9c5db199SXin Li line.type != "END"): 412*9c5db199SXin Li line.testname = "CLIENT_JOB" 413*9c5db199SXin Li else: 414*9c5db199SXin Li tko_utils.dprint("job level event, " 415*9c5db199SXin Li "ignoring") 416*9c5db199SXin Li continue 417*9c5db199SXin Li 418*9c5db199SXin Li # use the group subdir for END lines 419*9c5db199SXin Li if line.type == "END": 420*9c5db199SXin Li line.subdir = group_subdir 421*9c5db199SXin Li 422*9c5db199SXin Li # are we inside a block group? 423*9c5db199SXin Li if (line.indent != sought_level and 424*9c5db199SXin Li line.status != "ABORT" and 425*9c5db199SXin Li not line.testname.startswith('reboot.')): 426*9c5db199SXin Li if line.subdir: 427*9c5db199SXin Li tko_utils.dprint("set group_subdir: " 428*9c5db199SXin Li + line.subdir) 429*9c5db199SXin Li group_subdir = line.subdir 430*9c5db199SXin Li tko_utils.dprint("ignoring incorrect indent " 431*9c5db199SXin Li "level %d != %d," % 432*9c5db199SXin Li (line.indent, sought_level)) 433*9c5db199SXin Li continue 434*9c5db199SXin Li 435*9c5db199SXin Li # use the subdir as the testname, except for 436*9c5db199SXin Li # boot.* and kernel.* tests 437*9c5db199SXin Li if (line.testname is None or 438*9c5db199SXin Li not re.search(r"^(boot(\.\d+)?$|kernel\.)", 439*9c5db199SXin Li line.testname)): 440*9c5db199SXin Li if line.subdir and '.' in line.subdir: 441*9c5db199SXin Li line.testname = line.subdir 442*9c5db199SXin Li 443*9c5db199SXin Li # has a reboot started? 444*9c5db199SXin Li if line.testname == "reboot.start": 445*9c5db199SXin Li started_time = tko_utils.get_timestamp( 446*9c5db199SXin Li line.optional_fields, "timestamp") 447*9c5db199SXin Li tko_utils.dprint("reboot start event, " 448*9c5db199SXin Li "ignoring") 449*9c5db199SXin Li boot_in_progress = True 450*9c5db199SXin Li continue 451*9c5db199SXin Li 452*9c5db199SXin Li # has a reboot finished? 453*9c5db199SXin Li if line.testname == "reboot.verify": 454*9c5db199SXin Li line.testname = "boot.%d" % boot_count 455*9c5db199SXin Li tko_utils.dprint("reboot verified") 456*9c5db199SXin Li boot_in_progress = False 457*9c5db199SXin Li verify_ident = line.reason.strip() 458*9c5db199SXin Li current_kernel = kernel(self.job, verify_ident) 459*9c5db199SXin Li boot_count += 1 460*9c5db199SXin Li 461*9c5db199SXin Li if alert_pending: 462*9c5db199SXin Li line.status = "ALERT" 463*9c5db199SXin Li line.reason = alert_pending 464*9c5db199SXin Li alert_pending = None 465*9c5db199SXin Li 466*9c5db199SXin Li # create the actual test object 467*9c5db199SXin Li finished_time = tko_utils.get_timestamp( 468*9c5db199SXin Li line.optional_fields, "timestamp") 469*9c5db199SXin Li final_status = stack.end() 470*9c5db199SXin Li tko_utils.dprint("Adding: " 471*9c5db199SXin Li "%s\nSubdir:%s\nTestname:%s\n%s" % 472*9c5db199SXin Li (final_status, line.subdir, 473*9c5db199SXin Li line.testname, line.reason)) 474*9c5db199SXin Li new_test = test.parse_test(self.job, line.subdir, 475*9c5db199SXin Li line.testname, 476*9c5db199SXin Li final_status, line.reason, 477*9c5db199SXin Li current_kernel, 478*9c5db199SXin Li started_time, 479*9c5db199SXin Li finished_time) 480*9c5db199SXin Li started_time = None 481*9c5db199SXin Li new_tests.append(new_test) 482*9c5db199SXin Li 483*9c5db199SXin Li # the job is finished, but we never came back from reboot 484*9c5db199SXin Li if boot_in_progress: 485*9c5db199SXin Li testname = "boot.%d" % boot_count 486*9c5db199SXin Li reason = "machine did not return from reboot" 487*9c5db199SXin Li tko_utils.dprint(("Adding: ABORT\nSubdir:----\n" 488*9c5db199SXin Li "Testname:%s\n%s") 489*9c5db199SXin Li % (testname, reason)) 490*9c5db199SXin Li new_test = test.parse_test(self.job, None, testname, 491*9c5db199SXin Li "ABORT", reason, 492*9c5db199SXin Li current_kernel, None, None) 493*9c5db199SXin Li new_tests.append(new_test) 494*9c5db199SXin Li yield new_tests 495