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