blob: 5c064751a62613cdc181617bdc03df91b18c16e1 [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
Dan Shi8684b922015-10-06 13:29:18 -0700109 try:
110 # create the db connection and cursor
111 self.con = self.connect(self.host, self.database,
112 self.user, self.password, self.port)
113 except:
114 autotest_stats.Counter('tko_db_con_error').increment()
115 raise
jadmanski0afbb632008-06-06 21:10:57 +0000116 self.cur = self.con.cursor()
mbligh96cf0512008-04-17 15:25:38 +0000117
118
jadmanski0afbb632008-06-06 21:10:57 +0000119 def _random_delay(self):
120 delay = random.randint(self.min_delay, self.max_delay)
121 time.sleep(delay)
mbligh65acae52008-04-24 20:21:55 +0000122
123
jadmanski0afbb632008-06-06 21:10:57 +0000124 def run_with_retry(self, function, *args, **dargs):
125 """Call function(*args, **dargs) until either it passes
126 without an operational error, or a timeout is reached.
127 This will re-connect to the database, so it is NOT safe
128 to use this inside of a database transaction.
jadmanskie7a69092008-05-29 21:03:13 +0000129
jadmanski0afbb632008-06-06 21:10:57 +0000130 It can be safely used with transactions, but the
131 transaction start & end must be completely contained
132 within the call to 'function'."""
133 OperationalError = _get_error_class("OperationalError")
mbligh65acae52008-04-24 20:21:55 +0000134
jadmanski0afbb632008-06-06 21:10:57 +0000135 success = False
136 start_time = time.time()
137 while not success:
138 try:
139 result = function(*args, **dargs)
140 except OperationalError, e:
141 self._log_operational_error(e)
142 stop_time = time.time()
143 elapsed_time = stop_time - start_time
144 if elapsed_time > self.query_timeout:
145 raise
146 else:
147 try:
148 self._random_delay()
149 self._init_db()
Dan Shie8e0c052015-09-01 00:27:27 -0700150 autotest_stats.Counter('tko_db_error').increment()
jadmanski0afbb632008-06-06 21:10:57 +0000151 except OperationalError, e:
152 self._log_operational_error(e)
153 else:
154 success = True
155 return result
mbligh96cf0512008-04-17 15:25:38 +0000156
157
jadmanski0afbb632008-06-06 21:10:57 +0000158 def _log_operational_error(self, e):
mbligh097407d2009-02-17 15:49:37 +0000159 msg = ("%s: An operational error occured during a database "
jadmanski5d4c27e2009-03-02 16:45:42 +0000160 "operation: %s" % (time.strftime("%X %x"), str(e)))
jadmanski0afbb632008-06-06 21:10:57 +0000161 print >> sys.stderr, msg
162 sys.stderr.flush() # we want these msgs to show up immediately
jadmanski60d4fa62008-05-06 22:49:41 +0000163
164
jadmanski0afbb632008-06-06 21:10:57 +0000165 def dprint(self, value):
166 if self.debug:
167 sys.stdout.write('SQL: ' + str(value) + '\n')
mbligh8e1ab172007-09-13 17:29:56 +0000168
mblighd5c33db2006-10-08 21:34:16 +0000169
Dan Shie8e0c052015-09-01 00:27:27 -0700170 def _commit(self):
171 """Private method for function commit to call for retry.
172 """
173 return self.con.commit()
174
175
jadmanski0afbb632008-06-06 21:10:57 +0000176 def commit(self):
Dan Shie8e0c052015-09-01 00:27:27 -0700177 if self.autocommit:
178 return self.run_with_retry(self._commit)
179 else:
180 return self._commit()
mbligh432bad42007-10-09 19:56:07 +0000181
182
Simran Basie129a962012-08-31 13:03:53 -0700183 def rollback(self):
184 self.con.rollback()
185
186
jadmanski0afbb632008-06-06 21:10:57 +0000187 def get_last_autonumber_value(self):
188 self.cur.execute('SELECT LAST_INSERT_ID()', [])
189 return self.cur.fetchall()[0][0]
mblighe12b8612008-02-12 20:58:14 +0000190
191
showardc1a98d12010-01-15 00:22:22 +0000192 def _quote(self, field):
193 return '`%s`' % field
194
195
196 def _where_clause(self, where):
197 if not where:
198 return '', []
199
200 if isinstance(where, dict):
201 # key/value pairs (which should be equal, or None for null)
202 keys, values = [], []
203 for field, value in where.iteritems():
204 quoted_field = self._quote(field)
205 if value is None:
206 keys.append(quoted_field + ' is null')
207 else:
208 keys.append(quoted_field + '=%s')
209 values.append(value)
210 where_clause = ' and '.join(keys)
211 elif isinstance(where, basestring):
212 # the exact string
213 where_clause = where
214 values = []
215 elif isinstance(where, tuple):
216 # preformatted where clause + values
217 where_clause, values = where
218 assert where_clause
219 else:
220 raise ValueError('Invalid "where" value: %r' % where)
221
222 return ' WHERE ' + where_clause, values
223
224
225
226 def select(self, fields, table, where, distinct=False, group_by=None,
227 max_rows=None):
jadmanski0afbb632008-06-06 21:10:57 +0000228 """\
229 This selects all the fields requested from a
230 specific table with a particular where clause.
231 The where clause can either be a dictionary of
232 field=value pairs, a string, or a tuple of (string,
233 a list of values). The last option is what you
234 should use when accepting user input as it'll
235 protect you against sql injection attacks (if
236 all user data is placed in the array rather than
237 the raw SQL).
mbligh12eebfa2008-01-03 02:01:53 +0000238
jadmanski0afbb632008-06-06 21:10:57 +0000239 For example:
240 where = ("a = %s AND b = %s", ['val', 'val'])
241 is better than
242 where = "a = 'val' AND b = 'val'"
243 """
244 cmd = ['select']
245 if distinct:
246 cmd.append('distinct')
247 cmd += [fields, 'from', table]
mbligh608c3252007-08-31 13:53:00 +0000248
showardc1a98d12010-01-15 00:22:22 +0000249 where_clause, values = self._where_clause(where)
250 cmd.append(where_clause)
mbligh96cf0512008-04-17 15:25:38 +0000251
jadmanski0afbb632008-06-06 21:10:57 +0000252 if group_by:
253 cmd.append(' GROUP BY ' + group_by)
mbligh83f63a02007-12-12 19:13:04 +0000254
jadmanski0afbb632008-06-06 21:10:57 +0000255 self.dprint('%s %s' % (' '.join(cmd), values))
mbligh96cf0512008-04-17 15:25:38 +0000256
jadmanski0afbb632008-06-06 21:10:57 +0000257 # create a re-runable function for executing the query
258 def exec_sql():
259 sql = ' '.join(cmd)
260 numRec = self.cur.execute(sql, values)
mblighd876f452008-12-03 15:09:17 +0000261 if max_rows is not None and numRec > max_rows:
jadmanski0afbb632008-06-06 21:10:57 +0000262 msg = 'Exceeded allowed number of records'
263 raise MySQLTooManyRows(msg)
264 return self.cur.fetchall()
mbligh96cf0512008-04-17 15:25:38 +0000265
jadmanski0afbb632008-06-06 21:10:57 +0000266 # run the query, re-trying after operational errors
267 if self.autocommit:
268 return self.run_with_retry(exec_sql)
269 else:
270 return exec_sql()
mblighd5c33db2006-10-08 21:34:16 +0000271
mbligh056d0d32006-10-08 22:31:10 +0000272
jadmanski0afbb632008-06-06 21:10:57 +0000273 def select_sql(self, fields, table, sql, values):
274 """\
275 select fields from table "sql"
276 """
277 cmd = 'select %s from %s %s' % (fields, table, sql)
278 self.dprint(cmd)
mbligh414c69e2007-10-05 15:13:06 +0000279
jadmanski0afbb632008-06-06 21:10:57 +0000280 # create a -re-runable function for executing the query
281 def exec_sql():
282 self.cur.execute(cmd, values)
283 return self.cur.fetchall()
mbligh96b9a5a2007-11-24 19:32:20 +0000284
jadmanski0afbb632008-06-06 21:10:57 +0000285 # run the query, re-trying after operational errors
286 if self.autocommit:
287 return self.run_with_retry(exec_sql)
288 else:
289 return exec_sql()
mbligh96b9a5a2007-11-24 19:32:20 +0000290
mbligh608c3252007-08-31 13:53:00 +0000291
jadmanski0afbb632008-06-06 21:10:57 +0000292 def _exec_sql_with_commit(self, sql, values, commit):
293 if self.autocommit:
294 # re-run the query until it succeeds
295 def exec_sql():
296 self.cur.execute(sql, values)
297 self.con.commit()
298 self.run_with_retry(exec_sql)
299 else:
300 # take one shot at running the query
301 self.cur.execute(sql, values)
302 if commit:
303 self.con.commit()
mbligh96b9a5a2007-11-24 19:32:20 +0000304
mbligh2bd48872007-09-20 18:32:25 +0000305
jadmanskib591fba2008-09-10 16:19:22 +0000306 def insert(self, table, data, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000307 """\
308 'insert into table (keys) values (%s ... %s)', values
mbligh96cf0512008-04-17 15:25:38 +0000309
jadmanski0afbb632008-06-06 21:10:57 +0000310 data:
311 dictionary of fields and data
312 """
313 fields = data.keys()
314 refs = ['%s' for field in fields]
315 values = [data[field] for field in fields]
showardc1a98d12010-01-15 00:22:22 +0000316 cmd = ('insert into %s (%s) values (%s)' %
317 (table, ','.join(self._quote(field) for field in fields),
318 ','.join(refs)))
jadmanski0afbb632008-06-06 21:10:57 +0000319 self.dprint('%s %s' % (cmd, values))
mblighe9cf9d42007-08-31 08:56:00 +0000320
jadmanski0afbb632008-06-06 21:10:57 +0000321 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000322
mbligh048e1c92007-10-07 00:10:33 +0000323
jadmanski0afbb632008-06-06 21:10:57 +0000324 def delete(self, table, where, commit = None):
325 cmd = ['delete from', table]
mblighd876f452008-12-03 15:09:17 +0000326 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000327 commit = self.autocommit
showardc1a98d12010-01-15 00:22:22 +0000328 where_clause, values = self._where_clause(where)
329 cmd.append(where_clause)
jadmanski0afbb632008-06-06 21:10:57 +0000330 sql = ' '.join(cmd)
331 self.dprint('%s %s' % (sql, values))
mbligh048e1c92007-10-07 00:10:33 +0000332
jadmanski0afbb632008-06-06 21:10:57 +0000333 self._exec_sql_with_commit(sql, values, commit)
mbligh048e1c92007-10-07 00:10:33 +0000334
mbligh7a41a862007-11-30 17:44:24 +0000335
jadmanski0afbb632008-06-06 21:10:57 +0000336 def update(self, table, data, where, commit = None):
337 """\
338 'update table set data values (%s ... %s) where ...'
mbligh2aaeb672007-10-01 14:54:18 +0000339
jadmanski0afbb632008-06-06 21:10:57 +0000340 data:
341 dictionary of fields and data
342 """
mblighd876f452008-12-03 15:09:17 +0000343 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000344 commit = self.autocommit
345 cmd = 'update %s ' % table
346 fields = data.keys()
showardc1a98d12010-01-15 00:22:22 +0000347 data_refs = [self._quote(field) + '=%s' for field in fields]
jadmanski0afbb632008-06-06 21:10:57 +0000348 data_values = [data[field] for field in fields]
jadmanski74eebf32008-07-15 20:04:42 +0000349 cmd += ' set ' + ', '.join(data_refs)
mbligh2aaeb672007-10-01 14:54:18 +0000350
showardc1a98d12010-01-15 00:22:22 +0000351 where_clause, where_values = self._where_clause(where)
352 cmd += where_clause
mbligh2aaeb672007-10-01 14:54:18 +0000353
jadmanski0afbb632008-06-06 21:10:57 +0000354 values = data_values + where_values
jadmanski74eebf32008-07-15 20:04:42 +0000355 self.dprint('%s %s' % (cmd, values))
mbligh2aaeb672007-10-01 14:54:18 +0000356
jadmanski0afbb632008-06-06 21:10:57 +0000357 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000358
359
jadmanski0afbb632008-06-06 21:10:57 +0000360 def delete_job(self, tag, commit = None):
361 job_idx = self.find_job(tag)
362 for test_idx in self.find_tests(job_idx):
363 where = {'test_idx' : test_idx}
showardeab66ce2009-12-23 00:03:56 +0000364 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700365 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000366 self.delete('tko_iteration_attributes', where)
367 self.delete('tko_test_attributes', where)
368 self.delete('tko_test_labels_tests', {'test_id': test_idx})
jadmanski0afbb632008-06-06 21:10:57 +0000369 where = {'job_idx' : job_idx}
showardeab66ce2009-12-23 00:03:56 +0000370 self.delete('tko_tests', where)
371 self.delete('tko_jobs', where)
apw7a7316b2008-02-21 17:42:05 +0000372
apw7a7316b2008-02-21 17:42:05 +0000373
Dan Shi70647ca2015-07-16 22:52:35 -0700374 def insert_job(self, tag, job, parent_job_id=None, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000375 job.machine_idx = self.lookup_machine(job.machine)
376 if not job.machine_idx:
showard71b94312009-08-20 23:40:02 +0000377 job.machine_idx = self.insert_machine(job, commit=commit)
378 else:
379 self.update_machine_information(job, commit=commit)
380
jamesrena12b8a02010-06-16 23:28:23 +0000381 afe_job_id = utils.get_afe_job_id(tag)
showardc1c1caf2009-09-08 16:26:50 +0000382
showard0fec8a02009-12-04 01:19:54 +0000383 data = {'tag':tag,
384 'label': job.label,
385 'username': job.user,
386 'machine_idx': job.machine_idx,
387 'queued_time': job.queued_time,
388 'started_time': job.started_time,
389 'finished_time': job.finished_time,
Dan Shi70647ca2015-07-16 22:52:35 -0700390 'afe_job_id': afe_job_id,
391 'afe_parent_job_id': parent_job_id}
392 if job.label:
393 label_info = site_utils.parse_job_name(job.label)
394 if label_info:
395 data['build'] = label_info.get('build', None)
396 data['build_version'] = label_info.get('build_version', None)
397 data['board'] = label_info.get('board', None)
398 data['suite'] = label_info.get('suite', None)
showard0fec8a02009-12-04 01:19:54 +0000399 is_update = hasattr(job, 'index')
400 if is_update:
showardeab66ce2009-12-23 00:03:56 +0000401 self.update('tko_jobs', data, {'job_idx': job.index}, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000402 else:
showardeab66ce2009-12-23 00:03:56 +0000403 self.insert('tko_jobs', data, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000404 job.index = self.get_last_autonumber_value()
showardc1a98d12010-01-15 00:22:22 +0000405 self.update_job_keyvals(job, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000406 for test in job.tests:
407 self.insert_test(job, test, commit=commit)
apw7a7316b2008-02-21 17:42:05 +0000408
mbligh237bed32007-09-05 13:05:57 +0000409
showardc1a98d12010-01-15 00:22:22 +0000410 def update_job_keyvals(self, job, commit=None):
411 for key, value in job.keyval_dict.iteritems():
412 where = {'job_id': job.index, 'key': key}
413 data = dict(where, value=value)
414 exists = self.select('id', 'tko_job_keyvals', where=where)
415
416 if exists:
417 self.update('tko_job_keyvals', data, where=where, commit=commit)
418 else:
419 self.insert('tko_job_keyvals', data, commit=commit)
420
421
jadmanski0afbb632008-06-06 21:10:57 +0000422 def insert_test(self, job, test, commit = None):
423 kver = self.insert_kernel(test.kernel, commit=commit)
424 data = {'job_idx':job.index, 'test':test.testname,
425 'subdir':test.subdir, 'kernel_idx':kver,
426 'status':self.status_idx[test.status],
427 'reason':test.reason, 'machine_idx':job.machine_idx,
428 'started_time': test.started_time,
429 'finished_time':test.finished_time}
jadmanski9b6babf2009-04-21 17:57:40 +0000430 is_update = hasattr(test, "test_idx")
431 if is_update:
jadmanski74eebf32008-07-15 20:04:42 +0000432 test_idx = test.test_idx
showardeab66ce2009-12-23 00:03:56 +0000433 self.update('tko_tests', data,
434 {'test_idx': test_idx}, commit=commit)
jadmanskib591fba2008-09-10 16:19:22 +0000435 where = {'test_idx': test_idx}
showardeab66ce2009-12-23 00:03:56 +0000436 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700437 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000438 self.delete('tko_iteration_attributes', where)
showard0fec8a02009-12-04 01:19:54 +0000439 where['user_created'] = 0
showardeab66ce2009-12-23 00:03:56 +0000440 self.delete('tko_test_attributes', where)
jadmanski74eebf32008-07-15 20:04:42 +0000441 else:
showardeab66ce2009-12-23 00:03:56 +0000442 self.insert('tko_tests', data, commit=commit)
jadmanski74eebf32008-07-15 20:04:42 +0000443 test_idx = test.test_idx = self.get_last_autonumber_value()
444 data = {'test_idx': test_idx}
mbligh237bed32007-09-05 13:05:57 +0000445
jadmanski0afbb632008-06-06 21:10:57 +0000446 for i in test.iterations:
447 data['iteration'] = i.index
448 for key, value in i.attr_keyval.iteritems():
449 data['attribute'] = key
450 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000451 self.insert('tko_iteration_attributes', data,
jadmanski0afbb632008-06-06 21:10:57 +0000452 commit=commit)
453 for key, value in i.perf_keyval.iteritems():
454 data['attribute'] = key
455 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000456 self.insert('tko_iteration_result', data,
mbligh432bad42007-10-09 19:56:07 +0000457 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000458
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700459 data = {'test_idx': test_idx}
460 for i in test.perf_values:
461 data['iteration'] = i.index
462 for perf_dict in i.perf_measurements:
463 data['description'] = perf_dict['description']
464 data['value'] = perf_dict['value']
465 data['stddev'] = perf_dict['stddev']
466 data['units'] = perf_dict['units']
Fang Dengcdee2512014-12-09 15:11:25 -0800467 # TODO(fdeng): In db, higher_is_better doesn't allow null,
468 # This is a workaround to avoid altering the
469 # table (very expensive) while still allows test to send
470 # higher_is_better=None. Ideally, the table should be
471 # altered to allow this.
472 if perf_dict['higher_is_better'] is not None:
473 data['higher_is_better'] = perf_dict['higher_is_better']
Fang Deng7f24f0b2013-11-12 11:22:16 -0800474 data['graph'] = perf_dict['graph']
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700475 self.insert('tko_iteration_perf_value', data, commit=commit)
476
jadmanski0afbb632008-06-06 21:10:57 +0000477 for key, value in test.attributes.iteritems():
478 data = {'test_idx': test_idx, 'attribute': key,
479 'value': value}
showardeab66ce2009-12-23 00:03:56 +0000480 self.insert('tko_test_attributes', data, commit=commit)
mbligh2bd48872007-09-20 18:32:25 +0000481
jadmanski9b6babf2009-04-21 17:57:40 +0000482 if not is_update:
483 for label_index in test.labels:
484 data = {'test_id': test_idx, 'testlabel_id': label_index}
showardeab66ce2009-12-23 00:03:56 +0000485 self.insert('tko_test_labels_tests', data, commit=commit)
jadmanski9b6babf2009-04-21 17:57:40 +0000486
mbligh056d0d32006-10-08 22:31:10 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 def read_machine_map(self):
showard71b94312009-08-20 23:40:02 +0000489 if self.machine_group or not self.machine_map:
490 return
jadmanski0afbb632008-06-06 21:10:57 +0000491 for line in open(self.machine_map, 'r').readlines():
492 (machine, group) = line.split()
493 self.machine_group[machine] = group
mbligh96b9a5a2007-11-24 19:32:20 +0000494
495
showard71b94312009-08-20 23:40:02 +0000496 def machine_info_dict(self, job):
jadmanski0afbb632008-06-06 21:10:57 +0000497 hostname = job.machine
showard71b94312009-08-20 23:40:02 +0000498 group = job.machine_group
499 owner = job.machine_owner
jadmanski0afbb632008-06-06 21:10:57 +0000500
501 if not group:
showard71b94312009-08-20 23:40:02 +0000502 self.read_machine_map()
jadmanski0afbb632008-06-06 21:10:57 +0000503 group = self.machine_group.get(hostname, hostname)
showard71b94312009-08-20 23:40:02 +0000504 if group == hostname and owner:
505 group = owner + '/' + hostname
jadmanski0afbb632008-06-06 21:10:57 +0000506
showard71b94312009-08-20 23:40:02 +0000507 return {'hostname': hostname, 'machine_group': group, 'owner': owner}
508
509
510 def insert_machine(self, job, commit = None):
511 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000512 self.insert('tko_machines', machine_info, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000513 return self.get_last_autonumber_value()
514
515
showard71b94312009-08-20 23:40:02 +0000516 def update_machine_information(self, job, commit = None):
517 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000518 self.update('tko_machines', machine_info,
showard71b94312009-08-20 23:40:02 +0000519 where={'hostname': machine_info['hostname']},
520 commit=commit)
521
522
jadmanski0afbb632008-06-06 21:10:57 +0000523 def lookup_machine(self, hostname):
524 where = { 'hostname' : hostname }
showardeab66ce2009-12-23 00:03:56 +0000525 rows = self.select('machine_idx', 'tko_machines', where)
jadmanski0afbb632008-06-06 21:10:57 +0000526 if rows:
527 return rows[0][0]
528 else:
529 return None
530
531
532 def lookup_kernel(self, kernel):
showardeab66ce2009-12-23 00:03:56 +0000533 rows = self.select('kernel_idx', 'tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000534 {'kernel_hash':kernel.kernel_hash})
535 if rows:
536 return rows[0][0]
537 else:
538 return None
539
540
541 def insert_kernel(self, kernel, commit = None):
542 kver = self.lookup_kernel(kernel)
543 if kver:
544 return kver
545
546 # If this kernel has any significant patches, append their hash
547 # as diferentiator.
548 printable = kernel.base
549 patch_count = 0
550 for patch in kernel.patches:
551 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
552 patch.reference)
553 if not match:
554 patch_count += 1
555
showardeab66ce2009-12-23 00:03:56 +0000556 self.insert('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000557 {'base':kernel.base,
558 'kernel_hash':kernel.kernel_hash,
559 'printable':printable},
560 commit=commit)
561 kver = self.get_last_autonumber_value()
562
563 if patch_count > 0:
564 printable += ' p%d' % (kver)
showardeab66ce2009-12-23 00:03:56 +0000565 self.update('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000566 {'printable':printable},
567 {'kernel_idx':kver})
568
569 for patch in kernel.patches:
570 self.insert_patch(kver, patch, commit=commit)
571 return kver
572
573
574 def insert_patch(self, kver, patch, commit = None):
575 print patch.reference
576 name = os.path.basename(patch.reference)[:80]
showardeab66ce2009-12-23 00:03:56 +0000577 self.insert('tko_patches',
jadmanski0afbb632008-06-06 21:10:57 +0000578 {'kernel_idx': kver,
579 'name':name,
580 'url':patch.reference,
581 'hash':patch.hash},
582 commit=commit)
583
584
jadmanski74eebf32008-07-15 20:04:42 +0000585 def find_test(self, job_idx, testname, subdir):
586 where = {'job_idx': job_idx , 'test': testname, 'subdir': subdir}
showardeab66ce2009-12-23 00:03:56 +0000587 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000588 if rows:
589 return rows[0][0]
590 else:
591 return None
592
593
594 def find_tests(self, job_idx):
595 where = { 'job_idx':job_idx }
showardeab66ce2009-12-23 00:03:56 +0000596 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000597 if rows:
598 return [row[0] for row in rows]
599 else:
600 return []
601
602
603 def find_job(self, tag):
showardeab66ce2009-12-23 00:03:56 +0000604 rows = self.select('job_idx', 'tko_jobs', {'tag': tag})
jadmanski0afbb632008-06-06 21:10:57 +0000605 if rows:
606 return rows[0][0]
607 else:
608 return None
mblighaf25f062007-12-03 17:48:35 +0000609
610
mbligh96cf0512008-04-17 15:25:38 +0000611def _get_db_type():
jadmanski0afbb632008-06-06 21:10:57 +0000612 """Get the database type name to use from the global config."""
Jakob Juelich934f0dc2014-10-14 18:21:13 -0700613 get_value = global_config.global_config.get_config_value_with_fallback
614 return "db_" + get_value("AUTOTEST_WEB", "global_db_type", "db_type",
615 default="mysql")
mblighaf25f062007-12-03 17:48:35 +0000616
mbligh96cf0512008-04-17 15:25:38 +0000617
618def _get_error_class(class_name):
jadmanski0afbb632008-06-06 21:10:57 +0000619 """Retrieves the appropriate error class by name from the database
620 module."""
621 db_module = __import__("autotest_lib.tko." + _get_db_type(),
622 globals(), locals(), ["driver"])
623 return getattr(db_module.driver, class_name)
mbligh96cf0512008-04-17 15:25:38 +0000624
625
626def db(*args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000627 """Creates an instance of the database class with the arguments
628 provided in args and dargs, using the database type specified by
629 the global configuration (defaulting to mysql)."""
630 db_type = _get_db_type()
631 db_module = __import__("autotest_lib.tko." + db_type, globals(),
632 locals(), [db_type])
633 db = getattr(db_module, db_type)(*args, **dargs)
634 return db