blob: 362feae7e67ac568e05c0b984e72024ec6b219fb [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)
Dan Shid77fc1b2015-10-13 17:34:50 -0700378 elif job.machine:
379 # Only try to update tko_machines record if machine is set. This
380 # prevents unnecessary db writes for suite jobs.
showard71b94312009-08-20 23:40:02 +0000381 self.update_machine_information(job, commit=commit)
382
jamesrena12b8a02010-06-16 23:28:23 +0000383 afe_job_id = utils.get_afe_job_id(tag)
showardc1c1caf2009-09-08 16:26:50 +0000384
showard0fec8a02009-12-04 01:19:54 +0000385 data = {'tag':tag,
386 'label': job.label,
387 'username': job.user,
388 'machine_idx': job.machine_idx,
389 'queued_time': job.queued_time,
390 'started_time': job.started_time,
391 'finished_time': job.finished_time,
Dan Shi70647ca2015-07-16 22:52:35 -0700392 'afe_job_id': afe_job_id,
393 'afe_parent_job_id': parent_job_id}
394 if job.label:
395 label_info = site_utils.parse_job_name(job.label)
396 if label_info:
397 data['build'] = label_info.get('build', None)
398 data['build_version'] = label_info.get('build_version', None)
399 data['board'] = label_info.get('board', None)
400 data['suite'] = label_info.get('suite', None)
showard0fec8a02009-12-04 01:19:54 +0000401 is_update = hasattr(job, 'index')
402 if is_update:
showardeab66ce2009-12-23 00:03:56 +0000403 self.update('tko_jobs', data, {'job_idx': job.index}, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000404 else:
showardeab66ce2009-12-23 00:03:56 +0000405 self.insert('tko_jobs', data, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000406 job.index = self.get_last_autonumber_value()
showardc1a98d12010-01-15 00:22:22 +0000407 self.update_job_keyvals(job, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000408 for test in job.tests:
409 self.insert_test(job, test, commit=commit)
apw7a7316b2008-02-21 17:42:05 +0000410
mbligh237bed32007-09-05 13:05:57 +0000411
showardc1a98d12010-01-15 00:22:22 +0000412 def update_job_keyvals(self, job, commit=None):
413 for key, value in job.keyval_dict.iteritems():
414 where = {'job_id': job.index, 'key': key}
415 data = dict(where, value=value)
416 exists = self.select('id', 'tko_job_keyvals', where=where)
417
418 if exists:
419 self.update('tko_job_keyvals', data, where=where, commit=commit)
420 else:
421 self.insert('tko_job_keyvals', data, commit=commit)
422
423
jadmanski0afbb632008-06-06 21:10:57 +0000424 def insert_test(self, job, test, commit = None):
425 kver = self.insert_kernel(test.kernel, commit=commit)
426 data = {'job_idx':job.index, 'test':test.testname,
427 'subdir':test.subdir, 'kernel_idx':kver,
428 'status':self.status_idx[test.status],
429 'reason':test.reason, 'machine_idx':job.machine_idx,
430 'started_time': test.started_time,
431 'finished_time':test.finished_time}
jadmanski9b6babf2009-04-21 17:57:40 +0000432 is_update = hasattr(test, "test_idx")
433 if is_update:
jadmanski74eebf32008-07-15 20:04:42 +0000434 test_idx = test.test_idx
showardeab66ce2009-12-23 00:03:56 +0000435 self.update('tko_tests', data,
436 {'test_idx': test_idx}, commit=commit)
jadmanskib591fba2008-09-10 16:19:22 +0000437 where = {'test_idx': test_idx}
showardeab66ce2009-12-23 00:03:56 +0000438 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700439 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000440 self.delete('tko_iteration_attributes', where)
showard0fec8a02009-12-04 01:19:54 +0000441 where['user_created'] = 0
showardeab66ce2009-12-23 00:03:56 +0000442 self.delete('tko_test_attributes', where)
jadmanski74eebf32008-07-15 20:04:42 +0000443 else:
showardeab66ce2009-12-23 00:03:56 +0000444 self.insert('tko_tests', data, commit=commit)
jadmanski74eebf32008-07-15 20:04:42 +0000445 test_idx = test.test_idx = self.get_last_autonumber_value()
446 data = {'test_idx': test_idx}
mbligh237bed32007-09-05 13:05:57 +0000447
jadmanski0afbb632008-06-06 21:10:57 +0000448 for i in test.iterations:
449 data['iteration'] = i.index
450 for key, value in i.attr_keyval.iteritems():
451 data['attribute'] = key
452 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000453 self.insert('tko_iteration_attributes', data,
jadmanski0afbb632008-06-06 21:10:57 +0000454 commit=commit)
455 for key, value in i.perf_keyval.iteritems():
456 data['attribute'] = key
457 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000458 self.insert('tko_iteration_result', data,
mbligh432bad42007-10-09 19:56:07 +0000459 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000460
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700461 data = {'test_idx': test_idx}
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700462
jadmanski0afbb632008-06-06 21:10:57 +0000463 for key, value in test.attributes.iteritems():
464 data = {'test_idx': test_idx, 'attribute': key,
465 'value': value}
showardeab66ce2009-12-23 00:03:56 +0000466 self.insert('tko_test_attributes', data, commit=commit)
mbligh2bd48872007-09-20 18:32:25 +0000467
jadmanski9b6babf2009-04-21 17:57:40 +0000468 if not is_update:
469 for label_index in test.labels:
470 data = {'test_id': test_idx, 'testlabel_id': label_index}
showardeab66ce2009-12-23 00:03:56 +0000471 self.insert('tko_test_labels_tests', data, commit=commit)
jadmanski9b6babf2009-04-21 17:57:40 +0000472
mbligh056d0d32006-10-08 22:31:10 +0000473
jadmanski0afbb632008-06-06 21:10:57 +0000474 def read_machine_map(self):
showard71b94312009-08-20 23:40:02 +0000475 if self.machine_group or not self.machine_map:
476 return
jadmanski0afbb632008-06-06 21:10:57 +0000477 for line in open(self.machine_map, 'r').readlines():
478 (machine, group) = line.split()
479 self.machine_group[machine] = group
mbligh96b9a5a2007-11-24 19:32:20 +0000480
481
showard71b94312009-08-20 23:40:02 +0000482 def machine_info_dict(self, job):
jadmanski0afbb632008-06-06 21:10:57 +0000483 hostname = job.machine
showard71b94312009-08-20 23:40:02 +0000484 group = job.machine_group
485 owner = job.machine_owner
jadmanski0afbb632008-06-06 21:10:57 +0000486
487 if not group:
showard71b94312009-08-20 23:40:02 +0000488 self.read_machine_map()
jadmanski0afbb632008-06-06 21:10:57 +0000489 group = self.machine_group.get(hostname, hostname)
showard71b94312009-08-20 23:40:02 +0000490 if group == hostname and owner:
491 group = owner + '/' + hostname
jadmanski0afbb632008-06-06 21:10:57 +0000492
showard71b94312009-08-20 23:40:02 +0000493 return {'hostname': hostname, 'machine_group': group, 'owner': owner}
494
495
496 def insert_machine(self, job, commit = None):
497 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000498 self.insert('tko_machines', machine_info, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000499 return self.get_last_autonumber_value()
500
501
showard71b94312009-08-20 23:40:02 +0000502 def update_machine_information(self, job, commit = None):
503 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000504 self.update('tko_machines', machine_info,
showard71b94312009-08-20 23:40:02 +0000505 where={'hostname': machine_info['hostname']},
506 commit=commit)
507
508
jadmanski0afbb632008-06-06 21:10:57 +0000509 def lookup_machine(self, hostname):
510 where = { 'hostname' : hostname }
showardeab66ce2009-12-23 00:03:56 +0000511 rows = self.select('machine_idx', 'tko_machines', where)
jadmanski0afbb632008-06-06 21:10:57 +0000512 if rows:
513 return rows[0][0]
514 else:
515 return None
516
517
518 def lookup_kernel(self, kernel):
showardeab66ce2009-12-23 00:03:56 +0000519 rows = self.select('kernel_idx', 'tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000520 {'kernel_hash':kernel.kernel_hash})
521 if rows:
522 return rows[0][0]
523 else:
524 return None
525
526
527 def insert_kernel(self, kernel, commit = None):
528 kver = self.lookup_kernel(kernel)
529 if kver:
530 return kver
531
532 # If this kernel has any significant patches, append their hash
533 # as diferentiator.
534 printable = kernel.base
535 patch_count = 0
536 for patch in kernel.patches:
537 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
538 patch.reference)
539 if not match:
540 patch_count += 1
541
showardeab66ce2009-12-23 00:03:56 +0000542 self.insert('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000543 {'base':kernel.base,
544 'kernel_hash':kernel.kernel_hash,
545 'printable':printable},
546 commit=commit)
547 kver = self.get_last_autonumber_value()
548
549 if patch_count > 0:
550 printable += ' p%d' % (kver)
showardeab66ce2009-12-23 00:03:56 +0000551 self.update('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000552 {'printable':printable},
553 {'kernel_idx':kver})
554
555 for patch in kernel.patches:
556 self.insert_patch(kver, patch, commit=commit)
557 return kver
558
559
560 def insert_patch(self, kver, patch, commit = None):
561 print patch.reference
562 name = os.path.basename(patch.reference)[:80]
showardeab66ce2009-12-23 00:03:56 +0000563 self.insert('tko_patches',
jadmanski0afbb632008-06-06 21:10:57 +0000564 {'kernel_idx': kver,
565 'name':name,
566 'url':patch.reference,
567 'hash':patch.hash},
568 commit=commit)
569
570
jadmanski74eebf32008-07-15 20:04:42 +0000571 def find_test(self, job_idx, testname, subdir):
572 where = {'job_idx': job_idx , 'test': testname, 'subdir': subdir}
showardeab66ce2009-12-23 00:03:56 +0000573 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000574 if rows:
575 return rows[0][0]
576 else:
577 return None
578
579
580 def find_tests(self, job_idx):
581 where = { 'job_idx':job_idx }
showardeab66ce2009-12-23 00:03:56 +0000582 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000583 if rows:
584 return [row[0] for row in rows]
585 else:
586 return []
587
588
589 def find_job(self, tag):
showardeab66ce2009-12-23 00:03:56 +0000590 rows = self.select('job_idx', 'tko_jobs', {'tag': tag})
jadmanski0afbb632008-06-06 21:10:57 +0000591 if rows:
592 return rows[0][0]
593 else:
594 return None
mblighaf25f062007-12-03 17:48:35 +0000595
596
mbligh96cf0512008-04-17 15:25:38 +0000597def _get_db_type():
jadmanski0afbb632008-06-06 21:10:57 +0000598 """Get the database type name to use from the global config."""
Jakob Juelich934f0dc2014-10-14 18:21:13 -0700599 get_value = global_config.global_config.get_config_value_with_fallback
600 return "db_" + get_value("AUTOTEST_WEB", "global_db_type", "db_type",
601 default="mysql")
mblighaf25f062007-12-03 17:48:35 +0000602
mbligh96cf0512008-04-17 15:25:38 +0000603
604def _get_error_class(class_name):
jadmanski0afbb632008-06-06 21:10:57 +0000605 """Retrieves the appropriate error class by name from the database
606 module."""
607 db_module = __import__("autotest_lib.tko." + _get_db_type(),
608 globals(), locals(), ["driver"])
609 return getattr(db_module.driver, class_name)
mbligh96cf0512008-04-17 15:25:38 +0000610
611
612def db(*args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000613 """Creates an instance of the database class with the arguments
614 provided in args and dargs, using the database type specified by
615 the global configuration (defaulting to mysql)."""
616 db_type = _get_db_type()
617 db_module = __import__("autotest_lib.tko." + db_type, globals(),
618 locals(), [db_type])
619 db = getattr(db_module, db_type)(*args, **dargs)
620 return db