blob: 4c611f2a69c45a6d88a74039cffc8b84c9af2df8 [file] [log] [blame]
Jakob Juelich934f0dc2014-10-14 18:21:13 -07001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Fang Dengcdee2512014-12-09 15:11:25 -08005import re, os, sys, time, random
mbligh96cf0512008-04-17 15:25:38 +00006
7import common
8from autotest_lib.client.common_lib import global_config
Dan Shie8e0c052015-09-01 00:27:27 -07009from autotest_lib.client.common_lib.cros.graphite import autotest_stats
Jakob Juelich934f0dc2014-10-14 18:21:13 -070010from autotest_lib.frontend import database_settings_helper
Dan Shi70647ca2015-07-16 22:52:35 -070011from autotest_lib.server import site_utils
jamesrena12b8a02010-06-16 23:28:23 +000012from autotest_lib.tko import utils
mbligh96cf0512008-04-17 15:25:38 +000013
mblighed4d6dd2008-02-27 16:49:43 +000014
mblighaea09602008-04-16 22:59:37 +000015class MySQLTooManyRows(Exception):
jadmanski0afbb632008-06-06 21:10:57 +000016 pass
mblighaea09602008-04-16 22:59:37 +000017
mblighd5c33db2006-10-08 21:34:16 +000018
mbligh7636b3a2008-06-11 15:44:01 +000019class db_sql(object):
jadmanski0afbb632008-06-06 21:10:57 +000020 def __init__(self, debug=False, autocommit=True, host=None,
21 database=None, user=None, password=None):
22 self.debug = debug
23 self.autocommit = autocommit
24 self._load_config(host, database, user, password)
mbligh96cf0512008-04-17 15:25:38 +000025
jadmanski0afbb632008-06-06 21:10:57 +000026 self.con = None
27 self._init_db()
mblighd5c33db2006-10-08 21:34:16 +000028
jadmanski0afbb632008-06-06 21:10:57 +000029 # if not present, insert statuses
30 self.status_idx = {}
31 self.status_word = {}
showardeab66ce2009-12-23 00:03:56 +000032 status_rows = self.select('status_idx, word', 'tko_status', None)
jadmanski0afbb632008-06-06 21:10:57 +000033 for s in status_rows:
34 self.status_idx[s[1]] = s[0]
35 self.status_word[s[0]] = s[1]
mbligh048e1c92007-10-07 00:10:33 +000036
jadmanski0afbb632008-06-06 21:10:57 +000037 machine_map = os.path.join(os.path.dirname(__file__),
38 'machines')
39 if os.path.exists(machine_map):
40 self.machine_map = machine_map
41 else:
42 self.machine_map = None
43 self.machine_group = {}
mbligh048e1c92007-10-07 00:10:33 +000044
mbligh8e1ab172007-09-13 17:29:56 +000045
jadmanski0afbb632008-06-06 21:10:57 +000046 def _load_config(self, host, database, user, password):
Jakob Juelich934f0dc2014-10-14 18:21:13 -070047 """Loads configuration settings required to connect to the database.
48
49 This will try to connect to use the settings prefixed with global_db_.
50 If they do not exist, they un-prefixed settings will be used.
51
52 If parameters are supplied, these will be taken instead of the values
53 in global_config.
54
55 @param host: If set, this host will be used, if not, the host will be
56 retrieved from global_config.
57 @param database: If set, this database will be used, if not, the
58 database will be retrieved from global_config.
59 @param user: If set, this user will be used, if not, the
60 user will be retrieved from global_config.
61 @param password: If set, this password will be used, if not, the
62 password will be retrieved from global_config.
63 """
64 database_settings = database_settings_helper.get_global_db_config()
mbligh65acae52008-04-24 20:21:55 +000065
jadmanski0afbb632008-06-06 21:10:57 +000066 # grab the host, database
Jakob Juelich934f0dc2014-10-14 18:21:13 -070067 self.host = host or database_settings['HOST']
68 self.database = database or database_settings['NAME']
mbligh65acae52008-04-24 20:21:55 +000069
jadmanski0afbb632008-06-06 21:10:57 +000070 # grab the user and password
Jakob Juelich934f0dc2014-10-14 18:21:13 -070071 self.user = user or database_settings['USER']
72 self.password = password or database_settings['PASSWORD']
mbligh65acae52008-04-24 20:21:55 +000073
Michael Spang7a273472014-10-08 12:08:13 -040074 # grab the timeout configuration
Jakob Juelich934f0dc2014-10-14 18:21:13 -070075 self.query_timeout =(
76 database_settings.get('OPTIONS', {}).get('timeout', 3600))
77
78 # Using fallback to non-global in order to work without configuration
79 # overhead on non-shard instances.
80 get_value = global_config.global_config.get_config_value_with_fallback
Jakob Juelich475b82b2014-09-30 11:17:07 -070081 self.min_delay = get_value("AUTOTEST_WEB", "global_db_min_retry_delay",
Jakob Juelich934f0dc2014-10-14 18:21:13 -070082 "min_retry_delay", type=int, default=20)
Jakob Juelich475b82b2014-09-30 11:17:07 -070083 self.max_delay = get_value("AUTOTEST_WEB", "global_db_max_retry_delay",
Jakob Juelich934f0dc2014-10-14 18:21:13 -070084 "max_retry_delay", type=int, default=60)
mbligh65acae52008-04-24 20:21:55 +000085
Richard Barnette2468fbd2014-11-07 01:12:46 +000086 # TODO(beeps): Move this to django settings once we have routers.
87 # On test instances mysql connects through a different port. No point
88 # piping this through our entire infrastructure when it is only really
89 # used for testing; Ideally we would specify this through django
90 # settings and default it to the empty string so django will figure out
91 # the default based on the database backend (eg: mysql, 3306), but until
92 # we have database routers in place any django settings will apply to
93 # both tko and afe.
94 # The intended use of this port is to allow a testing shard vm to
95 # update the master vm's database with test results. Specifying
96 # and empty string will fallback to not even specifying the port
97 # to the backend in tko/db.py. Unfortunately this means retries
98 # won't work on the test cluster till we've migrated to routers.
99 self.port = global_config.global_config.get_config_value(
100 "AUTOTEST_WEB", "global_db_port", type=str, default='')
101
mbligh65acae52008-04-24 20:21:55 +0000102
jadmanski0afbb632008-06-06 21:10:57 +0000103 def _init_db(self):
104 # make sure we clean up any existing connection
105 if self.con:
106 self.con.close()
107 self.con = None
mbligh65acae52008-04-24 20:21:55 +0000108
jadmanski0afbb632008-06-06 21:10:57 +0000109 # create the db connection and cursor
110 self.con = self.connect(self.host, self.database,
Richard Barnette2468fbd2014-11-07 01:12:46 +0000111 self.user, self.password, self.port)
jadmanski0afbb632008-06-06 21:10:57 +0000112 self.cur = self.con.cursor()
mbligh96cf0512008-04-17 15:25:38 +0000113
114
jadmanski0afbb632008-06-06 21:10:57 +0000115 def _random_delay(self):
116 delay = random.randint(self.min_delay, self.max_delay)
117 time.sleep(delay)
mbligh65acae52008-04-24 20:21:55 +0000118
119
jadmanski0afbb632008-06-06 21:10:57 +0000120 def run_with_retry(self, function, *args, **dargs):
121 """Call function(*args, **dargs) until either it passes
122 without an operational error, or a timeout is reached.
123 This will re-connect to the database, so it is NOT safe
124 to use this inside of a database transaction.
jadmanskie7a69092008-05-29 21:03:13 +0000125
jadmanski0afbb632008-06-06 21:10:57 +0000126 It can be safely used with transactions, but the
127 transaction start & end must be completely contained
128 within the call to 'function'."""
129 OperationalError = _get_error_class("OperationalError")
mbligh65acae52008-04-24 20:21:55 +0000130
jadmanski0afbb632008-06-06 21:10:57 +0000131 success = False
132 start_time = time.time()
133 while not success:
134 try:
135 result = function(*args, **dargs)
136 except OperationalError, e:
137 self._log_operational_error(e)
138 stop_time = time.time()
139 elapsed_time = stop_time - start_time
140 if elapsed_time > self.query_timeout:
141 raise
142 else:
143 try:
144 self._random_delay()
145 self._init_db()
Dan Shie8e0c052015-09-01 00:27:27 -0700146 autotest_stats.Counter('tko_db_error').increment()
jadmanski0afbb632008-06-06 21:10:57 +0000147 except OperationalError, e:
148 self._log_operational_error(e)
149 else:
150 success = True
151 return result
mbligh96cf0512008-04-17 15:25:38 +0000152
153
jadmanski0afbb632008-06-06 21:10:57 +0000154 def _log_operational_error(self, e):
mbligh097407d2009-02-17 15:49:37 +0000155 msg = ("%s: An operational error occured during a database "
jadmanski5d4c27e2009-03-02 16:45:42 +0000156 "operation: %s" % (time.strftime("%X %x"), str(e)))
jadmanski0afbb632008-06-06 21:10:57 +0000157 print >> sys.stderr, msg
158 sys.stderr.flush() # we want these msgs to show up immediately
jadmanski60d4fa62008-05-06 22:49:41 +0000159
160
jadmanski0afbb632008-06-06 21:10:57 +0000161 def dprint(self, value):
162 if self.debug:
163 sys.stdout.write('SQL: ' + str(value) + '\n')
mbligh8e1ab172007-09-13 17:29:56 +0000164
mblighd5c33db2006-10-08 21:34:16 +0000165
Dan Shie8e0c052015-09-01 00:27:27 -0700166 def _commit(self):
167 """Private method for function commit to call for retry.
168 """
169 return self.con.commit()
170
171
jadmanski0afbb632008-06-06 21:10:57 +0000172 def commit(self):
Dan Shie8e0c052015-09-01 00:27:27 -0700173 if self.autocommit:
174 return self.run_with_retry(self._commit)
175 else:
176 return self._commit()
mbligh432bad42007-10-09 19:56:07 +0000177
178
Simran Basie129a962012-08-31 13:03:53 -0700179 def rollback(self):
180 self.con.rollback()
181
182
jadmanski0afbb632008-06-06 21:10:57 +0000183 def get_last_autonumber_value(self):
184 self.cur.execute('SELECT LAST_INSERT_ID()', [])
185 return self.cur.fetchall()[0][0]
mblighe12b8612008-02-12 20:58:14 +0000186
187
showardc1a98d12010-01-15 00:22:22 +0000188 def _quote(self, field):
189 return '`%s`' % field
190
191
192 def _where_clause(self, where):
193 if not where:
194 return '', []
195
196 if isinstance(where, dict):
197 # key/value pairs (which should be equal, or None for null)
198 keys, values = [], []
199 for field, value in where.iteritems():
200 quoted_field = self._quote(field)
201 if value is None:
202 keys.append(quoted_field + ' is null')
203 else:
204 keys.append(quoted_field + '=%s')
205 values.append(value)
206 where_clause = ' and '.join(keys)
207 elif isinstance(where, basestring):
208 # the exact string
209 where_clause = where
210 values = []
211 elif isinstance(where, tuple):
212 # preformatted where clause + values
213 where_clause, values = where
214 assert where_clause
215 else:
216 raise ValueError('Invalid "where" value: %r' % where)
217
218 return ' WHERE ' + where_clause, values
219
220
221
222 def select(self, fields, table, where, distinct=False, group_by=None,
223 max_rows=None):
jadmanski0afbb632008-06-06 21:10:57 +0000224 """\
225 This selects all the fields requested from a
226 specific table with a particular where clause.
227 The where clause can either be a dictionary of
228 field=value pairs, a string, or a tuple of (string,
229 a list of values). The last option is what you
230 should use when accepting user input as it'll
231 protect you against sql injection attacks (if
232 all user data is placed in the array rather than
233 the raw SQL).
mbligh12eebfa2008-01-03 02:01:53 +0000234
jadmanski0afbb632008-06-06 21:10:57 +0000235 For example:
236 where = ("a = %s AND b = %s", ['val', 'val'])
237 is better than
238 where = "a = 'val' AND b = 'val'"
239 """
240 cmd = ['select']
241 if distinct:
242 cmd.append('distinct')
243 cmd += [fields, 'from', table]
mbligh608c3252007-08-31 13:53:00 +0000244
showardc1a98d12010-01-15 00:22:22 +0000245 where_clause, values = self._where_clause(where)
246 cmd.append(where_clause)
mbligh96cf0512008-04-17 15:25:38 +0000247
jadmanski0afbb632008-06-06 21:10:57 +0000248 if group_by:
249 cmd.append(' GROUP BY ' + group_by)
mbligh83f63a02007-12-12 19:13:04 +0000250
jadmanski0afbb632008-06-06 21:10:57 +0000251 self.dprint('%s %s' % (' '.join(cmd), values))
mbligh96cf0512008-04-17 15:25:38 +0000252
jadmanski0afbb632008-06-06 21:10:57 +0000253 # create a re-runable function for executing the query
254 def exec_sql():
255 sql = ' '.join(cmd)
256 numRec = self.cur.execute(sql, values)
mblighd876f452008-12-03 15:09:17 +0000257 if max_rows is not None and numRec > max_rows:
jadmanski0afbb632008-06-06 21:10:57 +0000258 msg = 'Exceeded allowed number of records'
259 raise MySQLTooManyRows(msg)
260 return self.cur.fetchall()
mbligh96cf0512008-04-17 15:25:38 +0000261
jadmanski0afbb632008-06-06 21:10:57 +0000262 # run the query, re-trying after operational errors
263 if self.autocommit:
264 return self.run_with_retry(exec_sql)
265 else:
266 return exec_sql()
mblighd5c33db2006-10-08 21:34:16 +0000267
mbligh056d0d32006-10-08 22:31:10 +0000268
jadmanski0afbb632008-06-06 21:10:57 +0000269 def select_sql(self, fields, table, sql, values):
270 """\
271 select fields from table "sql"
272 """
273 cmd = 'select %s from %s %s' % (fields, table, sql)
274 self.dprint(cmd)
mbligh414c69e2007-10-05 15:13:06 +0000275
jadmanski0afbb632008-06-06 21:10:57 +0000276 # create a -re-runable function for executing the query
277 def exec_sql():
278 self.cur.execute(cmd, values)
279 return self.cur.fetchall()
mbligh96b9a5a2007-11-24 19:32:20 +0000280
jadmanski0afbb632008-06-06 21:10:57 +0000281 # run the query, re-trying after operational errors
282 if self.autocommit:
283 return self.run_with_retry(exec_sql)
284 else:
285 return exec_sql()
mbligh96b9a5a2007-11-24 19:32:20 +0000286
mbligh608c3252007-08-31 13:53:00 +0000287
jadmanski0afbb632008-06-06 21:10:57 +0000288 def _exec_sql_with_commit(self, sql, values, commit):
289 if self.autocommit:
290 # re-run the query until it succeeds
291 def exec_sql():
292 self.cur.execute(sql, values)
293 self.con.commit()
294 self.run_with_retry(exec_sql)
295 else:
296 # take one shot at running the query
297 self.cur.execute(sql, values)
298 if commit:
299 self.con.commit()
mbligh96b9a5a2007-11-24 19:32:20 +0000300
mbligh2bd48872007-09-20 18:32:25 +0000301
jadmanskib591fba2008-09-10 16:19:22 +0000302 def insert(self, table, data, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000303 """\
304 'insert into table (keys) values (%s ... %s)', values
mbligh96cf0512008-04-17 15:25:38 +0000305
jadmanski0afbb632008-06-06 21:10:57 +0000306 data:
307 dictionary of fields and data
308 """
309 fields = data.keys()
310 refs = ['%s' for field in fields]
311 values = [data[field] for field in fields]
showardc1a98d12010-01-15 00:22:22 +0000312 cmd = ('insert into %s (%s) values (%s)' %
313 (table, ','.join(self._quote(field) for field in fields),
314 ','.join(refs)))
jadmanski0afbb632008-06-06 21:10:57 +0000315 self.dprint('%s %s' % (cmd, values))
mblighe9cf9d42007-08-31 08:56:00 +0000316
jadmanski0afbb632008-06-06 21:10:57 +0000317 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000318
mbligh048e1c92007-10-07 00:10:33 +0000319
jadmanski0afbb632008-06-06 21:10:57 +0000320 def delete(self, table, where, commit = None):
321 cmd = ['delete from', table]
mblighd876f452008-12-03 15:09:17 +0000322 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000323 commit = self.autocommit
showardc1a98d12010-01-15 00:22:22 +0000324 where_clause, values = self._where_clause(where)
325 cmd.append(where_clause)
jadmanski0afbb632008-06-06 21:10:57 +0000326 sql = ' '.join(cmd)
327 self.dprint('%s %s' % (sql, values))
mbligh048e1c92007-10-07 00:10:33 +0000328
jadmanski0afbb632008-06-06 21:10:57 +0000329 self._exec_sql_with_commit(sql, values, commit)
mbligh048e1c92007-10-07 00:10:33 +0000330
mbligh7a41a862007-11-30 17:44:24 +0000331
jadmanski0afbb632008-06-06 21:10:57 +0000332 def update(self, table, data, where, commit = None):
333 """\
334 'update table set data values (%s ... %s) where ...'
mbligh2aaeb672007-10-01 14:54:18 +0000335
jadmanski0afbb632008-06-06 21:10:57 +0000336 data:
337 dictionary of fields and data
338 """
mblighd876f452008-12-03 15:09:17 +0000339 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000340 commit = self.autocommit
341 cmd = 'update %s ' % table
342 fields = data.keys()
showardc1a98d12010-01-15 00:22:22 +0000343 data_refs = [self._quote(field) + '=%s' for field in fields]
jadmanski0afbb632008-06-06 21:10:57 +0000344 data_values = [data[field] for field in fields]
jadmanski74eebf32008-07-15 20:04:42 +0000345 cmd += ' set ' + ', '.join(data_refs)
mbligh2aaeb672007-10-01 14:54:18 +0000346
showardc1a98d12010-01-15 00:22:22 +0000347 where_clause, where_values = self._where_clause(where)
348 cmd += where_clause
mbligh2aaeb672007-10-01 14:54:18 +0000349
jadmanski0afbb632008-06-06 21:10:57 +0000350 values = data_values + where_values
jadmanski74eebf32008-07-15 20:04:42 +0000351 self.dprint('%s %s' % (cmd, values))
mbligh2aaeb672007-10-01 14:54:18 +0000352
jadmanski0afbb632008-06-06 21:10:57 +0000353 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000354
355
jadmanski0afbb632008-06-06 21:10:57 +0000356 def delete_job(self, tag, commit = None):
357 job_idx = self.find_job(tag)
358 for test_idx in self.find_tests(job_idx):
359 where = {'test_idx' : test_idx}
showardeab66ce2009-12-23 00:03:56 +0000360 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700361 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000362 self.delete('tko_iteration_attributes', where)
363 self.delete('tko_test_attributes', where)
364 self.delete('tko_test_labels_tests', {'test_id': test_idx})
jadmanski0afbb632008-06-06 21:10:57 +0000365 where = {'job_idx' : job_idx}
showardeab66ce2009-12-23 00:03:56 +0000366 self.delete('tko_tests', where)
367 self.delete('tko_jobs', where)
apw7a7316b2008-02-21 17:42:05 +0000368
apw7a7316b2008-02-21 17:42:05 +0000369
Dan Shi70647ca2015-07-16 22:52:35 -0700370 def insert_job(self, tag, job, parent_job_id=None, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000371 job.machine_idx = self.lookup_machine(job.machine)
372 if not job.machine_idx:
showard71b94312009-08-20 23:40:02 +0000373 job.machine_idx = self.insert_machine(job, commit=commit)
374 else:
375 self.update_machine_information(job, commit=commit)
376
jamesrena12b8a02010-06-16 23:28:23 +0000377 afe_job_id = utils.get_afe_job_id(tag)
showardc1c1caf2009-09-08 16:26:50 +0000378
showard0fec8a02009-12-04 01:19:54 +0000379 data = {'tag':tag,
380 'label': job.label,
381 'username': job.user,
382 'machine_idx': job.machine_idx,
383 'queued_time': job.queued_time,
384 'started_time': job.started_time,
385 'finished_time': job.finished_time,
Dan Shi70647ca2015-07-16 22:52:35 -0700386 'afe_job_id': afe_job_id,
387 'afe_parent_job_id': parent_job_id}
388 if job.label:
389 label_info = site_utils.parse_job_name(job.label)
390 if label_info:
391 data['build'] = label_info.get('build', None)
392 data['build_version'] = label_info.get('build_version', None)
393 data['board'] = label_info.get('board', None)
394 data['suite'] = label_info.get('suite', None)
showard0fec8a02009-12-04 01:19:54 +0000395 is_update = hasattr(job, 'index')
396 if is_update:
showardeab66ce2009-12-23 00:03:56 +0000397 self.update('tko_jobs', data, {'job_idx': job.index}, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000398 else:
showardeab66ce2009-12-23 00:03:56 +0000399 self.insert('tko_jobs', data, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000400 job.index = self.get_last_autonumber_value()
showardc1a98d12010-01-15 00:22:22 +0000401 self.update_job_keyvals(job, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000402 for test in job.tests:
403 self.insert_test(job, test, commit=commit)
apw7a7316b2008-02-21 17:42:05 +0000404
mbligh237bed32007-09-05 13:05:57 +0000405
showardc1a98d12010-01-15 00:22:22 +0000406 def update_job_keyvals(self, job, commit=None):
407 for key, value in job.keyval_dict.iteritems():
408 where = {'job_id': job.index, 'key': key}
409 data = dict(where, value=value)
410 exists = self.select('id', 'tko_job_keyvals', where=where)
411
412 if exists:
413 self.update('tko_job_keyvals', data, where=where, commit=commit)
414 else:
415 self.insert('tko_job_keyvals', data, commit=commit)
416
417
jadmanski0afbb632008-06-06 21:10:57 +0000418 def insert_test(self, job, test, commit = None):
419 kver = self.insert_kernel(test.kernel, commit=commit)
420 data = {'job_idx':job.index, 'test':test.testname,
421 'subdir':test.subdir, 'kernel_idx':kver,
422 'status':self.status_idx[test.status],
423 'reason':test.reason, 'machine_idx':job.machine_idx,
424 'started_time': test.started_time,
425 'finished_time':test.finished_time}
jadmanski9b6babf2009-04-21 17:57:40 +0000426 is_update = hasattr(test, "test_idx")
427 if is_update:
jadmanski74eebf32008-07-15 20:04:42 +0000428 test_idx = test.test_idx
showardeab66ce2009-12-23 00:03:56 +0000429 self.update('tko_tests', data,
430 {'test_idx': test_idx}, commit=commit)
jadmanskib591fba2008-09-10 16:19:22 +0000431 where = {'test_idx': test_idx}
showardeab66ce2009-12-23 00:03:56 +0000432 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700433 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000434 self.delete('tko_iteration_attributes', where)
showard0fec8a02009-12-04 01:19:54 +0000435 where['user_created'] = 0
showardeab66ce2009-12-23 00:03:56 +0000436 self.delete('tko_test_attributes', where)
jadmanski74eebf32008-07-15 20:04:42 +0000437 else:
showardeab66ce2009-12-23 00:03:56 +0000438 self.insert('tko_tests', data, commit=commit)
jadmanski74eebf32008-07-15 20:04:42 +0000439 test_idx = test.test_idx = self.get_last_autonumber_value()
440 data = {'test_idx': test_idx}
mbligh237bed32007-09-05 13:05:57 +0000441
jadmanski0afbb632008-06-06 21:10:57 +0000442 for i in test.iterations:
443 data['iteration'] = i.index
444 for key, value in i.attr_keyval.iteritems():
445 data['attribute'] = key
446 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000447 self.insert('tko_iteration_attributes', data,
jadmanski0afbb632008-06-06 21:10:57 +0000448 commit=commit)
449 for key, value in i.perf_keyval.iteritems():
450 data['attribute'] = key
451 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000452 self.insert('tko_iteration_result', data,
mbligh432bad42007-10-09 19:56:07 +0000453 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000454
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700455 data = {'test_idx': test_idx}
456 for i in test.perf_values:
457 data['iteration'] = i.index
458 for perf_dict in i.perf_measurements:
459 data['description'] = perf_dict['description']
460 data['value'] = perf_dict['value']
461 data['stddev'] = perf_dict['stddev']
462 data['units'] = perf_dict['units']
Fang Dengcdee2512014-12-09 15:11:25 -0800463 # TODO(fdeng): In db, higher_is_better doesn't allow null,
464 # This is a workaround to avoid altering the
465 # table (very expensive) while still allows test to send
466 # higher_is_better=None. Ideally, the table should be
467 # altered to allow this.
468 if perf_dict['higher_is_better'] is not None:
469 data['higher_is_better'] = perf_dict['higher_is_better']
Fang Deng7f24f0b2013-11-12 11:22:16 -0800470 data['graph'] = perf_dict['graph']
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700471 self.insert('tko_iteration_perf_value', data, commit=commit)
472
jadmanski0afbb632008-06-06 21:10:57 +0000473 for key, value in test.attributes.iteritems():
474 data = {'test_idx': test_idx, 'attribute': key,
475 'value': value}
showardeab66ce2009-12-23 00:03:56 +0000476 self.insert('tko_test_attributes', data, commit=commit)
mbligh2bd48872007-09-20 18:32:25 +0000477
jadmanski9b6babf2009-04-21 17:57:40 +0000478 if not is_update:
479 for label_index in test.labels:
480 data = {'test_id': test_idx, 'testlabel_id': label_index}
showardeab66ce2009-12-23 00:03:56 +0000481 self.insert('tko_test_labels_tests', data, commit=commit)
jadmanski9b6babf2009-04-21 17:57:40 +0000482
mbligh056d0d32006-10-08 22:31:10 +0000483
jadmanski0afbb632008-06-06 21:10:57 +0000484 def read_machine_map(self):
showard71b94312009-08-20 23:40:02 +0000485 if self.machine_group or not self.machine_map:
486 return
jadmanski0afbb632008-06-06 21:10:57 +0000487 for line in open(self.machine_map, 'r').readlines():
488 (machine, group) = line.split()
489 self.machine_group[machine] = group
mbligh96b9a5a2007-11-24 19:32:20 +0000490
491
showard71b94312009-08-20 23:40:02 +0000492 def machine_info_dict(self, job):
jadmanski0afbb632008-06-06 21:10:57 +0000493 hostname = job.machine
showard71b94312009-08-20 23:40:02 +0000494 group = job.machine_group
495 owner = job.machine_owner
jadmanski0afbb632008-06-06 21:10:57 +0000496
497 if not group:
showard71b94312009-08-20 23:40:02 +0000498 self.read_machine_map()
jadmanski0afbb632008-06-06 21:10:57 +0000499 group = self.machine_group.get(hostname, hostname)
showard71b94312009-08-20 23:40:02 +0000500 if group == hostname and owner:
501 group = owner + '/' + hostname
jadmanski0afbb632008-06-06 21:10:57 +0000502
showard71b94312009-08-20 23:40:02 +0000503 return {'hostname': hostname, 'machine_group': group, 'owner': owner}
504
505
506 def insert_machine(self, job, commit = None):
507 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000508 self.insert('tko_machines', machine_info, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000509 return self.get_last_autonumber_value()
510
511
showard71b94312009-08-20 23:40:02 +0000512 def update_machine_information(self, job, commit = None):
513 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000514 self.update('tko_machines', machine_info,
showard71b94312009-08-20 23:40:02 +0000515 where={'hostname': machine_info['hostname']},
516 commit=commit)
517
518
jadmanski0afbb632008-06-06 21:10:57 +0000519 def lookup_machine(self, hostname):
520 where = { 'hostname' : hostname }
showardeab66ce2009-12-23 00:03:56 +0000521 rows = self.select('machine_idx', 'tko_machines', where)
jadmanski0afbb632008-06-06 21:10:57 +0000522 if rows:
523 return rows[0][0]
524 else:
525 return None
526
527
528 def lookup_kernel(self, kernel):
showardeab66ce2009-12-23 00:03:56 +0000529 rows = self.select('kernel_idx', 'tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000530 {'kernel_hash':kernel.kernel_hash})
531 if rows:
532 return rows[0][0]
533 else:
534 return None
535
536
537 def insert_kernel(self, kernel, commit = None):
538 kver = self.lookup_kernel(kernel)
539 if kver:
540 return kver
541
542 # If this kernel has any significant patches, append their hash
543 # as diferentiator.
544 printable = kernel.base
545 patch_count = 0
546 for patch in kernel.patches:
547 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
548 patch.reference)
549 if not match:
550 patch_count += 1
551
showardeab66ce2009-12-23 00:03:56 +0000552 self.insert('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000553 {'base':kernel.base,
554 'kernel_hash':kernel.kernel_hash,
555 'printable':printable},
556 commit=commit)
557 kver = self.get_last_autonumber_value()
558
559 if patch_count > 0:
560 printable += ' p%d' % (kver)
showardeab66ce2009-12-23 00:03:56 +0000561 self.update('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000562 {'printable':printable},
563 {'kernel_idx':kver})
564
565 for patch in kernel.patches:
566 self.insert_patch(kver, patch, commit=commit)
567 return kver
568
569
570 def insert_patch(self, kver, patch, commit = None):
571 print patch.reference
572 name = os.path.basename(patch.reference)[:80]
showardeab66ce2009-12-23 00:03:56 +0000573 self.insert('tko_patches',
jadmanski0afbb632008-06-06 21:10:57 +0000574 {'kernel_idx': kver,
575 'name':name,
576 'url':patch.reference,
577 'hash':patch.hash},
578 commit=commit)
579
580
jadmanski74eebf32008-07-15 20:04:42 +0000581 def find_test(self, job_idx, testname, subdir):
582 where = {'job_idx': job_idx , 'test': testname, 'subdir': subdir}
showardeab66ce2009-12-23 00:03:56 +0000583 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000584 if rows:
585 return rows[0][0]
586 else:
587 return None
588
589
590 def find_tests(self, job_idx):
591 where = { 'job_idx':job_idx }
showardeab66ce2009-12-23 00:03:56 +0000592 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000593 if rows:
594 return [row[0] for row in rows]
595 else:
596 return []
597
598
599 def find_job(self, tag):
showardeab66ce2009-12-23 00:03:56 +0000600 rows = self.select('job_idx', 'tko_jobs', {'tag': tag})
jadmanski0afbb632008-06-06 21:10:57 +0000601 if rows:
602 return rows[0][0]
603 else:
604 return None
mblighaf25f062007-12-03 17:48:35 +0000605
606
mbligh96cf0512008-04-17 15:25:38 +0000607def _get_db_type():
jadmanski0afbb632008-06-06 21:10:57 +0000608 """Get the database type name to use from the global config."""
Jakob Juelich934f0dc2014-10-14 18:21:13 -0700609 get_value = global_config.global_config.get_config_value_with_fallback
610 return "db_" + get_value("AUTOTEST_WEB", "global_db_type", "db_type",
611 default="mysql")
mblighaf25f062007-12-03 17:48:35 +0000612
mbligh96cf0512008-04-17 15:25:38 +0000613
614def _get_error_class(class_name):
jadmanski0afbb632008-06-06 21:10:57 +0000615 """Retrieves the appropriate error class by name from the database
616 module."""
617 db_module = __import__("autotest_lib.tko." + _get_db_type(),
618 globals(), locals(), ["driver"])
619 return getattr(db_module.driver, class_name)
mbligh96cf0512008-04-17 15:25:38 +0000620
621
622def db(*args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000623 """Creates an instance of the database class with the arguments
624 provided in args and dargs, using the database type specified by
625 the global configuration (defaulting to mysql)."""
626 db_type = _get_db_type()
627 db_module = __import__("autotest_lib.tko." + db_type, globals(),
628 locals(), [db_type])
629 db = getattr(db_module, db_type)(*args, **dargs)
630 return db