blob: 1b96bab66cc566afc0079046ef60a70dc92ff4ee [file] [log] [blame]
mbligh96cf0512008-04-17 15:25:38 +00001#!/usr/bin/python -u
mblighc2514542008-02-19 15:54:26 +00002
mbligh96cf0512008-04-17 15:25:38 +00003import os, sys, optparse, fcntl, errno, traceback
mblighbb7b8912006-10-08 03:59:02 +00004
mbligh96cf0512008-04-17 15:25:38 +00005import common
jadmanski6e8bf752008-05-14 00:17:48 +00006from autotest_lib.client.common_lib import mail, utils
7from autotest_lib.tko import db as tko_db, utils as tko_utils, status_lib
mbligh74fc0462007-11-05 20:24:17 +00008
9
mbligh96cf0512008-04-17 15:25:38 +000010def parse_args():
11 # build up our options parser and parse sys.argv
12 parser = optparse.OptionParser()
13 parser.add_option("-m", help="Send mail for FAILED tests",
14 dest="mailit", action="store_true")
15 parser.add_option("-r", help="Reparse the results of a job",
16 dest="reparse", action="store_true")
17 parser.add_option("-o", help="Parse a single results directory",
18 dest="singledir", action="store_true")
19 parser.add_option("-l", help=("Levels of subdirectories to include "
20 "in the job name"),
21 type="int", dest="level", default=1)
22 parser.add_option("-n", help="No blocking on an existing parse",
23 dest="noblock", action="store_true")
24 parser.add_option("-s", help="Database server hostname",
25 dest="db_host", action="store")
26 parser.add_option("-u", help="Database username", dest="db_user",
27 action="store")
28 parser.add_option("-p", help="Database password", dest="db_pass",
29 action="store")
30 parser.add_option("-d", help="Database name", dest="db_name",
31 action="store")
32 options, args = parser.parse_args()
mbligh74fc0462007-11-05 20:24:17 +000033
mbligh96cf0512008-04-17 15:25:38 +000034 # we need a results directory
35 if len(args) == 0:
jadmanski6e8bf752008-05-14 00:17:48 +000036 tko_utils.dprint("ERROR: at least one results directory must "
37 "be provided")
mbligh96cf0512008-04-17 15:25:38 +000038 parser.print_help()
39 sys.exit(1)
mbligh74fc0462007-11-05 20:24:17 +000040
mbligh96cf0512008-04-17 15:25:38 +000041 # pass the options back
42 return options, args
mbligh74fc0462007-11-05 20:24:17 +000043
44
mbligh96cf0512008-04-17 15:25:38 +000045def format_failure_message(jobname, kernel, testname, status, reason):
46 format_string = "%-12s %-20s %-12s %-10s %s"
47 return format_string % (jobname, kernel, testname, status, reason)
mblighb85e6b02006-10-08 17:20:56 +000048
mblighbb7b8912006-10-08 03:59:02 +000049
mbligh96cf0512008-04-17 15:25:38 +000050def mailfailure(jobname, job, message):
51 message_lines = [""]
52 message_lines.append("The following tests FAILED for this job")
53 message_lines.append("http://%s/results/%s" %
54 (socket.gethostname(), jobname))
55 message_lines.append("")
56 message_lines.append(format_failure_message("Job name", "Kernel",
57 "Test name", "FAIL/WARN",
58 "Failure reason"))
59 message_lines.append(format_failure_message("=" * 8, "=" * 6, "=" * 8,
60 "=" * 8, "=" * 14))
61 message_header = "\n".join(message_lines)
62
63 subject = "AUTOTEST: FAILED tests from job %s" % jobname
jadmanski6e8bf752008-05-14 00:17:48 +000064 mail.send("", job.user, "", subject, message_header + message)
mbligh006f2302007-09-13 20:46:46 +000065
66
mbligh96cf0512008-04-17 15:25:38 +000067def parse_one(db, jobname, path, reparse, mail_on_failure):
68 """
69 Parse a single job. Optionally send email on failure.
70 """
jadmanski6e8bf752008-05-14 00:17:48 +000071 tko_utils.dprint("\nScanning %s (%s)" % (jobname, path))
mbligh96cf0512008-04-17 15:25:38 +000072 if reparse and db.find_job(jobname):
jadmanski6e8bf752008-05-14 00:17:48 +000073 tko_utils.dprint("! Deleting old copy of job results to "
mbligh96cf0512008-04-17 15:25:38 +000074 "reparse it")
75 db.delete_job(jobname)
76 if db.find_job(jobname):
jadmanski6e8bf752008-05-14 00:17:48 +000077 tko_utils.dprint("! Job is already parsed, done")
mbligh96cf0512008-04-17 15:25:38 +000078 return
79
jadmanski6e8bf752008-05-14 00:17:48 +000080 # look up the status version
81 try:
82 job_keyval = utils.read_keyval(path)
83 except IOError, e:
84 if e.errno == errno.ENOENT:
85 status_version = 0
86 else:
87 raise
88 else:
89 status_version = job_keyval.get("status_version", 0)
90
mbligh96cf0512008-04-17 15:25:38 +000091 # parse out the job
jadmanski6e8bf752008-05-14 00:17:48 +000092 parser = status_lib.parser(status_version)
mbligh96cf0512008-04-17 15:25:38 +000093 job = parser.make_job(path)
94 status_log = os.path.join(path, "status.log")
95 if not os.path.exists(status_log):
96 status_log = os.path.join(path, "status")
97 if not os.path.exists(status_log):
jadmanski6e8bf752008-05-14 00:17:48 +000098 tko_utils.dprint("! Unable to parse job, no status file")
mbligh96cf0512008-04-17 15:25:38 +000099 return
100
101 # parse the status logs
jadmanski6e8bf752008-05-14 00:17:48 +0000102 tko_utils.dprint("+ Parsing dir=%s, jobname=%s" % (path, jobname))
mbligh96cf0512008-04-17 15:25:38 +0000103 status_lines = open(status_log).readlines()
104 parser.start(job)
105 tests = parser.end(status_lines)
106 job.tests = tests
107
108 # check for failures
109 message_lines = [""]
110 for test in job.tests:
111 if not test.subdir:
112 continue
jadmanski6e8bf752008-05-14 00:17:48 +0000113 tko_utils.dprint("* testname, status, reason: %s %s %s"
114 % (test.subdir, test.status, test.reason))
mbligh96cf0512008-04-17 15:25:38 +0000115 if test.status in ("FAIL", "WARN"):
116 message_lines.append(format_failure_message(
117 jobname, test.kernel.base, test.subdir,
118 test.status, test.reason))
119 message = "\n".join(message_lines)
120
121 # send out a email report of failure
122 if len(message) > 2 and mail_on_failure:
jadmanski6e8bf752008-05-14 00:17:48 +0000123 tko_utils.dprint("Sending email report of failure on %s to %s"
124 % (jobname, job.user))
mbligh96cf0512008-04-17 15:25:38 +0000125 mailfailure(jobname, job, message)
126
127 # write the job into the database
128 db.insert_job(jobname, job)
129 db.commit()
mbligh26b992b2008-02-19 15:46:21 +0000130
131
mbligh96cf0512008-04-17 15:25:38 +0000132def parse_path(db, path, level, reparse, mail_on_failure):
133 machine_list = os.path.join(path, ".machines")
134 if os.path.exists(machine_list):
135 # multi-machine job
136 for m in file(machine_list):
137 machine = m.rstrip()
138 if not machine:
139 continue
140 jobpath = os.path.join(path, machine)
141 jobname = "%s/%s" % (os.path.basename(path), machine)
142 try:
143 parse_one(db, jobname, jobpath, reparse,
144 mail_on_failure)
145 except Exception:
146 traceback.print_exc()
147 continue
148 else:
149 # single machine job
150 job_elements = path.split("/")[-level:]
151 jobname = "/".join(job_elements)
mbligh8e1ab172007-09-13 17:29:56 +0000152 try:
mbligh96cf0512008-04-17 15:25:38 +0000153 parse_one(db, jobname, path, reparse, mail_on_failure)
154 except Exception:
155 traceback.print_exc()
mblighbb7b8912006-10-08 03:59:02 +0000156
157
mbligh96cf0512008-04-17 15:25:38 +0000158def main():
159 options, args = parse_args()
160 results_dir = os.path.abspath(args[0])
161 assert os.path.exists(results_dir)
162
163 # build up the list of job dirs to parse
164 if options.singledir:
165 jobs_list = [results_dir]
166 else:
167 jobs_list = [os.path.join(results_dir, subdir)
168 for subdir in os.listdir(results_dir)]
169
170 # build up the database
171 db = tko_db.db(autocommit=False, host=options.db_host,
172 user=options.db_user, password=options.db_pass,
173 database=options.db_name)
174
175 # parse all the jobs
176 for path in jobs_list:
177 lockfile = open(os.path.join(path, ".parse.lock"), "w")
178 flags = fcntl.LOCK_EX
179 if options.noblock:
180 flags != fcntl.LOCK_NB
mbligh532cb272007-11-26 18:54:20 +0000181 try:
mbligh96cf0512008-04-17 15:25:38 +0000182 fcntl.flock(lockfile, flags)
183 except IOError, e:
184 # was this because the lock is unavailable?
185 if e.errno == errno.EWOULDBLOCK:
186 lockfile.close()
187 continue
188 else:
189 raise # something unexpected happened
mbligh532cb272007-11-26 18:54:20 +0000190 try:
mbligh96cf0512008-04-17 15:25:38 +0000191 parse_path(db, path, options.level, options.reparse,
192 options.mailit)
193 finally:
194 fcntl.flock(lockfile, fcntl.LOCK_UN)
195 lockfile.close()
mbligh71d340d2008-03-05 15:51:16 +0000196
mbligh532cb272007-11-26 18:54:20 +0000197
mbligh96cf0512008-04-17 15:25:38 +0000198if __name__ == "__main__":
199 main()