blob: 66fe05203199c86ad1d074d34e7552466c529c10 [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"
mbligh2e4e5df2007-11-05 17:22:46 +000010
jadmanski22ab0692008-05-01 22:22:51 +000011import sys, os, urllib, cgi, cgitb, re, datetime, time
mbligh2e4e5df2007-11-05 17:22:46 +000012
jadmanski22ab0692008-05-01 22:22:51 +000013total_wall_time_start = time.time()
14
15import common
16from autotest_lib.tko import display, frontend, db, query_lib
17from autotest_lib.client.bin import kernel_versions
mbligh12eebfa2008-01-03 02:01:53 +000018
mbligh190a81d2007-11-05 20:40:38 +000019html_header = """\
20<form action="compose_query.cgi" method="get">
21<table border="0">
22<tr>
23 <td>Column: </td>
24 <td>Row: </td>
25 <td>Condition: </td>
mbligh44710b62008-03-07 00:25:43 +000026 <td align="center">
27 <a href="http://test.kernel.org/autotest/AutotestTKOCondition">Help</a>
28 </td>
mbligh190a81d2007-11-05 20:40:38 +000029</tr>
30<tr>
31 <td>
32 <SELECT NAME="columns">
33 %s
34 </SELECT>
35 </td>
36 <td>
37 <SELECT NAME="rows">
38 %s
39 </SELECT>
40 </td>
41 <td>
mbligh44710b62008-03-07 00:25:43 +000042 <input type="text" name="condition" size="30" value="%s">
mblighbe257452008-04-16 23:29:13 +000043 <input type="hidden" name="title" value="%s">
mbligh190a81d2007-11-05 20:40:38 +000044 </td>
45 <td align="center"><input type="submit" value="Submit">
46 </td>
47</tr>
48</table>
49</form>
mbligh5a42d852008-05-15 23:49:04 +000050<form action="save_query.cgi" method="get">
51<table border="0">
52<tr>
53 <td>Name your query:&nbsp;&nbsp;</td>
54 <td>
55 <input type="text" name="label" size="15" value="">
56 </td>
57 <td align="center">&nbsp;<input type="submit" value="Save Query">
58 </td>
59 <td>&nbsp;&nbsp;<a href="query_history.cgi">View saved queries</a></td>
60</tr>
61</table>
62</form>
mbligh190a81d2007-11-05 20:40:38 +000063"""
64
mbligh12eebfa2008-01-03 02:01:53 +000065
mbligh5dd503b2008-01-03 16:35:27 +000066next_field = {
mbligh44710b62008-03-07 00:25:43 +000067 'machine_group': 'hostname',
68 'hostname': 'tag',
69 'tag': 'tag',
mbligh5dd503b2008-01-03 16:35:27 +000070
mbligh44710b62008-03-07 00:25:43 +000071 'kernel': 'test',
72 'test': 'label',
73 'label': 'tag',
mbligh5dd503b2008-01-03 16:35:27 +000074
mbligh44710b62008-03-07 00:25:43 +000075 'reason': 'tag',
76 'user': 'tag',
77 'status': 'tag',
78
mbligh5bb55862008-04-16 23:09:31 +000079 'time': 'tag',
80 'time_daily': 'time',
mbligh5dd503b2008-01-03 16:35:27 +000081}
82
83
mbligh11204f82008-04-16 23:30:07 +000084def parse_field(form, form_field, field_default):
85 if not form_field in form:
86 return field_default
mbligh12eebfa2008-01-03 02:01:53 +000087 field_input = form[form_field].value.lower()
mbligh2ba3e732008-01-16 01:30:19 +000088 if field_input and field_input in frontend.test_view_field_dict:
mbligh12eebfa2008-01-03 02:01:53 +000089 return field_input
mbligh11204f82008-04-16 23:30:07 +000090 return field_default
mbligh12eebfa2008-01-03 02:01:53 +000091
92
93def parse_condition(form, form_field, field_default):
94 if not form_field in form:
95 return field_default
96 return form[form_field].value
97
98
99form = cgi.FieldStorage()
mbligh3d7a5f52008-04-16 23:06:36 +0000100
mblighbe257452008-04-16 23:29:13 +0000101title_field = parse_condition(form, 'title', '')
mbligh9add5882008-04-16 23:32:01 +0000102row = parse_field(form, 'rows', 'kernel')
103column = parse_field(form, 'columns', 'machine_group')
104condition_field = parse_condition(form, 'condition', '')
mbligh44710b62008-03-07 00:25:43 +0000105
mbligh5684b032008-06-06 14:25:35 +0000106if 'brief' in form.keys() and form['brief'].value <> '0':
107 display.set_brief_mode()
108
mbligh439661b2008-02-19 15:57:53 +0000109## caller can specify rows and columns that shall be included into the report
110## regardless of whether actual test data is available yet
111force_row_field = parse_condition(form,'force_row','')
112force_column_field = parse_condition(form,'force_column','')
mbligh190a81d2007-11-05 20:40:38 +0000113
mbligh9add5882008-04-16 23:32:01 +0000114
mbligh439661b2008-02-19 15:57:53 +0000115def split_forced_fields(force_field):
116 if force_field:
117 return force_field.split()
118 else:
119 return []
120
121force_row = split_forced_fields(force_row_field)
122force_column = split_forced_fields(force_column_field)
123
mbligh2e4e5df2007-11-05 17:22:46 +0000124cgitb.enable()
mblighaea09602008-04-16 22:59:37 +0000125db_obj = db.db()
mbligh2e4e5df2007-11-05 17:22:46 +0000126
mbligh12eebfa2008-01-03 02:01:53 +0000127
mbligh2ba3e732008-01-16 01:30:19 +0000128def construct_link(x, y):
129 next_row = row
130 next_column = column
mbligh5dd503b2008-01-03 16:35:27 +0000131 condition_list = []
132 if condition_field != '':
133 condition_list.append(condition_field)
mbligh2ba3e732008-01-16 01:30:19 +0000134 if y:
135 next_row = next_field[row]
136 condition_list.append("%s='%s'" % (row, y))
137 if x:
138 next_column = next_field[column]
139 condition_list.append("%s='%s'" % (column, x))
mblighb180f6c2008-01-04 20:24:41 +0000140 next_condition = '&'.join(condition_list)
mblighbe257452008-04-16 23:29:13 +0000141 link = 'compose_query.cgi?' + urllib.urlencode({'columns': next_column,
142 'rows': next_row, 'condition': next_condition,
143 'title': title_field})
144 return link
mbligh5dd503b2008-01-03 16:35:27 +0000145
146
mbligh38e9d782008-05-01 20:49:41 +0000147def construct_logs_link(x, y, job_tag):
148 job_path = frontend.html_root + job_tag + '/'
149 test = ''
150 if (row == 'test' and
151 not y.split('.')[0] in ('boot', 'build', 'install')):
152 test = y
153 if (column == 'test' and
154 not x.split('.')[0] in ('boot', 'build', 'install')):
155 test = x
156 return 'retrieve_logs.cgi?' + urllib.urlencode({'job' : job_path,
157 'test' : test})
158
159
mbligh12eebfa2008-01-03 02:01:53 +0000160def create_select_options(selected_val):
mbligh190a81d2007-11-05 20:40:38 +0000161 ret = ""
mbligh2ba3e732008-01-16 01:30:19 +0000162 for option in sorted(frontend.test_view_field_dict.keys()):
mbligh190a81d2007-11-05 20:40:38 +0000163 if selected_val == option:
164 selected = " SELECTED"
165 else:
166 selected = ""
167
mbligh2ba3e732008-01-16 01:30:19 +0000168 ret += '<OPTION VALUE="%s"%s>%s</OPTION>\n' % \
169 (option, selected, option)
mbligh190a81d2007-11-05 20:40:38 +0000170 return ret
171
172
apw7a7316b2008-02-21 17:42:05 +0000173def map_kernel_base(kernel_name):
mbligh439661b2008-02-19 15:57:53 +0000174 ## insert <br> after each / in kernel name
175 ## but spare consequtive //
mbligh8e7c78e2008-02-20 21:18:49 +0000176 kernel_name = kernel_name.replace('/','/<br>')
177 kernel_name = kernel_name.replace('/<br>/<br>','//')
mbligh439661b2008-02-19 15:57:53 +0000178 return kernel_name
179
180
mbligh44710b62008-03-07 00:25:43 +0000181def header_tuneup(field_name, header):
182 ## header tune up depends on particular field name and may include:
183 ## - breaking header into several strings if it is long url
184 ## - creating date from datetime stamp
185 ## - possibly, expect more various refinements for different fields
186 if field_name == 'kernel':
187 return map_kernel_base(header)
mbligh44710b62008-03-07 00:25:43 +0000188 else:
189 return header
190
191
apw7a7316b2008-02-21 17:42:05 +0000192# Kernel name mappings -- the kernels table 'printable' field is
193# effectively a sortable identifier for the kernel It encodes the base
194# release which is used for overall sorting, plus where patches are
195# applied it adds an increasing pNNN patch combination identifier
196# (actually the kernel_idx for the entry). This allows sorting
197# as normal by the base kernel version and then sub-sorting by the
198# "first time we saw" a patch combination which should keep them in
199# approximatly date order. This patch identifier is not suitable
200# for display, so we have to map it to a suitable html fragment for
201# display. This contains the kernel base version plus the truncated
202# names of all the patches,
203#
204# 2.6.24-mm1 p112
205# +add-new-string-functions-
206# +x86-amd-thermal-interrupt
207#
208# This mapping is produced when the first mapping is request, with
209# a single query over the patches table; the result is then cached.
210#
211# Note: that we only count a base version as patched if it contains
212# patches which are not already "expressed" in the base version.
213# This includes both -gitN and -mmN kernels.
214map_kernel_map = None
215
216
217def map_kernel_init():
218 fields = ['base', 'k.kernel_idx', 'name', 'url']
219 map = {}
mblighaea09602008-04-16 22:59:37 +0000220 for (base, idx, name, url) in db_obj.select(','.join(fields),
apw7a7316b2008-02-21 17:42:05 +0000221 'kernels k,patches p', 'k.kernel_idx=p.kernel_idx'):
222 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$', url)
223 if match:
224 continue
225
226 key = base + ' p%d' % (idx)
227 if not map.has_key(key):
228 map[key] = map_kernel_base(base) + ' p%d' % (idx)
mbligh5a42d852008-05-15 23:49:04 +0000229 map[key] += ('<br>+<span title="' + name + '">' +
230 name[0:25] + '</span>')
apw7a7316b2008-02-21 17:42:05 +0000231
232 return map
233
234
235def map_kernel(name):
236 global map_kernel_map
237 if map_kernel_map == None:
238 map_kernel_map = map_kernel_init()
239
240 if map_kernel_map.has_key(name):
241 return map_kernel_map[name]
242
243 return map_kernel_base(name.split(' ')[0])
244
245
246field_map = {
247 'kernel':map_kernel
248}
249
jadmanski36505a92008-05-01 22:07:02 +0000250sql_wall_time = 0
apw7a7316b2008-02-21 17:42:05 +0000251
mbligh12eebfa2008-01-03 02:01:53 +0000252def gen_matrix():
mbligh12eebfa2008-01-03 02:01:53 +0000253 where = None
254 if condition_field.strip() != '':
mbligh44710b62008-03-07 00:25:43 +0000255 try:
256 where = query_lib.parse_scrub_and_gen_condition(
257 condition_field, frontend.test_view_field_dict)
258 print "<!-- where clause: %s -->" % (where,)
259 except:
260 msg = "Unspecified error when parsing condition"
261 return [[display.box(msg)]]
mbligh2e4e5df2007-11-05 17:22:46 +0000262
jadmanski36505a92008-05-01 22:07:02 +0000263 wall_time_start = time.time()
mblighaea09602008-04-16 22:59:37 +0000264 try:
mbligh31260692008-04-16 23:12:12 +0000265 ## Unfortunately, we can not request reasons of failure always
266 ## because it may result in an inflated size of data transfer
267 ## (at the moment we fetch 500 bytes of reason descriptions into
268 ## each cell )
269 ## If 'status' in [row,column] then either width or height
270 ## of the table <=7, hence table is not really 2D, and
271 ## query_reason is relatively save.
272 ## At the same time view when either rows or columns grouped
273 ## by status is when users need reasons of failures the most.
274
275 ## TO DO: implement [Show/Hide reasons] button or link in
276 ## all views and make thorough performance testing
277 test_data = frontend.get_matrix_data(db_obj, column, row, where,
278 query_reasons = ('status' in [row,column])
279 )
jadmanski36505a92008-05-01 22:07:02 +0000280 global sql_wall_time
281 sql_wall_time = time.time() - wall_time_start
282
mblighaea09602008-04-16 22:59:37 +0000283 except db.MySQLTooManyRows, error:
284 return [[display.box(str(error))]]
mbligh44710b62008-03-07 00:25:43 +0000285
mbligh439661b2008-02-19 15:57:53 +0000286 for f_row in force_row:
287 if not f_row in test_data.y_values:
288 test_data.y_values.append(f_row)
289 for f_column in force_column:
290 if not f_column in test_data.x_values:
291 test_data.x_values.append(f_column)
mbligh2e4e5df2007-11-05 17:22:46 +0000292
mbligh2ba3e732008-01-16 01:30:19 +0000293 if not test_data.y_values:
mbligh12eebfa2008-01-03 02:01:53 +0000294 msg = "There are no results for this query (yet?)."
295 return [[display.box(msg)]]
mbligh2e4e5df2007-11-05 17:22:46 +0000296
mbligh456b4772008-03-25 23:54:45 +0000297 dict_url = {'columns': row,
mblighbe257452008-04-16 23:29:13 +0000298 'rows': column, 'condition': condition_field,
299 'title': title_field}
mbligh456b4772008-03-25 23:54:45 +0000300 link = 'compose_query.cgi?' + urllib.urlencode(dict_url)
mbligh5dd503b2008-01-03 16:35:27 +0000301 header_row = [display.box("<center>(Flip Axis)</center>", link=link)]
302
mbligh2ba3e732008-01-16 01:30:19 +0000303 for x in test_data.x_values:
apw7a7316b2008-02-21 17:42:05 +0000304 dx = x
305 if field_map.has_key(column):
306 dx = field_map[column](x)
mbligh44710b62008-03-07 00:25:43 +0000307 x_header = header_tuneup(column, dx)
308 link = construct_link(x, None)
309 header_row.append(display.box(x_header,header=True,link=link))
mbligh2e4e5df2007-11-05 17:22:46 +0000310
311 matrix = [header_row]
jadmanskif9fa4272008-05-02 15:43:33 +0000312 # For each row, we are looping horizontally over the columns.
mbligh2ba3e732008-01-16 01:30:19 +0000313 for y in test_data.y_values:
apw7a7316b2008-02-21 17:42:05 +0000314 dy = y
315 if field_map.has_key(row):
316 dy = field_map[row](y)
mbligh44710b62008-03-07 00:25:43 +0000317 y_header = header_tuneup(row, dy)
mbligh5bb55862008-04-16 23:09:31 +0000318 link = construct_link(None, y)
mbligh44710b62008-03-07 00:25:43 +0000319 cur_row = [display.box(y_header, header=True, link=link)]
mbligh2ba3e732008-01-16 01:30:19 +0000320 for x in test_data.x_values:
mbligh44710b62008-03-07 00:25:43 +0000321 ## next 2 lines: temporary, until non timestamped
322 ## records are in the database
323 if x==datetime.datetime(1970,1,1): x = None
324 if y==datetime.datetime(1970,1,1): y = None
mbligh12eebfa2008-01-03 02:01:53 +0000325 try:
mbligh31260692008-04-16 23:12:12 +0000326 box_data = test_data.data[x][y]
mbligh12eebfa2008-01-03 02:01:53 +0000327 except:
jadmanskif9fa4272008-05-02 15:43:33 +0000328 cur_row.append(display.box(None, None,
mbligh5a42d852008-05-15 23:49:04 +0000329 row_label=y, column_label=x))
mbligh12eebfa2008-01-03 02:01:53 +0000330 continue
mbligh2ba3e732008-01-16 01:30:19 +0000331 job_tag = test_data.data[x][y].job_tag
mbligh5dd503b2008-01-03 16:35:27 +0000332 if job_tag:
mbligh38e9d782008-05-01 20:49:41 +0000333 link = construct_logs_link(x, y, job_tag)
mbligh5dd503b2008-01-03 16:35:27 +0000334 else:
mbligh2ba3e732008-01-16 01:30:19 +0000335 link = construct_link(x, y)
jadmanskif9fa4272008-05-02 15:43:33 +0000336
mbligh5a42d852008-05-15 23:49:04 +0000337 apnd = display.status_precounted_box(db_obj, box_data,
338 link, y, x)
339 cur_row.append(apnd)
mbligh12eebfa2008-01-03 02:01:53 +0000340 matrix.append(cur_row)
mbligh12eebfa2008-01-03 02:01:53 +0000341 return matrix
mbligh2b672532007-11-05 19:24:51 +0000342
mbligh2b672532007-11-05 19:24:51 +0000343
mbligh12eebfa2008-01-03 02:01:53 +0000344def main():
mbligh5684b032008-06-06 14:25:35 +0000345 if display.is_brief_mode():
346 ## create main grid table only as provided by gen_matrix()
347 display.print_table(gen_matrix())
348 else:
349 # create the actual page
350 print '<html><head><title>'
351 print 'Filtered Autotest Results'
352 print '</title></head><body>'
353 display.print_main_header()
354 print html_header % (create_select_options(column),
355 create_select_options(row),
356 condition_field, title_field)
357 if title_field:
358 print '<h1> %s </h1>' % (title_field)
359 print display.color_keys_row()
360 display.print_table(gen_matrix())
361 print display.color_keys_row()
362 total_wall_time = time.time() - total_wall_time_start
363
364 perf_info = '<p style="font-size:x-small;">'
365 perf_info += 'sql access wall time = %s secs,' % sql_wall_time
366 perf_info += 'total wall time = %s secs</p>' % total_wall_time
367 print perf_info
368 print '</body></html>'
369
mbligh2e4e5df2007-11-05 17:22:46 +0000370
mbligh2e4e5df2007-11-05 17:22:46 +0000371main()