blob: 4e94d52ac61162ecc0862371706e85d177ff055c [file] [log] [blame]
mbligh2e4e5df2007-11-05 17:22:46 +00001#!/usr/bin/python
2
3"""
4Selects all rows and columns that satisfy the condition specified
5and draws the matrix. There is a seperate SQL query made for every (x,y)
6in the matrix.
7"""
8
mbligh2e4e5df2007-11-05 17:22:46 +00009print "Content-type: text/html\n"
jadmanski36505a92008-05-01 22:07:02 +000010import cgi, cgitb, re, datetime, query_lib, time
mbligha4266932007-11-05 18:12:16 +000011import sys, os
mblighb180f6c2008-01-04 20:24:41 +000012import urllib
mbligh2e4e5df2007-11-05 17:22:46 +000013
jadmanski36505a92008-05-01 22:07:02 +000014total_wall_time_start = time.time();
mbligh2e4e5df2007-11-05 17:22:46 +000015tko = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
16sys.path.insert(0, tko)
17
mbligh2b672532007-11-05 19:24:51 +000018import display, frontend, db, query_lib
mbligh12eebfa2008-01-03 02:01:53 +000019client_bin = os.path.abspath(os.path.join(tko, '../client/bin'))
20sys.path.insert(0, client_bin)
21import kernel_versions
22
mbligh190a81d2007-11-05 20:40:38 +000023html_header = """\
24<form action="compose_query.cgi" method="get">
25<table border="0">
26<tr>
27 <td>Column: </td>
28 <td>Row: </td>
29 <td>Condition: </td>
mbligh44710b62008-03-07 00:25:43 +000030 <td align="center">
31 <a href="http://test.kernel.org/autotest/AutotestTKOCondition">Help</a>
32 </td>
mbligh190a81d2007-11-05 20:40:38 +000033</tr>
34<tr>
35 <td>
36 <SELECT NAME="columns">
37 %s
38 </SELECT>
39 </td>
40 <td>
41 <SELECT NAME="rows">
42 %s
43 </SELECT>
44 </td>
45 <td>
mbligh44710b62008-03-07 00:25:43 +000046 <input type="text" name="condition" size="30" value="%s">
mblighbe257452008-04-16 23:29:13 +000047 <input type="hidden" name="title" value="%s">
mbligh190a81d2007-11-05 20:40:38 +000048 </td>
49 <td align="center"><input type="submit" value="Submit">
50 </td>
51</tr>
52</table>
53</form>
54"""
55
mbligh12eebfa2008-01-03 02:01:53 +000056
mbligh5dd503b2008-01-03 16:35:27 +000057next_field = {
mbligh44710b62008-03-07 00:25:43 +000058 'machine_group': 'hostname',
59 'hostname': 'tag',
60 'tag': 'tag',
mbligh5dd503b2008-01-03 16:35:27 +000061
mbligh44710b62008-03-07 00:25:43 +000062 'kernel': 'test',
63 'test': 'label',
64 'label': 'tag',
mbligh5dd503b2008-01-03 16:35:27 +000065
mbligh44710b62008-03-07 00:25:43 +000066 'reason': 'tag',
67 'user': 'tag',
68 'status': 'tag',
69
mbligh5bb55862008-04-16 23:09:31 +000070 'time': 'tag',
71 'time_daily': 'time',
mbligh5dd503b2008-01-03 16:35:27 +000072}
73
74
mbligh11204f82008-04-16 23:30:07 +000075def parse_field(form, form_field, field_default):
76 if not form_field in form:
77 return field_default
mbligh12eebfa2008-01-03 02:01:53 +000078 field_input = form[form_field].value.lower()
mbligh2ba3e732008-01-16 01:30:19 +000079 if field_input and field_input in frontend.test_view_field_dict:
mbligh12eebfa2008-01-03 02:01:53 +000080 return field_input
mbligh11204f82008-04-16 23:30:07 +000081 return field_default
mbligh12eebfa2008-01-03 02:01:53 +000082
83
84def parse_condition(form, form_field, field_default):
85 if not form_field in form:
86 return field_default
87 return form[form_field].value
88
89
90form = cgi.FieldStorage()
mbligh3d7a5f52008-04-16 23:06:36 +000091
mblighbe257452008-04-16 23:29:13 +000092title_field = parse_condition(form, 'title', '')
mbligh9add5882008-04-16 23:32:01 +000093row = parse_field(form, 'rows', 'kernel')
94column = parse_field(form, 'columns', 'machine_group')
95condition_field = parse_condition(form, 'condition', '')
mbligh44710b62008-03-07 00:25:43 +000096
mbligh439661b2008-02-19 15:57:53 +000097## caller can specify rows and columns that shall be included into the report
98## regardless of whether actual test data is available yet
99force_row_field = parse_condition(form,'force_row','')
100force_column_field = parse_condition(form,'force_column','')
mbligh190a81d2007-11-05 20:40:38 +0000101
mbligh9add5882008-04-16 23:32:01 +0000102
mbligh439661b2008-02-19 15:57:53 +0000103def split_forced_fields(force_field):
104 if force_field:
105 return force_field.split()
106 else:
107 return []
108
109force_row = split_forced_fields(force_row_field)
110force_column = split_forced_fields(force_column_field)
111
mbligh2e4e5df2007-11-05 17:22:46 +0000112cgitb.enable()
mblighaea09602008-04-16 22:59:37 +0000113db_obj = db.db()
mbligh2e4e5df2007-11-05 17:22:46 +0000114
mbligh12eebfa2008-01-03 02:01:53 +0000115
mbligh2ba3e732008-01-16 01:30:19 +0000116def construct_link(x, y):
117 next_row = row
118 next_column = column
mbligh5dd503b2008-01-03 16:35:27 +0000119 condition_list = []
120 if condition_field != '':
121 condition_list.append(condition_field)
mbligh2ba3e732008-01-16 01:30:19 +0000122 if y:
123 next_row = next_field[row]
124 condition_list.append("%s='%s'" % (row, y))
125 if x:
126 next_column = next_field[column]
127 condition_list.append("%s='%s'" % (column, x))
mblighb180f6c2008-01-04 20:24:41 +0000128 next_condition = '&'.join(condition_list)
mblighbe257452008-04-16 23:29:13 +0000129 link = 'compose_query.cgi?' + urllib.urlencode({'columns': next_column,
130 'rows': next_row, 'condition': next_condition,
131 'title': title_field})
132 return link
mbligh5dd503b2008-01-03 16:35:27 +0000133
134
mbligh38e9d782008-05-01 20:49:41 +0000135def construct_logs_link(x, y, job_tag):
136 job_path = frontend.html_root + job_tag + '/'
137 test = ''
138 if (row == 'test' and
139 not y.split('.')[0] in ('boot', 'build', 'install')):
140 test = y
141 if (column == 'test' and
142 not x.split('.')[0] in ('boot', 'build', 'install')):
143 test = x
144 return 'retrieve_logs.cgi?' + urllib.urlencode({'job' : job_path,
145 'test' : test})
146
147
mbligh12eebfa2008-01-03 02:01:53 +0000148def create_select_options(selected_val):
mbligh190a81d2007-11-05 20:40:38 +0000149 ret = ""
mbligh2ba3e732008-01-16 01:30:19 +0000150 for option in sorted(frontend.test_view_field_dict.keys()):
mbligh190a81d2007-11-05 20:40:38 +0000151 if selected_val == option:
152 selected = " SELECTED"
153 else:
154 selected = ""
155
mbligh2ba3e732008-01-16 01:30:19 +0000156 ret += '<OPTION VALUE="%s"%s>%s</OPTION>\n' % \
157 (option, selected, option)
mbligh190a81d2007-11-05 20:40:38 +0000158 return ret
159
160
apw7a7316b2008-02-21 17:42:05 +0000161def map_kernel_base(kernel_name):
mbligh439661b2008-02-19 15:57:53 +0000162 ## insert <br> after each / in kernel name
163 ## but spare consequtive //
mbligh8e7c78e2008-02-20 21:18:49 +0000164 kernel_name = kernel_name.replace('/','/<br>')
165 kernel_name = kernel_name.replace('/<br>/<br>','//')
mbligh439661b2008-02-19 15:57:53 +0000166 return kernel_name
167
168
mbligh44710b62008-03-07 00:25:43 +0000169def header_tuneup(field_name, header):
170 ## header tune up depends on particular field name and may include:
171 ## - breaking header into several strings if it is long url
172 ## - creating date from datetime stamp
173 ## - possibly, expect more various refinements for different fields
174 if field_name == 'kernel':
175 return map_kernel_base(header)
mbligh44710b62008-03-07 00:25:43 +0000176 else:
177 return header
178
179
apw7a7316b2008-02-21 17:42:05 +0000180# Kernel name mappings -- the kernels table 'printable' field is
181# effectively a sortable identifier for the kernel It encodes the base
182# release which is used for overall sorting, plus where patches are
183# applied it adds an increasing pNNN patch combination identifier
184# (actually the kernel_idx for the entry). This allows sorting
185# as normal by the base kernel version and then sub-sorting by the
186# "first time we saw" a patch combination which should keep them in
187# approximatly date order. This patch identifier is not suitable
188# for display, so we have to map it to a suitable html fragment for
189# display. This contains the kernel base version plus the truncated
190# names of all the patches,
191#
192# 2.6.24-mm1 p112
193# +add-new-string-functions-
194# +x86-amd-thermal-interrupt
195#
196# This mapping is produced when the first mapping is request, with
197# a single query over the patches table; the result is then cached.
198#
199# Note: that we only count a base version as patched if it contains
200# patches which are not already "expressed" in the base version.
201# This includes both -gitN and -mmN kernels.
202map_kernel_map = None
203
204
205def map_kernel_init():
206 fields = ['base', 'k.kernel_idx', 'name', 'url']
207 map = {}
mblighaea09602008-04-16 22:59:37 +0000208 for (base, idx, name, url) in db_obj.select(','.join(fields),
apw7a7316b2008-02-21 17:42:05 +0000209 'kernels k,patches p', 'k.kernel_idx=p.kernel_idx'):
210 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$', url)
211 if match:
212 continue
213
214 key = base + ' p%d' % (idx)
215 if not map.has_key(key):
216 map[key] = map_kernel_base(base) + ' p%d' % (idx)
217 map[key] += '<br>+<span title="' + name + '">' + name[0:25] + '</span>'
218
219 return map
220
221
222def map_kernel(name):
223 global map_kernel_map
224 if map_kernel_map == None:
225 map_kernel_map = map_kernel_init()
226
227 if map_kernel_map.has_key(name):
228 return map_kernel_map[name]
229
230 return map_kernel_base(name.split(' ')[0])
231
232
233field_map = {
234 'kernel':map_kernel
235}
236
jadmanski36505a92008-05-01 22:07:02 +0000237sql_wall_time = 0
apw7a7316b2008-02-21 17:42:05 +0000238
mbligh12eebfa2008-01-03 02:01:53 +0000239def gen_matrix():
mbligh12eebfa2008-01-03 02:01:53 +0000240 where = None
241 if condition_field.strip() != '':
mbligh44710b62008-03-07 00:25:43 +0000242 try:
243 where = query_lib.parse_scrub_and_gen_condition(
244 condition_field, frontend.test_view_field_dict)
245 print "<!-- where clause: %s -->" % (where,)
246 except:
247 msg = "Unspecified error when parsing condition"
248 return [[display.box(msg)]]
mbligh2e4e5df2007-11-05 17:22:46 +0000249
jadmanski36505a92008-05-01 22:07:02 +0000250 wall_time_start = time.time()
mblighaea09602008-04-16 22:59:37 +0000251 try:
mbligh31260692008-04-16 23:12:12 +0000252 ## Unfortunately, we can not request reasons of failure always
253 ## because it may result in an inflated size of data transfer
254 ## (at the moment we fetch 500 bytes of reason descriptions into
255 ## each cell )
256 ## If 'status' in [row,column] then either width or height
257 ## of the table <=7, hence table is not really 2D, and
258 ## query_reason is relatively save.
259 ## At the same time view when either rows or columns grouped
260 ## by status is when users need reasons of failures the most.
261
262 ## TO DO: implement [Show/Hide reasons] button or link in
263 ## all views and make thorough performance testing
264 test_data = frontend.get_matrix_data(db_obj, column, row, where,
265 query_reasons = ('status' in [row,column])
266 )
jadmanski36505a92008-05-01 22:07:02 +0000267 global sql_wall_time
268 sql_wall_time = time.time() - wall_time_start
269
mblighaea09602008-04-16 22:59:37 +0000270 except db.MySQLTooManyRows, error:
271 return [[display.box(str(error))]]
mbligh44710b62008-03-07 00:25:43 +0000272
mbligh439661b2008-02-19 15:57:53 +0000273 for f_row in force_row:
274 if not f_row in test_data.y_values:
275 test_data.y_values.append(f_row)
276 for f_column in force_column:
277 if not f_column in test_data.x_values:
278 test_data.x_values.append(f_column)
mbligh2e4e5df2007-11-05 17:22:46 +0000279
mbligh2ba3e732008-01-16 01:30:19 +0000280 if not test_data.y_values:
mbligh12eebfa2008-01-03 02:01:53 +0000281 msg = "There are no results for this query (yet?)."
282 return [[display.box(msg)]]
mbligh2e4e5df2007-11-05 17:22:46 +0000283
mbligh456b4772008-03-25 23:54:45 +0000284 dict_url = {'columns': row,
mblighbe257452008-04-16 23:29:13 +0000285 'rows': column, 'condition': condition_field,
286 'title': title_field}
mbligh456b4772008-03-25 23:54:45 +0000287 link = 'compose_query.cgi?' + urllib.urlencode(dict_url)
mbligh5dd503b2008-01-03 16:35:27 +0000288 header_row = [display.box("<center>(Flip Axis)</center>", link=link)]
289
mbligh2ba3e732008-01-16 01:30:19 +0000290 for x in test_data.x_values:
apw7a7316b2008-02-21 17:42:05 +0000291 dx = x
292 if field_map.has_key(column):
293 dx = field_map[column](x)
mbligh44710b62008-03-07 00:25:43 +0000294 x_header = header_tuneup(column, dx)
295 link = construct_link(x, None)
296 header_row.append(display.box(x_header,header=True,link=link))
mbligh2e4e5df2007-11-05 17:22:46 +0000297
298 matrix = [header_row]
mbligh2ba3e732008-01-16 01:30:19 +0000299 for y in test_data.y_values:
apw7a7316b2008-02-21 17:42:05 +0000300 dy = y
301 if field_map.has_key(row):
302 dy = field_map[row](y)
mbligh44710b62008-03-07 00:25:43 +0000303 y_header = header_tuneup(row, dy)
mbligh5bb55862008-04-16 23:09:31 +0000304 link = construct_link(None, y)
mbligh44710b62008-03-07 00:25:43 +0000305 cur_row = [display.box(y_header, header=True, link=link)]
mbligh2ba3e732008-01-16 01:30:19 +0000306 for x in test_data.x_values:
mbligh44710b62008-03-07 00:25:43 +0000307 ## next 2 lines: temporary, until non timestamped
308 ## records are in the database
309 if x==datetime.datetime(1970,1,1): x = None
310 if y==datetime.datetime(1970,1,1): y = None
mbligh12eebfa2008-01-03 02:01:53 +0000311 try:
mbligh31260692008-04-16 23:12:12 +0000312 box_data = test_data.data[x][y]
mbligh12eebfa2008-01-03 02:01:53 +0000313 except:
314 cur_row.append(display.box(None, None))
315 continue
mbligh2ba3e732008-01-16 01:30:19 +0000316 job_tag = test_data.data[x][y].job_tag
mbligh5dd503b2008-01-03 16:35:27 +0000317 if job_tag:
mbligh38e9d782008-05-01 20:49:41 +0000318 link = construct_logs_link(x, y, job_tag)
mbligh5dd503b2008-01-03 16:35:27 +0000319 else:
mbligh2ba3e732008-01-16 01:30:19 +0000320 link = construct_link(x, y)
mbligh44710b62008-03-07 00:25:43 +0000321
mblighaea09602008-04-16 22:59:37 +0000322 cur_row.append(display.status_precounted_box(db_obj,
mbligh5dd503b2008-01-03 16:35:27 +0000323 box_data,
324 link))
mbligh12eebfa2008-01-03 02:01:53 +0000325 matrix.append(cur_row)
mbligh12eebfa2008-01-03 02:01:53 +0000326 return matrix
mbligh2b672532007-11-05 19:24:51 +0000327
mbligh2b672532007-11-05 19:24:51 +0000328
mbligh12eebfa2008-01-03 02:01:53 +0000329def main():
mbligh190a81d2007-11-05 20:40:38 +0000330 # create the actual page
mbligh190a81d2007-11-05 20:40:38 +0000331 print '<html><head><title>'
332 print 'Filtered Autotest Results'
333 print '</title></head><body>'
mbligh14671622008-01-11 16:49:54 +0000334 display.print_main_header()
mbligh2ba3e732008-01-16 01:30:19 +0000335 print html_header % (create_select_options(column),
336 create_select_options(row),
mblighbe257452008-04-16 23:29:13 +0000337 condition_field, title_field)
338 if title_field:
339 print '<h1> %s </h1>' % (title_field)
mbligh439661b2008-02-19 15:57:53 +0000340 print display.color_keys_row()
mbligh12eebfa2008-01-03 02:01:53 +0000341 display.print_table(gen_matrix())
mbligh439661b2008-02-19 15:57:53 +0000342 print display.color_keys_row()
jadmanski36505a92008-05-01 22:07:02 +0000343 total_wall_time = time.time() - total_wall_time_start
344 print '<p style="font-size:x-small;">sql access wall time = %s secs, \
345 total wall time = %s secs</p>' % (sql_wall_time, total_wall_time)
mbligh190a81d2007-11-05 20:40:38 +0000346 print '</body></html>'
mbligh2e4e5df2007-11-05 17:22:46 +0000347
mbligh2e4e5df2007-11-05 17:22:46 +0000348main()