blob: a8741bf679d480102a0fc2d2226e7ff8e581228f [file] [log] [blame]
mbligh4263b062008-02-21 19:14:43 +00001#!/usr/bin/python -u
mbligh05bb6332008-03-13 15:34:04 +00002import os, re, parse, frontend, db, sys, socket, fcntl
mblighb89c7b32008-01-25 17:37:23 +00003from optparse import OptionParser
mblighbc985702007-11-05 20:52:15 +00004from traceback import format_exception
mbligh74fc0462007-11-05 20:24:17 +00005
mblighbc985702007-11-05 20:52:15 +00006def format_error():
7 t, o, tb = sys.exc_info()
8 trace = format_exception(t, o, tb)
9 # Clear the backtrace to prevent a circular reference
10 # in the heap -- as per tutorial
11 tb = ''
12
13 return ''.join(trace)
14
mblighb89c7b32008-01-25 17:37:23 +000015parser = OptionParser()
16parser.add_option('-m', help='Send mail for FAILED tests', dest='mailit',
17 action='store_true')
18parser.add_option('-r', help='Reparse the results of a job', dest='reparse',
19 action='store_true')
20parser.add_option('-o', help='one: parse a single results directory',
21 dest='singledir', action='store_true')
22parser.add_option('-l', help='levels of subdirectories to include in job name',
23 type='int', dest='level', default=1)
mblighb03ba642008-03-13 17:37:17 +000024parser.add_option('-n', help='no blocking on an existing parse',
25 dest='noblock', action='store_true')
mbligh5a0c9702008-03-11 22:40:55 +000026parser.add_option('-s', help='Database server hostname',
27 dest='db_host', action='store')
28parser.add_option('-u', help='Database username',
29 dest='db_user', action='store')
30parser.add_option('-p', help='Database password',
31 dest='db_pass', action='store')
32parser.add_option('-d', help='Database name',
33 dest='db_name', action='store')
mblighb89c7b32008-01-25 17:37:23 +000034(options, args) = parser.parse_args()
mblighbc985702007-11-05 20:52:15 +000035
mbligh5a0c9702008-03-11 22:40:55 +000036if len(args) == 0:
37 print "ERROR: You need to at least provide a directory to parse\n"
38 parser.print_help()
39 sys.exit(1)
40
mblighb89c7b32008-01-25 17:37:23 +000041dir = os.path.abspath(args[0])
42assert os.path.exists(dir)
mbligh74fc0462007-11-05 20:24:17 +000043
mblighb89c7b32008-01-25 17:37:23 +000044if options.singledir:
mbligh2c05bce2007-11-26 20:15:58 +000045 jobs_list = [dir]
mbligh74fc0462007-11-05 20:24:17 +000046else:
mblighb89c7b32008-01-25 17:37:23 +000047 jobs_list = [os.path.join(dir, subdir) for subdir in os.listdir(dir)]
mblighbb7b8912006-10-08 03:59:02 +000048
mbligh9ec94852007-10-25 15:24:16 +000049debug = True
50
mbligh74fc0462007-11-05 20:24:17 +000051failcc = ""
mbligh9e7c6d02007-11-26 20:59:45 +000052# The user to notify on job failures - TOOD, pull into the config file
mbligh74fc0462007-11-05 20:24:17 +000053notify_user = None
mbligh5a0c9702008-03-11 22:40:55 +000054# do commits transactionally
55db = db.db(autocommit=False, host=options.db_host,
56 user=options.db_user, password=options.db_pass,
57 database=options.db_name)
mbligh056d0d32006-10-08 22:31:10 +000058
mbligh9ec94852007-10-25 15:24:16 +000059
mbligh74fc0462007-11-05 20:24:17 +000060def mailfailure(jobname, job, mesgtxt):
61 # XXX: Need to insert URL here too (frontend.test.url?)
62 link = "http://" + socket.gethostname() + "/results/" + jobname
63
64 # This looks pretty good on fixed-width-font email reader.
mbligh032c2de2007-11-24 19:20:12 +000065 message_header = "\n%s\n%s\n\n%-12s %-20s %-12s %-10s %s\n" % \
66 ("The following tests FAILED for this job:",
67 link, "Job name", "Kernel", "Test name",
68 "FAIL/WARN", "Failure Reason")
69 message_header += "%-12s %-20s %-12s %-10s %s\n" % \
70 ("========", "======", "=========",
71 "=========", "==============")
mbligh74fc0462007-11-05 20:24:17 +000072
73 subject = "AUTOTEST: FAILED tests from " + " job " + jobname
mbligh9e7c6d02007-11-26 20:59:45 +000074 parse.mail(notify_user, job.user, failcc, subject,
mbligh032c2de2007-11-24 19:20:12 +000075 message_header + mesgtxt)
mbligh74fc0462007-11-05 20:24:17 +000076
77
mblighe8b37a92007-12-19 15:54:11 +000078def dprint(string):
79 if debug:
80 print string
81
82
mbligh9ec94852007-10-25 15:24:16 +000083def do_parse(jobname, path):
mbligh0a498cb2007-11-26 17:34:28 +000084 """
85 Parse a single job. Optionally send email on failure, etc.
86 """
mblighe8b37a92007-12-19 15:54:11 +000087 dprint('\nScanning %s (%s)' % (jobname, path))
mblighb89c7b32008-01-25 17:37:23 +000088 if options.reparse and db.find_job(jobname):
mblighe8b37a92007-12-19 15:54:11 +000089 dprint('! Deleting old copy of job results, to reparse it')
90 db.delete_job(jobname)
91 if db.find_job(jobname): # Job has already been parsed
92 dprint('! Already processed')
mbligh9ec94852007-10-25 15:24:16 +000093 return
mbligh532cb272007-11-26 18:54:20 +000094 job = parse.job(path)
mbligh8e1ab172007-09-13 17:29:56 +000095 if not job:
mblighe8b37a92007-12-19 15:54:11 +000096 dprint('! Failed to parse job (no status file?)')
mbligh9ec94852007-10-25 15:24:16 +000097 return
mblighd5c33db2006-10-08 21:34:16 +000098 if not job.kernel:
mblighe8b37a92007-12-19 15:54:11 +000099 dprint('! Failed to find kernel for job')
mbligh9ec94852007-10-25 15:24:16 +0000100 return
mblighf54ed2a2007-11-24 19:31:30 +0000101 print '+ Parsing ' + path
mbligh74fc0462007-11-05 20:24:17 +0000102 print '* jobname, kernel version: %s %s' % (jobname, job.kernel.base)
103 mesgtxt = "\n"
mblighe9cf9d42007-08-31 08:56:00 +0000104 for test in job.tests:
mbligh74fc0462007-11-05 20:24:17 +0000105 if not test.subdir:
106 continue
mbligh4a2c61e2007-11-12 22:13:11 +0000107 print "* testname, status, reason: %s %s %s" % \
108 (test.subdir, test.status, test.reason)
mbligh74fc0462007-11-05 20:24:17 +0000109 if re.match(r'(FAIL|WARN)',test.status):
mbligh4a2c61e2007-11-12 22:13:11 +0000110 mesgtxt += "%-12s %-20s %-12s %-10s %s" % \
111 (jobname, job.kernel.base, test.subdir,
112 test.status, test.reason)
mbligh74fc0462007-11-05 20:24:17 +0000113
mblighb89c7b32008-01-25 17:37:23 +0000114 if len(mesgtxt) > 2 and options.mailit:
mbligh4a2c61e2007-11-12 22:13:11 +0000115 print "Sending email report of FAILURES on %s to %s" % \
116 (jobname, job.user)
mbligh74fc0462007-11-05 20:24:17 +0000117 mailfailure(jobname, job, mesgtxt)
mbligh9ec94852007-10-25 15:24:16 +0000118 db.insert_job(jobname, job)
mblighbc985702007-11-05 20:52:15 +0000119 print "COMMITING"
mbligh432bad42007-10-09 19:56:07 +0000120 db.commit()
mblighfd6682452007-09-30 22:02:02 +0000121
mbligh9ec94852007-10-25 15:24:16 +0000122
mbligh05bb6332008-03-13 15:34:04 +0000123def parse_path(path):
mblighb89c7b32008-01-25 17:37:23 +0000124 job_elements = path.split('/')[-options.level:]
125 # last 'level' elements of path
mbligh2c05bce2007-11-26 20:15:58 +0000126 jobname = '/'.join(job_elements)
mbligh9ec94852007-10-25 15:24:16 +0000127 machine_list = os.path.join(path, '.machines')
128 if os.path.exists(machine_list):
mbligh0a498cb2007-11-26 17:34:28 +0000129 # This is a multi-machine job
mbligh05bb6332008-03-13 15:34:04 +0000130 for m in open(machine_list):
mbligh9ec94852007-10-25 15:24:16 +0000131 machine = m.rstrip()
132 if not machine:
133 continue
134 jobpath = os.path.join(path, machine)
135 jobname = os.path.join(os.path.basename(path), machine)
mblighbc985702007-11-05 20:52:15 +0000136 try:
137 do_parse(jobname, jobpath)
138 except:
139 print format_error()
140 continue
mbligh9ec94852007-10-25 15:24:16 +0000141 else:
mbligh0a498cb2007-11-26 17:34:28 +0000142 # This is a single-machine job
mblighbc985702007-11-05 20:52:15 +0000143 try:
144 do_parse(jobname, path)
145 except:
146 print format_error()
mbligh9ec94852007-10-25 15:24:16 +0000147
148
mbligh05bb6332008-03-13 15:34:04 +0000149for path in jobs_list:
150 lockfile = open(os.path.join(path, ".parse.lock"), "w")
mblighb03ba642008-03-13 17:37:17 +0000151 try:
152 flags = fcntl.LOCK_EX
153 if options.noblock:
154 flags |= fcntl.LOCK_NB
155 fcntl.flock(lockfile, flags)
156 except IOError:
157 # only happens when non-blocking if the lock is already held
158 lockfile.close()
159 continue # a parse is already running here, skip it
mbligh05bb6332008-03-13 15:34:04 +0000160 try:
161 parse_path(path)
162 finally:
163 fcntl.flock(lockfile, fcntl.LOCK_UN)
164 lockfile.close()