blob: 80ea5c9e4a06c443f0ed175040f4e6491514fa3a [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
Jakob Juelich934f0dc2014-10-14 18:21:13 -07009from autotest_lib.frontend import database_settings_helper
Dan Shi70647ca2015-07-16 22:52:35 -070010from autotest_lib.server import site_utils
jamesrena12b8a02010-06-16 23:28:23 +000011from autotest_lib.tko import utils
mbligh96cf0512008-04-17 15:25:38 +000012
mblighed4d6dd2008-02-27 16:49:43 +000013
mblighaea09602008-04-16 22:59:37 +000014class MySQLTooManyRows(Exception):
jadmanski0afbb632008-06-06 21:10:57 +000015 pass
mblighaea09602008-04-16 22:59:37 +000016
mblighd5c33db2006-10-08 21:34:16 +000017
mbligh7636b3a2008-06-11 15:44:01 +000018class db_sql(object):
jadmanski0afbb632008-06-06 21:10:57 +000019 def __init__(self, debug=False, autocommit=True, host=None,
20 database=None, user=None, password=None):
21 self.debug = debug
22 self.autocommit = autocommit
23 self._load_config(host, database, user, password)
mbligh96cf0512008-04-17 15:25:38 +000024
jadmanski0afbb632008-06-06 21:10:57 +000025 self.con = None
26 self._init_db()
mblighd5c33db2006-10-08 21:34:16 +000027
jadmanski0afbb632008-06-06 21:10:57 +000028 # if not present, insert statuses
29 self.status_idx = {}
30 self.status_word = {}
showardeab66ce2009-12-23 00:03:56 +000031 status_rows = self.select('status_idx, word', 'tko_status', None)
jadmanski0afbb632008-06-06 21:10:57 +000032 for s in status_rows:
33 self.status_idx[s[1]] = s[0]
34 self.status_word[s[0]] = s[1]
mbligh048e1c92007-10-07 00:10:33 +000035
jadmanski0afbb632008-06-06 21:10:57 +000036 machine_map = os.path.join(os.path.dirname(__file__),
37 'machines')
38 if os.path.exists(machine_map):
39 self.machine_map = machine_map
40 else:
41 self.machine_map = None
42 self.machine_group = {}
mbligh048e1c92007-10-07 00:10:33 +000043
mbligh8e1ab172007-09-13 17:29:56 +000044
jadmanski0afbb632008-06-06 21:10:57 +000045 def _load_config(self, host, database, user, password):
Jakob Juelich934f0dc2014-10-14 18:21:13 -070046 """Loads configuration settings required to connect to the database.
47
48 This will try to connect to use the settings prefixed with global_db_.
49 If they do not exist, they un-prefixed settings will be used.
50
51 If parameters are supplied, these will be taken instead of the values
52 in global_config.
53
54 @param host: If set, this host will be used, if not, the host will be
55 retrieved from global_config.
56 @param database: If set, this database will be used, if not, the
57 database will be retrieved from global_config.
58 @param user: If set, this user will be used, if not, the
59 user will be retrieved from global_config.
60 @param password: If set, this password will be used, if not, the
61 password will be retrieved from global_config.
62 """
63 database_settings = database_settings_helper.get_global_db_config()
mbligh65acae52008-04-24 20:21:55 +000064
jadmanski0afbb632008-06-06 21:10:57 +000065 # grab the host, database
Jakob Juelich934f0dc2014-10-14 18:21:13 -070066 self.host = host or database_settings['HOST']
67 self.database = database or database_settings['NAME']
mbligh65acae52008-04-24 20:21:55 +000068
jadmanski0afbb632008-06-06 21:10:57 +000069 # grab the user and password
Jakob Juelich934f0dc2014-10-14 18:21:13 -070070 self.user = user or database_settings['USER']
71 self.password = password or database_settings['PASSWORD']
mbligh65acae52008-04-24 20:21:55 +000072
Michael Spang7a273472014-10-08 12:08:13 -040073 # grab the timeout configuration
Jakob Juelich934f0dc2014-10-14 18:21:13 -070074 self.query_timeout =(
75 database_settings.get('OPTIONS', {}).get('timeout', 3600))
76
77 # Using fallback to non-global in order to work without configuration
78 # overhead on non-shard instances.
79 get_value = global_config.global_config.get_config_value_with_fallback
Jakob Juelich475b82b2014-09-30 11:17:07 -070080 self.min_delay = get_value("AUTOTEST_WEB", "global_db_min_retry_delay",
Jakob Juelich934f0dc2014-10-14 18:21:13 -070081 "min_retry_delay", type=int, default=20)
Jakob Juelich475b82b2014-09-30 11:17:07 -070082 self.max_delay = get_value("AUTOTEST_WEB", "global_db_max_retry_delay",
Jakob Juelich934f0dc2014-10-14 18:21:13 -070083 "max_retry_delay", type=int, default=60)
mbligh65acae52008-04-24 20:21:55 +000084
Richard Barnette2468fbd2014-11-07 01:12:46 +000085 # TODO(beeps): Move this to django settings once we have routers.
86 # On test instances mysql connects through a different port. No point
87 # piping this through our entire infrastructure when it is only really
88 # used for testing; Ideally we would specify this through django
89 # settings and default it to the empty string so django will figure out
90 # the default based on the database backend (eg: mysql, 3306), but until
91 # we have database routers in place any django settings will apply to
92 # both tko and afe.
93 # The intended use of this port is to allow a testing shard vm to
94 # update the master vm's database with test results. Specifying
95 # and empty string will fallback to not even specifying the port
96 # to the backend in tko/db.py. Unfortunately this means retries
97 # won't work on the test cluster till we've migrated to routers.
98 self.port = global_config.global_config.get_config_value(
99 "AUTOTEST_WEB", "global_db_port", type=str, default='')
100
mbligh65acae52008-04-24 20:21:55 +0000101
jadmanski0afbb632008-06-06 21:10:57 +0000102 def _init_db(self):
103 # make sure we clean up any existing connection
104 if self.con:
105 self.con.close()
106 self.con = None
mbligh65acae52008-04-24 20:21:55 +0000107
jadmanski0afbb632008-06-06 21:10:57 +0000108 # create the db connection and cursor
109 self.con = self.connect(self.host, self.database,
Richard Barnette2468fbd2014-11-07 01:12:46 +0000110 self.user, self.password, self.port)
jadmanski0afbb632008-06-06 21:10:57 +0000111 self.cur = self.con.cursor()
mbligh96cf0512008-04-17 15:25:38 +0000112
113
jadmanski0afbb632008-06-06 21:10:57 +0000114 def _random_delay(self):
115 delay = random.randint(self.min_delay, self.max_delay)
116 time.sleep(delay)
mbligh65acae52008-04-24 20:21:55 +0000117
118
jadmanski0afbb632008-06-06 21:10:57 +0000119 def run_with_retry(self, function, *args, **dargs):
120 """Call function(*args, **dargs) until either it passes
121 without an operational error, or a timeout is reached.
122 This will re-connect to the database, so it is NOT safe
123 to use this inside of a database transaction.
jadmanskie7a69092008-05-29 21:03:13 +0000124
jadmanski0afbb632008-06-06 21:10:57 +0000125 It can be safely used with transactions, but the
126 transaction start & end must be completely contained
127 within the call to 'function'."""
128 OperationalError = _get_error_class("OperationalError")
mbligh65acae52008-04-24 20:21:55 +0000129
jadmanski0afbb632008-06-06 21:10:57 +0000130 success = False
131 start_time = time.time()
132 while not success:
133 try:
134 result = function(*args, **dargs)
135 except OperationalError, e:
136 self._log_operational_error(e)
137 stop_time = time.time()
138 elapsed_time = stop_time - start_time
139 if elapsed_time > self.query_timeout:
140 raise
141 else:
142 try:
143 self._random_delay()
144 self._init_db()
145 except OperationalError, e:
146 self._log_operational_error(e)
147 else:
148 success = True
149 return result
mbligh96cf0512008-04-17 15:25:38 +0000150
151
jadmanski0afbb632008-06-06 21:10:57 +0000152 def _log_operational_error(self, e):
mbligh097407d2009-02-17 15:49:37 +0000153 msg = ("%s: An operational error occured during a database "
jadmanski5d4c27e2009-03-02 16:45:42 +0000154 "operation: %s" % (time.strftime("%X %x"), str(e)))
jadmanski0afbb632008-06-06 21:10:57 +0000155 print >> sys.stderr, msg
156 sys.stderr.flush() # we want these msgs to show up immediately
jadmanski60d4fa62008-05-06 22:49:41 +0000157
158
jadmanski0afbb632008-06-06 21:10:57 +0000159 def dprint(self, value):
160 if self.debug:
161 sys.stdout.write('SQL: ' + str(value) + '\n')
mbligh8e1ab172007-09-13 17:29:56 +0000162
mblighd5c33db2006-10-08 21:34:16 +0000163
jadmanski0afbb632008-06-06 21:10:57 +0000164 def commit(self):
165 self.con.commit()
mbligh432bad42007-10-09 19:56:07 +0000166
167
Simran Basie129a962012-08-31 13:03:53 -0700168 def rollback(self):
169 self.con.rollback()
170
171
jadmanski0afbb632008-06-06 21:10:57 +0000172 def get_last_autonumber_value(self):
173 self.cur.execute('SELECT LAST_INSERT_ID()', [])
174 return self.cur.fetchall()[0][0]
mblighe12b8612008-02-12 20:58:14 +0000175
176
showardc1a98d12010-01-15 00:22:22 +0000177 def _quote(self, field):
178 return '`%s`' % field
179
180
181 def _where_clause(self, where):
182 if not where:
183 return '', []
184
185 if isinstance(where, dict):
186 # key/value pairs (which should be equal, or None for null)
187 keys, values = [], []
188 for field, value in where.iteritems():
189 quoted_field = self._quote(field)
190 if value is None:
191 keys.append(quoted_field + ' is null')
192 else:
193 keys.append(quoted_field + '=%s')
194 values.append(value)
195 where_clause = ' and '.join(keys)
196 elif isinstance(where, basestring):
197 # the exact string
198 where_clause = where
199 values = []
200 elif isinstance(where, tuple):
201 # preformatted where clause + values
202 where_clause, values = where
203 assert where_clause
204 else:
205 raise ValueError('Invalid "where" value: %r' % where)
206
207 return ' WHERE ' + where_clause, values
208
209
210
211 def select(self, fields, table, where, distinct=False, group_by=None,
212 max_rows=None):
jadmanski0afbb632008-06-06 21:10:57 +0000213 """\
214 This selects all the fields requested from a
215 specific table with a particular where clause.
216 The where clause can either be a dictionary of
217 field=value pairs, a string, or a tuple of (string,
218 a list of values). The last option is what you
219 should use when accepting user input as it'll
220 protect you against sql injection attacks (if
221 all user data is placed in the array rather than
222 the raw SQL).
mbligh12eebfa2008-01-03 02:01:53 +0000223
jadmanski0afbb632008-06-06 21:10:57 +0000224 For example:
225 where = ("a = %s AND b = %s", ['val', 'val'])
226 is better than
227 where = "a = 'val' AND b = 'val'"
228 """
229 cmd = ['select']
230 if distinct:
231 cmd.append('distinct')
232 cmd += [fields, 'from', table]
mbligh608c3252007-08-31 13:53:00 +0000233
showardc1a98d12010-01-15 00:22:22 +0000234 where_clause, values = self._where_clause(where)
235 cmd.append(where_clause)
mbligh96cf0512008-04-17 15:25:38 +0000236
jadmanski0afbb632008-06-06 21:10:57 +0000237 if group_by:
238 cmd.append(' GROUP BY ' + group_by)
mbligh83f63a02007-12-12 19:13:04 +0000239
jadmanski0afbb632008-06-06 21:10:57 +0000240 self.dprint('%s %s' % (' '.join(cmd), values))
mbligh96cf0512008-04-17 15:25:38 +0000241
jadmanski0afbb632008-06-06 21:10:57 +0000242 # create a re-runable function for executing the query
243 def exec_sql():
244 sql = ' '.join(cmd)
245 numRec = self.cur.execute(sql, values)
mblighd876f452008-12-03 15:09:17 +0000246 if max_rows is not None and numRec > max_rows:
jadmanski0afbb632008-06-06 21:10:57 +0000247 msg = 'Exceeded allowed number of records'
248 raise MySQLTooManyRows(msg)
249 return self.cur.fetchall()
mbligh96cf0512008-04-17 15:25:38 +0000250
jadmanski0afbb632008-06-06 21:10:57 +0000251 # run the query, re-trying after operational errors
252 if self.autocommit:
253 return self.run_with_retry(exec_sql)
254 else:
255 return exec_sql()
mblighd5c33db2006-10-08 21:34:16 +0000256
mbligh056d0d32006-10-08 22:31:10 +0000257
jadmanski0afbb632008-06-06 21:10:57 +0000258 def select_sql(self, fields, table, sql, values):
259 """\
260 select fields from table "sql"
261 """
262 cmd = 'select %s from %s %s' % (fields, table, sql)
263 self.dprint(cmd)
mbligh414c69e2007-10-05 15:13:06 +0000264
jadmanski0afbb632008-06-06 21:10:57 +0000265 # create a -re-runable function for executing the query
266 def exec_sql():
267 self.cur.execute(cmd, values)
268 return self.cur.fetchall()
mbligh96b9a5a2007-11-24 19:32:20 +0000269
jadmanski0afbb632008-06-06 21:10:57 +0000270 # run the query, re-trying after operational errors
271 if self.autocommit:
272 return self.run_with_retry(exec_sql)
273 else:
274 return exec_sql()
mbligh96b9a5a2007-11-24 19:32:20 +0000275
mbligh608c3252007-08-31 13:53:00 +0000276
jadmanski0afbb632008-06-06 21:10:57 +0000277 def _exec_sql_with_commit(self, sql, values, commit):
278 if self.autocommit:
279 # re-run the query until it succeeds
280 def exec_sql():
281 self.cur.execute(sql, values)
282 self.con.commit()
283 self.run_with_retry(exec_sql)
284 else:
285 # take one shot at running the query
286 self.cur.execute(sql, values)
287 if commit:
288 self.con.commit()
mbligh96b9a5a2007-11-24 19:32:20 +0000289
mbligh2bd48872007-09-20 18:32:25 +0000290
jadmanskib591fba2008-09-10 16:19:22 +0000291 def insert(self, table, data, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000292 """\
293 'insert into table (keys) values (%s ... %s)', values
mbligh96cf0512008-04-17 15:25:38 +0000294
jadmanski0afbb632008-06-06 21:10:57 +0000295 data:
296 dictionary of fields and data
297 """
298 fields = data.keys()
299 refs = ['%s' for field in fields]
300 values = [data[field] for field in fields]
showardc1a98d12010-01-15 00:22:22 +0000301 cmd = ('insert into %s (%s) values (%s)' %
302 (table, ','.join(self._quote(field) for field in fields),
303 ','.join(refs)))
jadmanski0afbb632008-06-06 21:10:57 +0000304 self.dprint('%s %s' % (cmd, values))
mblighe9cf9d42007-08-31 08:56:00 +0000305
jadmanski0afbb632008-06-06 21:10:57 +0000306 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000307
mbligh048e1c92007-10-07 00:10:33 +0000308
jadmanski0afbb632008-06-06 21:10:57 +0000309 def delete(self, table, where, commit = None):
310 cmd = ['delete from', table]
mblighd876f452008-12-03 15:09:17 +0000311 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000312 commit = self.autocommit
showardc1a98d12010-01-15 00:22:22 +0000313 where_clause, values = self._where_clause(where)
314 cmd.append(where_clause)
jadmanski0afbb632008-06-06 21:10:57 +0000315 sql = ' '.join(cmd)
316 self.dprint('%s %s' % (sql, values))
mbligh048e1c92007-10-07 00:10:33 +0000317
jadmanski0afbb632008-06-06 21:10:57 +0000318 self._exec_sql_with_commit(sql, values, commit)
mbligh048e1c92007-10-07 00:10:33 +0000319
mbligh7a41a862007-11-30 17:44:24 +0000320
jadmanski0afbb632008-06-06 21:10:57 +0000321 def update(self, table, data, where, commit = None):
322 """\
323 'update table set data values (%s ... %s) where ...'
mbligh2aaeb672007-10-01 14:54:18 +0000324
jadmanski0afbb632008-06-06 21:10:57 +0000325 data:
326 dictionary of fields and data
327 """
mblighd876f452008-12-03 15:09:17 +0000328 if commit is None:
jadmanski0afbb632008-06-06 21:10:57 +0000329 commit = self.autocommit
330 cmd = 'update %s ' % table
331 fields = data.keys()
showardc1a98d12010-01-15 00:22:22 +0000332 data_refs = [self._quote(field) + '=%s' for field in fields]
jadmanski0afbb632008-06-06 21:10:57 +0000333 data_values = [data[field] for field in fields]
jadmanski74eebf32008-07-15 20:04:42 +0000334 cmd += ' set ' + ', '.join(data_refs)
mbligh2aaeb672007-10-01 14:54:18 +0000335
showardc1a98d12010-01-15 00:22:22 +0000336 where_clause, where_values = self._where_clause(where)
337 cmd += where_clause
mbligh2aaeb672007-10-01 14:54:18 +0000338
jadmanski0afbb632008-06-06 21:10:57 +0000339 values = data_values + where_values
jadmanski74eebf32008-07-15 20:04:42 +0000340 self.dprint('%s %s' % (cmd, values))
mbligh2aaeb672007-10-01 14:54:18 +0000341
jadmanski0afbb632008-06-06 21:10:57 +0000342 self._exec_sql_with_commit(cmd, values, commit)
mblighe9cf9d42007-08-31 08:56:00 +0000343
344
jadmanski0afbb632008-06-06 21:10:57 +0000345 def delete_job(self, tag, commit = None):
346 job_idx = self.find_job(tag)
347 for test_idx in self.find_tests(job_idx):
348 where = {'test_idx' : test_idx}
showardeab66ce2009-12-23 00:03:56 +0000349 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700350 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000351 self.delete('tko_iteration_attributes', where)
352 self.delete('tko_test_attributes', where)
353 self.delete('tko_test_labels_tests', {'test_id': test_idx})
jadmanski0afbb632008-06-06 21:10:57 +0000354 where = {'job_idx' : job_idx}
showardeab66ce2009-12-23 00:03:56 +0000355 self.delete('tko_tests', where)
356 self.delete('tko_jobs', where)
apw7a7316b2008-02-21 17:42:05 +0000357
apw7a7316b2008-02-21 17:42:05 +0000358
Dan Shi70647ca2015-07-16 22:52:35 -0700359 def insert_job(self, tag, job, parent_job_id=None, commit=None):
jadmanski0afbb632008-06-06 21:10:57 +0000360 job.machine_idx = self.lookup_machine(job.machine)
361 if not job.machine_idx:
showard71b94312009-08-20 23:40:02 +0000362 job.machine_idx = self.insert_machine(job, commit=commit)
363 else:
364 self.update_machine_information(job, commit=commit)
365
jamesrena12b8a02010-06-16 23:28:23 +0000366 afe_job_id = utils.get_afe_job_id(tag)
showardc1c1caf2009-09-08 16:26:50 +0000367
showard0fec8a02009-12-04 01:19:54 +0000368 data = {'tag':tag,
369 'label': job.label,
370 'username': job.user,
371 'machine_idx': job.machine_idx,
372 'queued_time': job.queued_time,
373 'started_time': job.started_time,
374 'finished_time': job.finished_time,
Dan Shi70647ca2015-07-16 22:52:35 -0700375 'afe_job_id': afe_job_id,
376 'afe_parent_job_id': parent_job_id}
377 if job.label:
378 label_info = site_utils.parse_job_name(job.label)
379 if label_info:
380 data['build'] = label_info.get('build', None)
381 data['build_version'] = label_info.get('build_version', None)
382 data['board'] = label_info.get('board', None)
383 data['suite'] = label_info.get('suite', None)
showard0fec8a02009-12-04 01:19:54 +0000384 is_update = hasattr(job, 'index')
385 if is_update:
showardeab66ce2009-12-23 00:03:56 +0000386 self.update('tko_jobs', data, {'job_idx': job.index}, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000387 else:
showardeab66ce2009-12-23 00:03:56 +0000388 self.insert('tko_jobs', data, commit=commit)
showard0fec8a02009-12-04 01:19:54 +0000389 job.index = self.get_last_autonumber_value()
showardc1a98d12010-01-15 00:22:22 +0000390 self.update_job_keyvals(job, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000391 for test in job.tests:
392 self.insert_test(job, test, commit=commit)
apw7a7316b2008-02-21 17:42:05 +0000393
mbligh237bed32007-09-05 13:05:57 +0000394
showardc1a98d12010-01-15 00:22:22 +0000395 def update_job_keyvals(self, job, commit=None):
396 for key, value in job.keyval_dict.iteritems():
397 where = {'job_id': job.index, 'key': key}
398 data = dict(where, value=value)
399 exists = self.select('id', 'tko_job_keyvals', where=where)
400
401 if exists:
402 self.update('tko_job_keyvals', data, where=where, commit=commit)
403 else:
404 self.insert('tko_job_keyvals', data, commit=commit)
405
406
jadmanski0afbb632008-06-06 21:10:57 +0000407 def insert_test(self, job, test, commit = None):
408 kver = self.insert_kernel(test.kernel, commit=commit)
409 data = {'job_idx':job.index, 'test':test.testname,
410 'subdir':test.subdir, 'kernel_idx':kver,
411 'status':self.status_idx[test.status],
412 'reason':test.reason, 'machine_idx':job.machine_idx,
413 'started_time': test.started_time,
414 'finished_time':test.finished_time}
jadmanski9b6babf2009-04-21 17:57:40 +0000415 is_update = hasattr(test, "test_idx")
416 if is_update:
jadmanski74eebf32008-07-15 20:04:42 +0000417 test_idx = test.test_idx
showardeab66ce2009-12-23 00:03:56 +0000418 self.update('tko_tests', data,
419 {'test_idx': test_idx}, commit=commit)
jadmanskib591fba2008-09-10 16:19:22 +0000420 where = {'test_idx': test_idx}
showardeab66ce2009-12-23 00:03:56 +0000421 self.delete('tko_iteration_result', where)
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700422 self.delete('tko_iteration_perf_value', where)
showardeab66ce2009-12-23 00:03:56 +0000423 self.delete('tko_iteration_attributes', where)
showard0fec8a02009-12-04 01:19:54 +0000424 where['user_created'] = 0
showardeab66ce2009-12-23 00:03:56 +0000425 self.delete('tko_test_attributes', where)
jadmanski74eebf32008-07-15 20:04:42 +0000426 else:
showardeab66ce2009-12-23 00:03:56 +0000427 self.insert('tko_tests', data, commit=commit)
jadmanski74eebf32008-07-15 20:04:42 +0000428 test_idx = test.test_idx = self.get_last_autonumber_value()
429 data = {'test_idx': test_idx}
mbligh237bed32007-09-05 13:05:57 +0000430
jadmanski0afbb632008-06-06 21:10:57 +0000431 for i in test.iterations:
432 data['iteration'] = i.index
433 for key, value in i.attr_keyval.iteritems():
434 data['attribute'] = key
435 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000436 self.insert('tko_iteration_attributes', data,
jadmanski0afbb632008-06-06 21:10:57 +0000437 commit=commit)
438 for key, value in i.perf_keyval.iteritems():
439 data['attribute'] = key
440 data['value'] = value
showardeab66ce2009-12-23 00:03:56 +0000441 self.insert('tko_iteration_result', data,
mbligh432bad42007-10-09 19:56:07 +0000442 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000443
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700444 data = {'test_idx': test_idx}
445 for i in test.perf_values:
446 data['iteration'] = i.index
447 for perf_dict in i.perf_measurements:
448 data['description'] = perf_dict['description']
449 data['value'] = perf_dict['value']
450 data['stddev'] = perf_dict['stddev']
451 data['units'] = perf_dict['units']
Fang Dengcdee2512014-12-09 15:11:25 -0800452 # TODO(fdeng): In db, higher_is_better doesn't allow null,
453 # This is a workaround to avoid altering the
454 # table (very expensive) while still allows test to send
455 # higher_is_better=None. Ideally, the table should be
456 # altered to allow this.
457 if perf_dict['higher_is_better'] is not None:
458 data['higher_is_better'] = perf_dict['higher_is_better']
Fang Deng7f24f0b2013-11-12 11:22:16 -0800459 data['graph'] = perf_dict['graph']
Dennis Jeffrey368c54b2013-07-24 11:19:03 -0700460 self.insert('tko_iteration_perf_value', data, commit=commit)
461
jadmanski0afbb632008-06-06 21:10:57 +0000462 for key, value in test.attributes.iteritems():
463 data = {'test_idx': test_idx, 'attribute': key,
464 'value': value}
showardeab66ce2009-12-23 00:03:56 +0000465 self.insert('tko_test_attributes', data, commit=commit)
mbligh2bd48872007-09-20 18:32:25 +0000466
jadmanski9b6babf2009-04-21 17:57:40 +0000467 if not is_update:
468 for label_index in test.labels:
469 data = {'test_id': test_idx, 'testlabel_id': label_index}
showardeab66ce2009-12-23 00:03:56 +0000470 self.insert('tko_test_labels_tests', data, commit=commit)
jadmanski9b6babf2009-04-21 17:57:40 +0000471
mbligh056d0d32006-10-08 22:31:10 +0000472
jadmanski0afbb632008-06-06 21:10:57 +0000473 def read_machine_map(self):
showard71b94312009-08-20 23:40:02 +0000474 if self.machine_group or not self.machine_map:
475 return
jadmanski0afbb632008-06-06 21:10:57 +0000476 for line in open(self.machine_map, 'r').readlines():
477 (machine, group) = line.split()
478 self.machine_group[machine] = group
mbligh96b9a5a2007-11-24 19:32:20 +0000479
480
showard71b94312009-08-20 23:40:02 +0000481 def machine_info_dict(self, job):
jadmanski0afbb632008-06-06 21:10:57 +0000482 hostname = job.machine
showard71b94312009-08-20 23:40:02 +0000483 group = job.machine_group
484 owner = job.machine_owner
jadmanski0afbb632008-06-06 21:10:57 +0000485
486 if not group:
showard71b94312009-08-20 23:40:02 +0000487 self.read_machine_map()
jadmanski0afbb632008-06-06 21:10:57 +0000488 group = self.machine_group.get(hostname, hostname)
showard71b94312009-08-20 23:40:02 +0000489 if group == hostname and owner:
490 group = owner + '/' + hostname
jadmanski0afbb632008-06-06 21:10:57 +0000491
showard71b94312009-08-20 23:40:02 +0000492 return {'hostname': hostname, 'machine_group': group, 'owner': owner}
493
494
495 def insert_machine(self, job, commit = None):
496 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000497 self.insert('tko_machines', machine_info, commit=commit)
jadmanski0afbb632008-06-06 21:10:57 +0000498 return self.get_last_autonumber_value()
499
500
showard71b94312009-08-20 23:40:02 +0000501 def update_machine_information(self, job, commit = None):
502 machine_info = self.machine_info_dict(job)
showardeab66ce2009-12-23 00:03:56 +0000503 self.update('tko_machines', machine_info,
showard71b94312009-08-20 23:40:02 +0000504 where={'hostname': machine_info['hostname']},
505 commit=commit)
506
507
jadmanski0afbb632008-06-06 21:10:57 +0000508 def lookup_machine(self, hostname):
509 where = { 'hostname' : hostname }
showardeab66ce2009-12-23 00:03:56 +0000510 rows = self.select('machine_idx', 'tko_machines', where)
jadmanski0afbb632008-06-06 21:10:57 +0000511 if rows:
512 return rows[0][0]
513 else:
514 return None
515
516
517 def lookup_kernel(self, kernel):
showardeab66ce2009-12-23 00:03:56 +0000518 rows = self.select('kernel_idx', 'tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000519 {'kernel_hash':kernel.kernel_hash})
520 if rows:
521 return rows[0][0]
522 else:
523 return None
524
525
526 def insert_kernel(self, kernel, commit = None):
527 kver = self.lookup_kernel(kernel)
528 if kver:
529 return kver
530
531 # If this kernel has any significant patches, append their hash
532 # as diferentiator.
533 printable = kernel.base
534 patch_count = 0
535 for patch in kernel.patches:
536 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
537 patch.reference)
538 if not match:
539 patch_count += 1
540
showardeab66ce2009-12-23 00:03:56 +0000541 self.insert('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000542 {'base':kernel.base,
543 'kernel_hash':kernel.kernel_hash,
544 'printable':printable},
545 commit=commit)
546 kver = self.get_last_autonumber_value()
547
548 if patch_count > 0:
549 printable += ' p%d' % (kver)
showardeab66ce2009-12-23 00:03:56 +0000550 self.update('tko_kernels',
jadmanski0afbb632008-06-06 21:10:57 +0000551 {'printable':printable},
552 {'kernel_idx':kver})
553
554 for patch in kernel.patches:
555 self.insert_patch(kver, patch, commit=commit)
556 return kver
557
558
559 def insert_patch(self, kver, patch, commit = None):
560 print patch.reference
561 name = os.path.basename(patch.reference)[:80]
showardeab66ce2009-12-23 00:03:56 +0000562 self.insert('tko_patches',
jadmanski0afbb632008-06-06 21:10:57 +0000563 {'kernel_idx': kver,
564 'name':name,
565 'url':patch.reference,
566 'hash':patch.hash},
567 commit=commit)
568
569
jadmanski74eebf32008-07-15 20:04:42 +0000570 def find_test(self, job_idx, testname, subdir):
571 where = {'job_idx': job_idx , 'test': testname, 'subdir': subdir}
showardeab66ce2009-12-23 00:03:56 +0000572 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000573 if rows:
574 return rows[0][0]
575 else:
576 return None
577
578
579 def find_tests(self, job_idx):
580 where = { 'job_idx':job_idx }
showardeab66ce2009-12-23 00:03:56 +0000581 rows = self.select('test_idx', 'tko_tests', where)
jadmanski0afbb632008-06-06 21:10:57 +0000582 if rows:
583 return [row[0] for row in rows]
584 else:
585 return []
586
587
588 def find_job(self, tag):
showardeab66ce2009-12-23 00:03:56 +0000589 rows = self.select('job_idx', 'tko_jobs', {'tag': tag})
jadmanski0afbb632008-06-06 21:10:57 +0000590 if rows:
591 return rows[0][0]
592 else:
593 return None
mblighaf25f062007-12-03 17:48:35 +0000594
595
mbligh96cf0512008-04-17 15:25:38 +0000596def _get_db_type():
jadmanski0afbb632008-06-06 21:10:57 +0000597 """Get the database type name to use from the global config."""
Jakob Juelich934f0dc2014-10-14 18:21:13 -0700598 get_value = global_config.global_config.get_config_value_with_fallback
599 return "db_" + get_value("AUTOTEST_WEB", "global_db_type", "db_type",
600 default="mysql")
mblighaf25f062007-12-03 17:48:35 +0000601
mbligh96cf0512008-04-17 15:25:38 +0000602
603def _get_error_class(class_name):
jadmanski0afbb632008-06-06 21:10:57 +0000604 """Retrieves the appropriate error class by name from the database
605 module."""
606 db_module = __import__("autotest_lib.tko." + _get_db_type(),
607 globals(), locals(), ["driver"])
608 return getattr(db_module.driver, class_name)
mbligh96cf0512008-04-17 15:25:38 +0000609
610
611def db(*args, **dargs):
jadmanski0afbb632008-06-06 21:10:57 +0000612 """Creates an instance of the database class with the arguments
613 provided in args and dargs, using the database type specified by
614 the global configuration (defaulting to mysql)."""
615 db_type = _get_db_type()
616 db_module = __import__("autotest_lib.tko." + db_type, globals(),
617 locals(), [db_type])
618 db = getattr(db_module, db_type)(*args, **dargs)
619 return db