blob: 14e7c1bcf48d73a1cea2e67b086d836b829823fc [file] [log] [blame]
mblighaf25f062007-12-03 17:48:35 +00001import re, os, sys, types
mblighed4d6dd2008-02-27 16:49:43 +00002from common import global_config
3
mblighaea09602008-04-16 22:59:37 +00004class MySQLTooManyRows(Exception):
5 pass
6
mblighd5c33db2006-10-08 21:34:16 +00007
mblighaf25f062007-12-03 17:48:35 +00008class db_sql:
mbligh2bb6dfd2008-01-10 16:38:30 +00009 def __init__(self, debug = False, autocommit=True, host = None,
10 database = None, user = None, password = None):
mbligh8e1ab172007-09-13 17:29:56 +000011 self.debug = debug
mbligh432bad42007-10-09 19:56:07 +000012 self.autocommit = autocommit
mblighed4d6dd2008-02-27 16:49:43 +000013
mbligh6f075f02008-01-25 16:36:16 +000014 path = os.path.dirname(__file__)
mblighed4d6dd2008-02-27 16:49:43 +000015
16 # grab the global config
17 c = global_config.global_config
18
19 # grab the host, database
20 if not host:
mbligh104e9ce2008-03-11 22:01:44 +000021 host = c.get_config_value("TKO", "host")
mblighed4d6dd2008-02-27 16:49:43 +000022 if not database:
mbligh104e9ce2008-03-11 22:01:44 +000023 database = c.get_config_value("TKO", "database")
mblighed4d6dd2008-02-27 16:49:43 +000024
25 # grab the user and password
26 if not user:
mbligh104e9ce2008-03-11 22:01:44 +000027 user = c.get_config_value("TKO", "user")
mblighed4d6dd2008-02-27 16:49:43 +000028 if not password:
mbligh104e9ce2008-03-11 22:01:44 +000029 password = c.get_config_value("TKO", "password")
mblighed4d6dd2008-02-27 16:49:43 +000030
mblighaf25f062007-12-03 17:48:35 +000031 self.con = self.connect(host, database, user, password)
mblighd5c33db2006-10-08 21:34:16 +000032 self.cur = self.con.cursor()
33
mbligh8e1ab172007-09-13 17:29:56 +000034 # if not present, insert statuses
35 self.status_idx = {}
36 self.status_word = {}
mblighc82f2462007-10-02 19:19:17 +000037 status_rows = self.select('status_idx, word', 'status', None)
38 for s in status_rows:
mbligh97894452007-10-05 15:10:33 +000039 self.status_idx[s[1]] = s[0]
mblighc82f2462007-10-02 19:19:17 +000040 self.status_word[s[0]] = s[1]
mbligh048e1c92007-10-07 00:10:33 +000041
mbligh6f075f02008-01-25 16:36:16 +000042 dir = os.path.dirname(__file__)
mbligh048e1c92007-10-07 00:10:33 +000043 machine_map = os.path.join(dir, 'machines')
44 if os.path.exists(machine_map):
45 self.machine_map = machine_map
mblighba9c54c2007-10-26 16:41:26 +000046 else:
47 self.machine_map = None
mbligh048e1c92007-10-07 00:10:33 +000048 self.machine_group = {}
49
mbligh8e1ab172007-09-13 17:29:56 +000050
mbligh8e1ab172007-09-13 17:29:56 +000051 def dprint(self, value):
52 if self.debug:
mbligh83f63a02007-12-12 19:13:04 +000053 sys.stdout.write('SQL: ' + str(value) + '\n')
mbligh8e1ab172007-09-13 17:29:56 +000054
mblighd5c33db2006-10-08 21:34:16 +000055
mbligh432bad42007-10-09 19:56:07 +000056 def commit(self):
57 self.con.commit()
58
59
mblighe12b8612008-02-12 20:58:14 +000060 def get_last_autonumber_value(self):
61 self.cur.execute('SELECT LAST_INSERT_ID()', [])
62 return self.cur.fetchall()[0][0]
63
64
mblighaea09602008-04-16 22:59:37 +000065 def select(self, fields, table, where, wherein={},
66 distinct = False, group_by = None, max_rows = None):
mbligh608c3252007-08-31 13:53:00 +000067 """\
mbligh12eebfa2008-01-03 02:01:53 +000068 This selects all the fields requested from a
69 specific table with a particular where clause.
70 The where clause can either be a dictionary of
71 field=value pairs, a string, or a tuple of (string,
72 a list of values). The last option is what you
73 should use when accepting user input as it'll
74 protect you against sql injection attacks (if
75 all user data is placed in the array rather than
76 the raw SQL).
77
78 For example:
79 where = ("a = %s AND b = %s", ['val', 'val'])
80 is better than
81 where = "a = 'val' AND b = 'val'"
mbligh608c3252007-08-31 13:53:00 +000082 """
mbligh31d29c42007-09-27 00:51:33 +000083 cmd = ['select']
84 if distinct:
85 cmd.append('distinct')
86 cmd += [fields, 'from', table]
mbligh608c3252007-08-31 13:53:00 +000087
mbligh31d29c42007-09-27 00:51:33 +000088 values = []
mbligh414c69e2007-10-05 15:13:06 +000089 if where and isinstance(where, types.DictionaryType):
mbligh12eebfa2008-01-03 02:01:53 +000090 # key/value pairs (which should be equal)
mbligh53d14252007-09-12 16:33:14 +000091 keys = [field + '=%s' for field in where.keys()]
92 values = [where[field] for field in where.keys()]
93
mbligh31d29c42007-09-27 00:51:33 +000094 cmd.append(' where ' + ' and '.join(keys))
mbligh414c69e2007-10-05 15:13:06 +000095 elif where and isinstance(where, types.StringTypes):
mbligh12eebfa2008-01-03 02:01:53 +000096 # the exact string
mbligh414c69e2007-10-05 15:13:06 +000097 cmd.append(' where ' + where)
mbligh12eebfa2008-01-03 02:01:53 +000098 elif where and isinstance(where, types.TupleType):
99 # preformatted where clause + values
100 (sql, vals) = where
101 values = vals
102 cmd.append(' where (%s) ' % sql)
mbligh53d14252007-09-12 16:33:14 +0000103
mbligh12eebfa2008-01-03 02:01:53 +0000104 # TODO: this assumes there's a where clause...bad
mbligh85952b42007-12-07 16:28:33 +0000105 if wherein and isinstance(wherein, types.DictionaryType):
106 keys_in = []
107 for field_in in wherein.keys():
108 keys_in += [field_in + ' in (' + ','.join(wherein[field_in])+') ']
109
110 cmd.append(' and '+' and '.join(keys_in))
mbligh83f63a02007-12-12 19:13:04 +0000111 if group_by:
112 cmd.append(' GROUP BY ' + group_by)
113
114 self.dprint('%s %s' % (' '.join(cmd), values))
mblighaea09602008-04-16 22:59:37 +0000115 numRec = self.cur.execute(' '.join(cmd), values)
116 if max_rows != None and numRec > max_rows:
117 msg = 'Exceeded allowed number of records'
118 raise MySQLTooManyRows(msg)
mblighd5c33db2006-10-08 21:34:16 +0000119 return self.cur.fetchall()
120
mbligh056d0d32006-10-08 22:31:10 +0000121
mbligh414c69e2007-10-05 15:13:06 +0000122 def select_sql(self, fields, table, sql, values):
123 """\
124 select fields from table "sql"
125 """
126 cmd = 'select %s from %s %s' % (fields, table, sql)
127 self.dprint(cmd)
128 self.cur.execute(cmd, values)
129 return self.cur.fetchall()
130
131
mbligh432bad42007-10-09 19:56:07 +0000132 def insert(self, table, data, commit = None):
mbligh608c3252007-08-31 13:53:00 +0000133 """\
134 'insert into table (keys) values (%s ... %s)', values
135
136 data:
137 dictionary of fields and data
138 """
mbligh432bad42007-10-09 19:56:07 +0000139 if commit == None:
140 commit = self.autocommit
mbligh608c3252007-08-31 13:53:00 +0000141 fields = data.keys()
142 refs = ['%s' for field in fields]
143 values = [data[field] for field in fields]
144 cmd = 'insert into %s (%s) values (%s)' % \
145 (table, ','.join(fields), ','.join(refs))
mbligh53d14252007-09-12 16:33:14 +0000146
mbligh8e1ab172007-09-13 17:29:56 +0000147 self.dprint('%s %s' % (cmd,values))
mbligh608c3252007-08-31 13:53:00 +0000148 self.cur.execute(cmd, values)
mbligh432bad42007-10-09 19:56:07 +0000149 if commit:
150 self.con.commit()
mbligh608c3252007-08-31 13:53:00 +0000151
152
mbligh96b9a5a2007-11-24 19:32:20 +0000153 def delete(self, table, where, commit = None):
154 cmd = ['delete from', table]
155 if commit == None:
156 commit = self.autocommit
157 if where and isinstance(where, types.DictionaryType):
158 keys = [field + '=%s' for field in where.keys()]
159 values = [where[field] for field in where.keys()]
160 cmd += ['where', ' and '.join(keys)]
161 self.dprint('%s %s' % (' '.join(cmd),values))
162 self.cur.execute(' '.join(cmd), values)
163 if commit:
164 self.con.commit()
165
166
mbligh432bad42007-10-09 19:56:07 +0000167 def update(self, table, data, where, commit = None):
mbligh414c69e2007-10-05 15:13:06 +0000168 """\
169 'update table set data values (%s ... %s) where ...'
170
171 data:
172 dictionary of fields and data
173 """
mbligh432bad42007-10-09 19:56:07 +0000174 if commit == None:
175 commit = self.autocommit
mbligh414c69e2007-10-05 15:13:06 +0000176 cmd = 'update %s ' % table
177 fields = data.keys()
178 data_refs = [field + '=%s' for field in fields]
179 data_values = [data[field] for field in fields]
180 cmd += ' set ' + ' and '.join(data_refs)
181
182 where_keys = [field + '=%s' for field in where.keys()]
183 where_values = [where[field] for field in where.keys()]
184 cmd += ' where ' + ' and '.join(where_keys)
185
186 print '%s %s' % (cmd, data_values + where_values)
187 self.cur.execute(cmd, data_values + where_values)
mbligh432bad42007-10-09 19:56:07 +0000188 if commit:
189 self.con.commit()
mbligh414c69e2007-10-05 15:13:06 +0000190
191
mbligh96b9a5a2007-11-24 19:32:20 +0000192 def delete_job(self, tag, commit = None):
193 job_idx = self.find_job(tag)
194 for test_idx in self.find_tests(job_idx):
195 where = {'test_idx' : test_idx}
196 self.delete('iteration_result', where)
197 self.delete('test_attributes', where)
198 where = {'job_idx' : job_idx}
199 self.delete('tests', where)
200 self.delete('jobs', where)
201
202
mbligh432bad42007-10-09 19:56:07 +0000203 def insert_job(self, tag, job, commit = None):
mbligh2aaeb672007-10-01 14:54:18 +0000204 job.machine_idx = self.lookup_machine(job.machine)
205 if not job.machine_idx:
mbligh7a41a862007-11-30 17:44:24 +0000206 job.machine_idx = self.insert_machine(job,
mbligh432bad42007-10-09 19:56:07 +0000207 commit=commit)
mblighb10a60f2007-10-17 00:03:32 +0000208 self.insert('jobs', {'tag':tag,
209 'label': job.label,
mbligh05067a32007-12-03 17:48:04 +0000210 'username': job.user,
mbligh26b992b2008-02-19 15:46:21 +0000211 'machine_idx': job.machine_idx,
212 'queued_time': job.queued_time,
213 'started_time': job.started_time,
214 'finished_time': job.finished_time},
mblighb10a60f2007-10-17 00:03:32 +0000215 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000216 job.index = self.get_last_autonumber_value()
mbligh608c3252007-08-31 13:53:00 +0000217 for test in job.tests:
mbligh432bad42007-10-09 19:56:07 +0000218 self.insert_test(job, test, commit=commit)
mbligh608c3252007-08-31 13:53:00 +0000219
mbligh96b9a5a2007-11-24 19:32:20 +0000220
mbligh432bad42007-10-09 19:56:07 +0000221 def insert_test(self, job, test, commit = None):
222 kver = self.insert_kernel(test.kernel, commit=commit)
mbligh608c3252007-08-31 13:53:00 +0000223 data = {'job_idx':job.index, 'test':test.testname,
mbligh2bd48872007-09-20 18:32:25 +0000224 'subdir':test.subdir, 'kernel_idx':kver,
mbligh8e1ab172007-09-13 17:29:56 +0000225 'status':self.status_idx[test.status],
mblighc2514542008-02-19 15:54:26 +0000226 'reason':test.reason, 'machine_idx':job.machine_idx,
227 'finished_time':test.finished_time}
mbligh432bad42007-10-09 19:56:07 +0000228 self.insert('tests', data, commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000229 test_idx = self.get_last_autonumber_value()
mbligh2bd48872007-09-20 18:32:25 +0000230 data = { 'test_idx':test_idx }
231
232 for i in test.iterations:
233 data['iteration'] = i.index
234 for key in i.keyval:
235 data['attribute'] = key
236 data['value'] = i.keyval[key]
mbligh432bad42007-10-09 19:56:07 +0000237 self.insert('iteration_result',
238 data,
239 commit=commit)
mbligh994a23d2007-10-25 15:28:58 +0000240 data = {'test_idx':test_idx, 'attribute':'version', 'value':test.version}
241 self.insert('test_attributes', data, commit=commit)
mblighe9cf9d42007-08-31 08:56:00 +0000242
243
mbligh048e1c92007-10-07 00:10:33 +0000244 def read_machine_map(self):
245 self.machine_group = {}
246 for line in open(self.machine_map, 'r').readlines():
247 (machine, group) = line.split()
248 self.machine_group[machine] = group
249
250
mbligh7a41a862007-11-30 17:44:24 +0000251 def insert_machine(self, job, group = None, commit = None):
252 hostname = job.machine
mbligh048e1c92007-10-07 00:10:33 +0000253 if self.machine_map and not self.machine_group:
254 self.read_machine_map()
255
256 if not group:
257 group = self.machine_group.get(hostname, hostname)
mbligh7a41a862007-11-30 17:44:24 +0000258 if group == hostname and job.machine_owner:
259 group = job.machine_owner + '/' + hostname
260
mbligh432bad42007-10-09 19:56:07 +0000261 self.insert('machines',
262 { 'hostname' : hostname ,
mbligh7a41a862007-11-30 17:44:24 +0000263 'machine_group' : group ,
264 'owner' : job.machine_owner },
mbligh432bad42007-10-09 19:56:07 +0000265 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000266 return self.get_last_autonumber_value()
mbligh2aaeb672007-10-01 14:54:18 +0000267
268
269 def lookup_machine(self, hostname):
270 where = { 'hostname' : hostname }
271 rows = self.select('machine_idx', 'machines', where)
272 if rows:
273 return rows[0][0]
274 else:
275 return None
276
277
mbligh9bb92fe2007-09-12 15:54:23 +0000278 def lookup_kernel(self, kernel):
279 rows = self.select('kernel_idx', 'kernels',
mbligh048e1c92007-10-07 00:10:33 +0000280 {'kernel_hash':kernel.kernel_hash})
mbligh237bed32007-09-05 13:05:57 +0000281 if rows:
282 return rows[0][0]
283 else:
284 return None
mblighe9cf9d42007-08-31 08:56:00 +0000285
286
mbligh432bad42007-10-09 19:56:07 +0000287 def insert_kernel(self, kernel, commit = None):
mbligh9bb92fe2007-09-12 15:54:23 +0000288 kver = self.lookup_kernel(kernel)
mbligh237bed32007-09-05 13:05:57 +0000289 if kver:
290 return kver
apw7a7316b2008-02-21 17:42:05 +0000291
292 # If this kernel has any significant patches, append their hash
293 # as diferentiator.
294 printable = kernel.base
295 patch_count = 0
296 for patch in kernel.patches:
297 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
298 patch.reference)
299 if not match:
300 patch_count += 1
301
mbligh432bad42007-10-09 19:56:07 +0000302 self.insert('kernels',
303 {'base':kernel.base,
304 'kernel_hash':kernel.kernel_hash,
apw7a7316b2008-02-21 17:42:05 +0000305 'printable':printable},
mbligh432bad42007-10-09 19:56:07 +0000306 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000307 kver = self.get_last_autonumber_value()
apw7a7316b2008-02-21 17:42:05 +0000308
309 if patch_count > 0:
310 printable += ' p%d' % (kver)
311 self.update('kernels',
312 {'printable':printable},
313 {'kernel_idx':kver})
314
mbligh237bed32007-09-05 13:05:57 +0000315 for patch in kernel.patches:
mbligh432bad42007-10-09 19:56:07 +0000316 self.insert_patch(kver, patch, commit=commit)
mbligh237bed32007-09-05 13:05:57 +0000317 return kver
318
319
mbligh432bad42007-10-09 19:56:07 +0000320 def insert_patch(self, kver, patch, commit = None):
mbligh237bed32007-09-05 13:05:57 +0000321 print patch.reference
322 name = os.path.basename(patch.reference)[:80]
mbligh432bad42007-10-09 19:56:07 +0000323 self.insert('patches',
324 {'kernel_idx': kver,
325 'name':name,
326 'url':patch.reference,
327 'hash':patch.hash},
328 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000329
mbligh048e1c92007-10-07 00:10:33 +0000330
mbligh2bd48872007-09-20 18:32:25 +0000331 def find_test(self, job_idx, subdir):
332 where = { 'job_idx':job_idx , 'subdir':subdir }
333 rows = self.select('test_idx', 'tests', where)
334 if rows:
335 return rows[0][0]
336 else:
337 return None
338
mbligh056d0d32006-10-08 22:31:10 +0000339
mbligh96b9a5a2007-11-24 19:32:20 +0000340 def find_tests(self, job_idx):
341 where = { 'job_idx':job_idx }
342 rows = self.select('test_idx', 'tests', where)
343 if rows:
344 return [row[0] for row in rows]
345 else:
mbligh2e57e7b2007-11-29 16:10:50 +0000346 return []
mbligh96b9a5a2007-11-24 19:32:20 +0000347
348
mbligh056d0d32006-10-08 22:31:10 +0000349 def find_job(self, tag):
mbligh608c3252007-08-31 13:53:00 +0000350 rows = self.select('job_idx', 'jobs', {'tag': tag})
351 if rows:
352 return rows[0][0]
353 else:
354 return None
mblighaf25f062007-12-03 17:48:35 +0000355
356
357# Use a class method as a class factory, generating a relevant database object.
358def db(*args, **dargs):
mbligh6f075f02008-01-25 16:36:16 +0000359 path = os.path.dirname(__file__)
mbligha044e562007-12-07 16:26:13 +0000360 db_type = None
mblighed4d6dd2008-02-27 16:49:43 +0000361
362 # read db_type from global config
363 c = global_config.global_config
mblighe82a93e2008-03-11 22:34:31 +0000364 db_type = c.get_config_value("TKO", "db_type", default="mysql")
mbligha044e562007-12-07 16:26:13 +0000365 db_type = 'db_' + db_type
mblighed4d6dd2008-02-27 16:49:43 +0000366 exec ('import %s; db = %s.%s(*args, **dargs)'
367 % (db_type, db_type, db_type))
mblighaf25f062007-12-03 17:48:35 +0000368
369 return db