mbligh | 4a370cf | 2008-04-01 19:56:05 +0000 | [diff] [blame] | 1 | import os, re, db, sys, datetime |
mbligh | 753362d | 2009-06-22 18:37:49 +0000 | [diff] [blame] | 2 | import common |
| 3 | from autotest_lib.client.common_lib import kernel_versions |
| 4 | |
mbligh | 27eab24 | 2008-05-21 18:24:10 +0000 | [diff] [blame] | 5 | MAX_RECORDS = 50000L |
| 6 | MAX_CELLS = 500000L |
mbligh | 9bb92fe | 2007-09-12 15:54:23 +0000 | [diff] [blame] | 7 | |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 8 | tko = os.path.dirname(os.path.realpath(os.path.abspath(__file__))) |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 9 | root_url_file = os.path.join(tko, '.root_url') |
| 10 | if os.path.exists(root_url_file): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 11 | html_root = open(root_url_file, 'r').readline().rstrip() |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 12 | else: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 13 | html_root = '/results/' |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 14 | |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 15 | |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 16 | class status_cell: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 17 | # One cell in the matrix of status data. |
| 18 | def __init__(self): |
| 19 | # Count is a dictionary: status -> count of tests with status |
| 20 | self.status_count = {} |
| 21 | self.reasons_list = [] |
| 22 | self.job_tag = None |
| 23 | self.job_tag_count = 0 |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 24 | |
mbligh | 83f63a0 | 2007-12-12 19:13:04 +0000 | [diff] [blame] | 25 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 26 | def add(self, status, count, job_tags, reasons = None): |
| 27 | assert count > 0 |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 28 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 29 | self.job_tag = job_tags |
| 30 | self.job_tag_count += count |
| 31 | if self.job_tag_count > 1: |
| 32 | self.job_tag = None |
| 33 | |
| 34 | self.status_count[status] = count |
| 35 | ### status == 6 means 'GOOD' |
| 36 | if status != 6: |
| 37 | ## None implies sorting problems and extra CRs in a cell |
| 38 | if reasons: |
| 39 | self.reasons_list.append(reasons) |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 40 | |
| 41 | |
| 42 | class status_data: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 43 | def __init__(self, sql_rows, x_field, y_field, query_reasons = False): |
| 44 | data = {} |
| 45 | y_values = set() |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 46 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 47 | # Walk through the query, filing all results by x, y info |
| 48 | for row in sql_rows: |
| 49 | if query_reasons: |
| 50 | (x,y, status, count, job_tags, reasons) = row |
| 51 | else: |
| 52 | (x,y, status, count, job_tags) = row |
| 53 | reasons = None |
| 54 | if not data.has_key(x): |
| 55 | data[x] = {} |
| 56 | if not data[x].has_key(y): |
| 57 | y_values.add(y) |
| 58 | data[x][y] = status_cell() |
| 59 | data[x][y].add(status, count, job_tags, reasons) |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 60 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 61 | # 2-d hash of data - [x-value][y-value] |
| 62 | self.data = data |
| 63 | # List of possible columns (x-values) |
| 64 | self.x_values = smart_sort(data.keys(), x_field) |
| 65 | # List of rows columns (y-values) |
| 66 | self.y_values = smart_sort(list(y_values), y_field) |
| 67 | nCells = len(self.y_values)*len(self.x_values) |
| 68 | if nCells > MAX_CELLS: |
| 69 | msg = 'Exceeded allowed number of cells in a table' |
| 70 | raise db.MySQLTooManyRows(msg) |
| 71 | |
mbligh | 83f63a0 | 2007-12-12 19:13:04 +0000 | [diff] [blame] | 72 | |
mbligh | 3126069 | 2008-04-16 23:12:12 +0000 | [diff] [blame] | 73 | def get_matrix_data(db_obj, x_axis, y_axis, where = None, |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 74 | query_reasons = False): |
| 75 | # Searches on the test_view table - x_axis and y_axis must both be |
| 76 | # column names in that table. |
| 77 | x_field = test_view_field_dict[x_axis] |
| 78 | y_field = test_view_field_dict[y_axis] |
| 79 | query_fields_list = [x_field, y_field, 'status','COUNT(status)'] |
| 80 | query_fields_list.append("LEFT(GROUP_CONCAT(job_tag),100)") |
| 81 | if query_reasons: |
| 82 | query_fields_list.append( |
| 83 | "LEFT(GROUP_CONCAT(DISTINCT reason SEPARATOR '|'),500)" |
| 84 | ) |
| 85 | fields = ','.join(query_fields_list) |
mbligh | 3126069 | 2008-04-16 23:12:12 +0000 | [diff] [blame] | 86 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 87 | group_by = '%s, %s, status' % (x_field, y_field) |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 88 | rows = db_obj.select(fields, 'tko_test_view', |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 89 | where=where, group_by=group_by, max_rows = MAX_RECORDS) |
| 90 | return status_data(rows, x_field, y_field, query_reasons) |
mbligh | 83f63a0 | 2007-12-12 19:13:04 +0000 | [diff] [blame] | 91 | |
| 92 | |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 93 | # Dictionary used simply for fast lookups from short reference names for users |
| 94 | # to fieldnames in test_view |
| 95 | test_view_field_dict = { |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 96 | 'kernel' : 'kernel_printable', |
| 97 | 'hostname' : 'machine_hostname', |
| 98 | 'test' : 'test', |
| 99 | 'label' : 'job_label', |
| 100 | 'machine_group' : 'machine_group', |
| 101 | 'reason' : 'reason', |
| 102 | 'tag' : 'job_tag', |
| 103 | 'user' : 'job_username', |
| 104 | 'status' : 'status_word', |
| 105 | 'time' : 'test_finished_time', |
mbligh | 8d88a6d | 2009-02-05 21:37:12 +0000 | [diff] [blame] | 106 | 'start_time' : 'test_started_time', |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 107 | 'time_daily' : 'DATE(test_finished_time)' |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 108 | } |
mbligh | 2b67253 | 2007-11-05 19:24:51 +0000 | [diff] [blame] | 109 | |
mbligh | 3126069 | 2008-04-16 23:12:12 +0000 | [diff] [blame] | 110 | |
mbligh | 2ba3e73 | 2008-01-16 01:30:19 +0000 | [diff] [blame] | 111 | def smart_sort(list, field): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 112 | if field == 'kernel_printable': |
| 113 | def kernel_encode(kernel): |
| 114 | return kernel_versions.version_encode(kernel) |
| 115 | list.sort(key = kernel_encode, reverse = True) |
| 116 | return list |
| 117 | ## old records may contain time=None |
| 118 | ## make None comparable with timestamp datetime or date |
| 119 | elif field == 'test_finished_time': |
| 120 | def convert_None_to_datetime(date_time): |
| 121 | if not date_time: |
| 122 | return datetime.datetime(1970, 1, 1, 0, 0, 0) |
| 123 | else: |
| 124 | return date_time |
| 125 | list = map(convert_None_to_datetime, list) |
| 126 | elif field == 'DATE(test_finished_time)': |
| 127 | def convert_None_to_date(date): |
| 128 | if not date: |
| 129 | return datetime.date(1970, 1, 1) |
| 130 | else: |
| 131 | return date |
| 132 | list = map(convert_None_to_date, list) |
| 133 | list.sort() |
| 134 | return list |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 135 | |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 136 | |
mbligh | cff2d21 | 2007-10-07 00:11:10 +0000 | [diff] [blame] | 137 | class group: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 138 | @classmethod |
| 139 | def select(klass, db): |
| 140 | """Return all possible machine groups""" |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 141 | rows = db.select('distinct machine_group', 'tko_machines', |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 142 | 'machine_group is not null') |
| 143 | groupnames = sorted([row[0] for row in rows]) |
| 144 | return [klass(db, groupname) for groupname in groupnames] |
mbligh | 83f63a0 | 2007-12-12 19:13:04 +0000 | [diff] [blame] | 145 | |
| 146 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 147 | def __init__(self, db, name): |
| 148 | self.name = name |
| 149 | self.db = db |
mbligh | cff2d21 | 2007-10-07 00:11:10 +0000 | [diff] [blame] | 150 | |
| 151 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 152 | def machines(self): |
| 153 | return machine.select(self.db, { 'machine_group' : self.name }) |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 154 | |
mbligh | cff2d21 | 2007-10-07 00:11:10 +0000 | [diff] [blame] | 155 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 156 | def tests(self, where = {}): |
| 157 | values = [self.name] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 158 | sql = 't inner join tko_machines m on m.machine_idx=t.machine_idx' |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 159 | sql += ' where m.machine_group=%s' |
| 160 | for key in where.keys(): |
| 161 | sql += ' and %s=%%s' % key |
| 162 | values.append(where[key]) |
| 163 | return test.select_sql(self.db, sql, values) |
mbligh | cff2d21 | 2007-10-07 00:11:10 +0000 | [diff] [blame] | 164 | |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 165 | |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 166 | class machine: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 167 | @classmethod |
| 168 | def select(klass, db, where = {}): |
| 169 | fields = ['machine_idx', 'hostname', 'machine_group', 'owner'] |
| 170 | machines = [] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 171 | for row in db.select(','.join(fields), 'tko_machines', where): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 172 | machines.append(klass(db, *row)) |
| 173 | return machines |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 174 | |
| 175 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 176 | def __init__(self, db, idx, hostname, group, owner): |
| 177 | self.db = db |
| 178 | self.idx = idx |
| 179 | self.hostname = hostname |
| 180 | self.group = group |
| 181 | self.owner = owner |
mbligh | 2e4e5df | 2007-11-05 17:22:46 +0000 | [diff] [blame] | 182 | |
mbligh | 250300e | 2007-09-18 00:50:57 +0000 | [diff] [blame] | 183 | |
mbligh | 9bb92fe | 2007-09-12 15:54:23 +0000 | [diff] [blame] | 184 | class kernel: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 185 | @classmethod |
| 186 | def select(klass, db, where = {}): |
| 187 | fields = ['kernel_idx', 'kernel_hash', 'base', 'printable'] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 188 | rows = db.select(','.join(fields), 'tko_kernels', where) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 189 | return [klass(db, *row) for row in rows] |
mbligh | 9bb92fe | 2007-09-12 15:54:23 +0000 | [diff] [blame] | 190 | |
mbligh | 8e1ab17 | 2007-09-13 17:29:56 +0000 | [diff] [blame] | 191 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 192 | def __init__(self, db, idx, hash, base, printable): |
| 193 | self.db = db |
| 194 | self.idx = idx |
| 195 | self.hash = hash |
| 196 | self.base = base |
| 197 | self.printable = printable |
| 198 | self.patches = [] # THIS SHOULD PULL IN PATCHES! |
mbligh | 9bb92fe | 2007-09-12 15:54:23 +0000 | [diff] [blame] | 199 | |
| 200 | |
| 201 | class test: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 202 | @classmethod |
showard | c1a98d1 | 2010-01-15 00:22:22 +0000 | [diff] [blame] | 203 | def select(klass, db, where={}, distinct=False): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 204 | fields = ['test_idx', 'job_idx', 'test', 'subdir', |
| 205 | 'kernel_idx', 'status', 'reason', 'machine_idx'] |
| 206 | tests = [] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 207 | for row in db.select(','.join(fields), 'tko_tests', where, |
showard | c1a98d1 | 2010-01-15 00:22:22 +0000 | [diff] [blame] | 208 | distinct): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 209 | tests.append(klass(db, *row)) |
| 210 | return tests |
mbligh | 8e1ab17 | 2007-09-13 17:29:56 +0000 | [diff] [blame] | 211 | |
| 212 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 213 | @classmethod |
| 214 | def select_sql(klass, db, sql, values): |
| 215 | fields = ['test_idx', 'job_idx', 'test', 'subdir', |
| 216 | 'kernel_idx', 'status', 'reason', 'machine_idx'] |
| 217 | fields = ['t.'+field for field in fields] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 218 | rows = db.select_sql(','.join(fields), 'tko_tests', sql, values) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 219 | return [klass(db, *row) for row in rows] |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 220 | |
mbligh | 50a2525 | 2007-09-27 15:26:17 +0000 | [diff] [blame] | 221 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 222 | def __init__(self, db, test_idx, job_idx, testname, subdir, kernel_idx, |
| 223 | status_num, reason, machine_idx): |
| 224 | self.idx = test_idx |
| 225 | self.job = job(db, job_idx) |
| 226 | self.testname = testname |
| 227 | self.subdir = subdir |
| 228 | self.kernel_idx = kernel_idx |
| 229 | self.__kernel = None |
| 230 | self.__iterations = None |
| 231 | self.machine_idx = machine_idx |
| 232 | self.__machine = None |
| 233 | self.status_num = status_num |
| 234 | self.status_word = db.status_word[status_num] |
| 235 | self.reason = reason |
| 236 | self.db = db |
| 237 | if self.subdir: |
| 238 | self.url = html_root + self.job.tag + '/' + self.subdir |
| 239 | else: |
| 240 | self.url = None |
mbligh | 50a2525 | 2007-09-27 15:26:17 +0000 | [diff] [blame] | 241 | |
mbligh | 250300e | 2007-09-18 00:50:57 +0000 | [diff] [blame] | 242 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 243 | def iterations(self): |
| 244 | """ |
| 245 | Caching function for iterations |
| 246 | """ |
| 247 | if not self.__iterations: |
| 248 | self.__iterations = {} |
| 249 | # A dictionary - dict{key} = [value1, value2, ....] |
| 250 | where = {'test_idx' : self.idx} |
| 251 | for i in iteration.select(self.db, where): |
| 252 | if self.__iterations.has_key(i.key): |
| 253 | self.__iterations[i.key].append(i.value) |
| 254 | else: |
| 255 | self.__iterations[i.key] = [i.value] |
| 256 | return self.__iterations |
| 257 | |
| 258 | |
| 259 | def kernel(self): |
| 260 | """ |
| 261 | Caching function for kernels |
| 262 | """ |
| 263 | if not self.__kernel: |
| 264 | where = {'kernel_idx' : self.kernel_idx} |
| 265 | self.__kernel = kernel.select(self.db, where)[0] |
| 266 | return self.__kernel |
| 267 | |
| 268 | |
| 269 | def machine(self): |
| 270 | """ |
| 271 | Caching function for kernels |
| 272 | """ |
| 273 | if not self.__machine: |
| 274 | where = {'machine_idx' : self.machine_idx} |
| 275 | self.__machine = machine.select(self.db, where)[0] |
| 276 | return self.__machine |
mbligh | 2aaeb67 | 2007-10-01 14:54:18 +0000 | [diff] [blame] | 277 | |
| 278 | |
mbligh | 250300e | 2007-09-18 00:50:57 +0000 | [diff] [blame] | 279 | class job: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 280 | def __init__(self, db, job_idx): |
| 281 | where = {'job_idx' : job_idx} |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 282 | rows = db.select('tag, machine_idx', 'tko_jobs', where) |
mbligh | b33e53e | 2008-06-17 19:41:26 +0000 | [diff] [blame] | 283 | if rows: |
| 284 | self.tag, self.machine_idx = rows[0] |
| 285 | self.job_idx = job_idx |
mbligh | 250300e | 2007-09-18 00:50:57 +0000 | [diff] [blame] | 286 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 287 | |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 288 | class iteration: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 289 | @classmethod |
| 290 | def select(klass, db, where): |
| 291 | fields = ['iteration', 'attribute', 'value'] |
| 292 | iterations = [] |
showard | eab66ce | 2009-12-23 00:03:56 +0000 | [diff] [blame] | 293 | rows = db.select(','.join(fields), 'tko_iteration_result', where) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 294 | for row in rows: |
| 295 | iterations.append(klass(*row)) |
| 296 | return iterations |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 297 | |
| 298 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 299 | def __init__(self, iteration, key, value): |
| 300 | self.iteration = iteration |
| 301 | self.key = key |
| 302 | self.value = value |
mbligh | 16ae926 | 2007-09-21 00:53:08 +0000 | [diff] [blame] | 303 | |
mbligh | 8e1ab17 | 2007-09-13 17:29:56 +0000 | [diff] [blame] | 304 | # class patch: |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 305 | # def __init__(self): |
| 306 | # self.spec = None |