blob: 235b485d6cc98bb063c87a716cb769a3c6de70e0 [file] [log] [blame]
mbligh65acae52008-04-24 20:21:55 +00001import re, os, sys, types, time, random
mbligh96cf0512008-04-17 15:25:38 +00002
3import common
4from autotest_lib.client.common_lib import global_config
5
mblighed4d6dd2008-02-27 16:49:43 +00006
mblighaea09602008-04-16 22:59:37 +00007class MySQLTooManyRows(Exception):
8 pass
9
mblighd5c33db2006-10-08 21:34:16 +000010
mblighaf25f062007-12-03 17:48:35 +000011class db_sql:
mbligh65acae52008-04-24 20:21:55 +000012 def __init__(self, debug=False, autocommit=True, host=None,
13 database=None, user=None, password=None):
mbligh8e1ab172007-09-13 17:29:56 +000014 self.debug = debug
mbligh432bad42007-10-09 19:56:07 +000015 self.autocommit = autocommit
mbligh65acae52008-04-24 20:21:55 +000016 self._load_config(host, database, user, password)
mbligh96cf0512008-04-17 15:25:38 +000017
mbligh65acae52008-04-24 20:21:55 +000018 self.con = None
mbligh96cf0512008-04-17 15:25:38 +000019 self._init_db()
mblighd5c33db2006-10-08 21:34:16 +000020
mbligh8e1ab172007-09-13 17:29:56 +000021 # if not present, insert statuses
22 self.status_idx = {}
23 self.status_word = {}
mblighc82f2462007-10-02 19:19:17 +000024 status_rows = self.select('status_idx, word', 'status', None)
25 for s in status_rows:
mbligh97894452007-10-05 15:10:33 +000026 self.status_idx[s[1]] = s[0]
mblighc82f2462007-10-02 19:19:17 +000027 self.status_word[s[0]] = s[1]
mbligh048e1c92007-10-07 00:10:33 +000028
mbligh96cf0512008-04-17 15:25:38 +000029 machine_map = os.path.join(os.path.dirname(__file__),
30 'machines')
mbligh048e1c92007-10-07 00:10:33 +000031 if os.path.exists(machine_map):
32 self.machine_map = machine_map
mblighba9c54c2007-10-26 16:41:26 +000033 else:
34 self.machine_map = None
mbligh048e1c92007-10-07 00:10:33 +000035 self.machine_group = {}
36
mbligh8e1ab172007-09-13 17:29:56 +000037
mbligh65acae52008-04-24 20:21:55 +000038 def _load_config(self, host, database, user, password):
39 # grab the global config
40 get_value = global_config.global_config.get_config_value
41
42 # grab the host, database
mblighe0032ad2008-06-02 19:32:33 +000043 if host:
44 self.host = host
45 else:
mbligh65acae52008-04-24 20:21:55 +000046 self.host = get_value("TKO", "host")
mblighe0032ad2008-06-02 19:32:33 +000047 if database:
48 self.database = database
49 else:
mbligh65acae52008-04-24 20:21:55 +000050 self.database = get_value("TKO", "database")
51
52 # grab the user and password
mblighe0032ad2008-06-02 19:32:33 +000053 if user:
54 self.user = user
55 else:
mbligh65acae52008-04-24 20:21:55 +000056 self.user = get_value("TKO", "user")
mblighe0032ad2008-06-02 19:32:33 +000057 if password:
58 self.password = password
59 else:
mbligh65acae52008-04-24 20:21:55 +000060 self.password = get_value("TKO", "password")
61
62 # grab the timeout configuration
63 self.query_timeout = get_value("TKO", "query_timeout",
64 type=int, default=3600)
65 self.min_delay = get_value("TKO", "min_retry_delay", type=int,
66 default=20)
67 self.max_delay = get_value("TKO", "max_retry_delay", type=int,
68 default=60)
69
70
mbligh96cf0512008-04-17 15:25:38 +000071 def _init_db(self):
mbligh65acae52008-04-24 20:21:55 +000072 # make sure we clean up any existing connection
73 if self.con:
74 self.con.close()
75 self.con = None
76
mbligh96cf0512008-04-17 15:25:38 +000077 # create the db connection and cursor
78 self.con = self.connect(self.host, self.database,
79 self.user, self.password)
80 self.cur = self.con.cursor()
81
82
mbligh65acae52008-04-24 20:21:55 +000083 def _random_delay(self):
84 delay = random.randint(self.min_delay, self.max_delay)
85 time.sleep(delay)
86
87
jadmanskie7a69092008-05-29 21:03:13 +000088 def run_with_retry(self, function, *args, **dargs):
mbligh96cf0512008-04-17 15:25:38 +000089 """Call function(*args, **dargs) until either it passes
jadmanskie7a69092008-05-29 21:03:13 +000090 without an operational error, or a timeout is reached.
91 This will re-connect to the database, so it is NOT safe
92 to use this inside of a database transaction.
93
94 It can be safely used with transactions, but the
95 transaction start & end must be completely contained
96 within the call to 'function'."""
mbligh96cf0512008-04-17 15:25:38 +000097 OperationalError = _get_error_class("OperationalError")
mbligh65acae52008-04-24 20:21:55 +000098
mbligh96cf0512008-04-17 15:25:38 +000099 success = False
100 start_time = time.time()
101 while not success:
102 try:
103 result = function(*args, **dargs)
jadmanski60d4fa62008-05-06 22:49:41 +0000104 except OperationalError, e:
105 self._log_operational_error(e)
mbligh96cf0512008-04-17 15:25:38 +0000106 stop_time = time.time()
107 elapsed_time = stop_time - start_time
mbligh65acae52008-04-24 20:21:55 +0000108 if elapsed_time > self.query_timeout:
mbligh96cf0512008-04-17 15:25:38 +0000109 raise
110 else:
111 try:
mbligh65acae52008-04-24 20:21:55 +0000112 self._random_delay()
mbligh96cf0512008-04-17 15:25:38 +0000113 self._init_db()
jadmanski60d4fa62008-05-06 22:49:41 +0000114 except OperationalError, e:
115 self._log_operational_error(e)
mbligh96cf0512008-04-17 15:25:38 +0000116 else:
117 success = True
118 return result
119
120
jadmanski60d4fa62008-05-06 22:49:41 +0000121 def _log_operational_error(self, e):
122 msg = ("An operational error occured during a database "
123 "operation: %s" % str(e))
124 print >> sys.stderr, msg
125 sys.stderr.flush() # we want these msgs to show up immediately
126
127
mbligh8e1ab172007-09-13 17:29:56 +0000128 def dprint(self, value):
129 if self.debug:
mbligh83f63a02007-12-12 19:13:04 +0000130 sys.stdout.write('SQL: ' + str(value) + '\n')
mbligh8e1ab172007-09-13 17:29:56 +0000131
mblighd5c33db2006-10-08 21:34:16 +0000132
mbligh432bad42007-10-09 19:56:07 +0000133 def commit(self):
134 self.con.commit()
135
136
mblighe12b8612008-02-12 20:58:14 +0000137 def get_last_autonumber_value(self):
138 self.cur.execute('SELECT LAST_INSERT_ID()', [])
139 return self.cur.fetchall()[0][0]
140
141
mblighaea09602008-04-16 22:59:37 +0000142 def select(self, fields, table, where, wherein={},
143 distinct = False, group_by = None, max_rows = None):
mbligh608c3252007-08-31 13:53:00 +0000144 """\
mbligh12eebfa2008-01-03 02:01:53 +0000145 This selects all the fields requested from a
146 specific table with a particular where clause.
147 The where clause can either be a dictionary of
148 field=value pairs, a string, or a tuple of (string,
149 a list of values). The last option is what you
150 should use when accepting user input as it'll
151 protect you against sql injection attacks (if
152 all user data is placed in the array rather than
153 the raw SQL).
154
155 For example:
156 where = ("a = %s AND b = %s", ['val', 'val'])
157 is better than
158 where = "a = 'val' AND b = 'val'"
mbligh608c3252007-08-31 13:53:00 +0000159 """
mbligh31d29c42007-09-27 00:51:33 +0000160 cmd = ['select']
161 if distinct:
162 cmd.append('distinct')
163 cmd += [fields, 'from', table]
mbligh608c3252007-08-31 13:53:00 +0000164
mbligh31d29c42007-09-27 00:51:33 +0000165 values = []
mbligh414c69e2007-10-05 15:13:06 +0000166 if where and isinstance(where, types.DictionaryType):
mbligh12eebfa2008-01-03 02:01:53 +0000167 # key/value pairs (which should be equal)
mbligh53d14252007-09-12 16:33:14 +0000168 keys = [field + '=%s' for field in where.keys()]
169 values = [where[field] for field in where.keys()]
170
mbligh31d29c42007-09-27 00:51:33 +0000171 cmd.append(' where ' + ' and '.join(keys))
mbligh414c69e2007-10-05 15:13:06 +0000172 elif where and isinstance(where, types.StringTypes):
mbligh12eebfa2008-01-03 02:01:53 +0000173 # the exact string
mbligh414c69e2007-10-05 15:13:06 +0000174 cmd.append(' where ' + where)
mbligh12eebfa2008-01-03 02:01:53 +0000175 elif where and isinstance(where, types.TupleType):
176 # preformatted where clause + values
177 (sql, vals) = where
178 values = vals
179 cmd.append(' where (%s) ' % sql)
mbligh53d14252007-09-12 16:33:14 +0000180
mbligh12eebfa2008-01-03 02:01:53 +0000181 # TODO: this assumes there's a where clause...bad
mbligh85952b42007-12-07 16:28:33 +0000182 if wherein and isinstance(wherein, types.DictionaryType):
mbligh96cf0512008-04-17 15:25:38 +0000183 keys_in = ["%s in (%s) " % (field, ','.join(where))
184 for field, where in wherein.iteritems()]
mbligh85952b42007-12-07 16:28:33 +0000185 cmd.append(' and '+' and '.join(keys_in))
mbligh96cf0512008-04-17 15:25:38 +0000186
mbligh83f63a02007-12-12 19:13:04 +0000187 if group_by:
188 cmd.append(' GROUP BY ' + group_by)
189
190 self.dprint('%s %s' % (' '.join(cmd), values))
mbligh96cf0512008-04-17 15:25:38 +0000191
192 # create a re-runable function for executing the query
193 def exec_sql():
194 sql = ' '.join(cmd)
195 numRec = self.cur.execute(sql, values)
196 if max_rows != None and numRec > max_rows:
197 msg = 'Exceeded allowed number of records'
198 raise MySQLTooManyRows(msg)
199 return self.cur.fetchall()
200
201 # run the query, re-trying after operational errors
jadmanskie7a69092008-05-29 21:03:13 +0000202 if self.autocommit:
203 return self.run_with_retry(exec_sql)
204 else:
205 return exec_sql()
mblighd5c33db2006-10-08 21:34:16 +0000206
mbligh056d0d32006-10-08 22:31:10 +0000207
mbligh414c69e2007-10-05 15:13:06 +0000208 def select_sql(self, fields, table, sql, values):
209 """\
210 select fields from table "sql"
211 """
212 cmd = 'select %s from %s %s' % (fields, table, sql)
213 self.dprint(cmd)
mbligh96cf0512008-04-17 15:25:38 +0000214
215 # create a -re-runable function for executing the query
216 def exec_sql():
217 self.cur.execute(cmd, values)
218 return self.cur.fetchall()
219
220 # run the query, re-trying after operational errors
jadmanskie7a69092008-05-29 21:03:13 +0000221 if self.autocommit:
222 return self.run_with_retry(exec_sql)
223 else:
224 return exec_sql()
mbligh96cf0512008-04-17 15:25:38 +0000225
226
227 def _exec_sql_with_commit(self, sql, values, commit):
228 if self.autocommit:
229 # re-run the query until it succeeds
230 def exec_sql():
231 self.cur.execute(sql, values)
232 self.con.commit()
jadmanskie7a69092008-05-29 21:03:13 +0000233 self.run_with_retry(exec_sql)
mbligh96cf0512008-04-17 15:25:38 +0000234 else:
235 # take one shot at running the query
236 self.cur.execute(sql, values)
237 if commit:
238 self.con.commit()
mbligh414c69e2007-10-05 15:13:06 +0000239
240
mbligh432bad42007-10-09 19:56:07 +0000241 def insert(self, table, data, commit = None):
mbligh608c3252007-08-31 13:53:00 +0000242 """\
243 'insert into table (keys) values (%s ... %s)', values
244
245 data:
246 dictionary of fields and data
247 """
248 fields = data.keys()
249 refs = ['%s' for field in fields]
250 values = [data[field] for field in fields]
251 cmd = 'insert into %s (%s) values (%s)' % \
252 (table, ','.join(fields), ','.join(refs))
mbligh96cf0512008-04-17 15:25:38 +0000253 self.dprint('%s %s' % (cmd, values))
mbligh53d14252007-09-12 16:33:14 +0000254
mbligh96cf0512008-04-17 15:25:38 +0000255 self._exec_sql_with_commit(cmd, values, commit)
mbligh608c3252007-08-31 13:53:00 +0000256
257
mbligh96b9a5a2007-11-24 19:32:20 +0000258 def delete(self, table, where, commit = None):
259 cmd = ['delete from', table]
260 if commit == None:
261 commit = self.autocommit
262 if where and isinstance(where, types.DictionaryType):
263 keys = [field + '=%s' for field in where.keys()]
264 values = [where[field] for field in where.keys()]
265 cmd += ['where', ' and '.join(keys)]
mbligh96cf0512008-04-17 15:25:38 +0000266 sql = ' '.join(cmd)
267 self.dprint('%s %s' % (sql, values))
268
269 self._exec_sql_with_commit(sql, values, commit)
270
mbligh96b9a5a2007-11-24 19:32:20 +0000271
mbligh432bad42007-10-09 19:56:07 +0000272 def update(self, table, data, where, commit = None):
mbligh414c69e2007-10-05 15:13:06 +0000273 """\
274 'update table set data values (%s ... %s) where ...'
275
276 data:
277 dictionary of fields and data
278 """
mbligh432bad42007-10-09 19:56:07 +0000279 if commit == None:
280 commit = self.autocommit
mbligh414c69e2007-10-05 15:13:06 +0000281 cmd = 'update %s ' % table
282 fields = data.keys()
283 data_refs = [field + '=%s' for field in fields]
284 data_values = [data[field] for field in fields]
285 cmd += ' set ' + ' and '.join(data_refs)
286
287 where_keys = [field + '=%s' for field in where.keys()]
288 where_values = [where[field] for field in where.keys()]
289 cmd += ' where ' + ' and '.join(where_keys)
290
mbligh96cf0512008-04-17 15:25:38 +0000291 values = data_values + where_values
292 print '%s %s' % (cmd, values)
293
294 self._exec_sql_with_commit(cmd, values, commit)
mbligh414c69e2007-10-05 15:13:06 +0000295
296
mbligh96b9a5a2007-11-24 19:32:20 +0000297 def delete_job(self, tag, commit = None):
298 job_idx = self.find_job(tag)
299 for test_idx in self.find_tests(job_idx):
300 where = {'test_idx' : test_idx}
301 self.delete('iteration_result', where)
302 self.delete('test_attributes', where)
303 where = {'job_idx' : job_idx}
304 self.delete('tests', where)
305 self.delete('jobs', where)
306
307
mbligh432bad42007-10-09 19:56:07 +0000308 def insert_job(self, tag, job, commit = None):
mbligh2aaeb672007-10-01 14:54:18 +0000309 job.machine_idx = self.lookup_machine(job.machine)
310 if not job.machine_idx:
mbligh7a41a862007-11-30 17:44:24 +0000311 job.machine_idx = self.insert_machine(job,
mbligh432bad42007-10-09 19:56:07 +0000312 commit=commit)
mblighb10a60f2007-10-17 00:03:32 +0000313 self.insert('jobs', {'tag':tag,
314 'label': job.label,
mbligh05067a32007-12-03 17:48:04 +0000315 'username': job.user,
mbligh26b992b2008-02-19 15:46:21 +0000316 'machine_idx': job.machine_idx,
317 'queued_time': job.queued_time,
318 'started_time': job.started_time,
319 'finished_time': job.finished_time},
mblighb10a60f2007-10-17 00:03:32 +0000320 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000321 job.index = self.get_last_autonumber_value()
mbligh608c3252007-08-31 13:53:00 +0000322 for test in job.tests:
mbligh432bad42007-10-09 19:56:07 +0000323 self.insert_test(job, test, commit=commit)
mbligh608c3252007-08-31 13:53:00 +0000324
mbligh96b9a5a2007-11-24 19:32:20 +0000325
mbligh432bad42007-10-09 19:56:07 +0000326 def insert_test(self, job, test, commit = None):
327 kver = self.insert_kernel(test.kernel, commit=commit)
mbligh608c3252007-08-31 13:53:00 +0000328 data = {'job_idx':job.index, 'test':test.testname,
mbligh2bd48872007-09-20 18:32:25 +0000329 'subdir':test.subdir, 'kernel_idx':kver,
mbligh8e1ab172007-09-13 17:29:56 +0000330 'status':self.status_idx[test.status],
mblighc2514542008-02-19 15:54:26 +0000331 'reason':test.reason, 'machine_idx':job.machine_idx,
mbligh9cae1502008-04-18 20:40:45 +0000332 'started_time': test.started_time,
mblighc2514542008-02-19 15:54:26 +0000333 'finished_time':test.finished_time}
mbligh432bad42007-10-09 19:56:07 +0000334 self.insert('tests', data, commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000335 test_idx = self.get_last_autonumber_value()
mbligh2bd48872007-09-20 18:32:25 +0000336 data = { 'test_idx':test_idx }
337
338 for i in test.iterations:
339 data['iteration'] = i.index
jadmanskicc549172008-05-21 18:11:51 +0000340 for key, value in i.attr_keyval.iteritems():
mbligh2bd48872007-09-20 18:32:25 +0000341 data['attribute'] = key
jadmanskicc549172008-05-21 18:11:51 +0000342 data['value'] = value
343 self.insert('iteration_attributes', data,
344 commit=commit)
345 for key, value in i.perf_keyval.iteritems():
346 data['attribute'] = key
347 data['value'] = value
348 self.insert('iteration_result', data,
349 commit=commit)
mbligh96cf0512008-04-17 15:25:38 +0000350
351 for key, value in test.attributes.iteritems():
jadmanskicc549172008-05-21 18:11:51 +0000352 data = {'test_idx': test_idx, 'attribute': key,
353 'value': value}
mbligh96cf0512008-04-17 15:25:38 +0000354 self.insert('test_attributes', data, commit=commit)
mblighe9cf9d42007-08-31 08:56:00 +0000355
356
mbligh048e1c92007-10-07 00:10:33 +0000357 def read_machine_map(self):
358 self.machine_group = {}
359 for line in open(self.machine_map, 'r').readlines():
360 (machine, group) = line.split()
361 self.machine_group[machine] = group
362
363
mbligh7a41a862007-11-30 17:44:24 +0000364 def insert_machine(self, job, group = None, commit = None):
365 hostname = job.machine
mbligh048e1c92007-10-07 00:10:33 +0000366 if self.machine_map and not self.machine_group:
367 self.read_machine_map()
368
369 if not group:
370 group = self.machine_group.get(hostname, hostname)
mbligh7a41a862007-11-30 17:44:24 +0000371 if group == hostname and job.machine_owner:
372 group = job.machine_owner + '/' + hostname
373
mbligh432bad42007-10-09 19:56:07 +0000374 self.insert('machines',
375 { 'hostname' : hostname ,
mbligh7a41a862007-11-30 17:44:24 +0000376 'machine_group' : group ,
377 'owner' : job.machine_owner },
mbligh432bad42007-10-09 19:56:07 +0000378 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000379 return self.get_last_autonumber_value()
mbligh2aaeb672007-10-01 14:54:18 +0000380
381
382 def lookup_machine(self, hostname):
383 where = { 'hostname' : hostname }
384 rows = self.select('machine_idx', 'machines', where)
385 if rows:
386 return rows[0][0]
387 else:
388 return None
389
390
mbligh9bb92fe2007-09-12 15:54:23 +0000391 def lookup_kernel(self, kernel):
392 rows = self.select('kernel_idx', 'kernels',
mbligh048e1c92007-10-07 00:10:33 +0000393 {'kernel_hash':kernel.kernel_hash})
mbligh237bed32007-09-05 13:05:57 +0000394 if rows:
395 return rows[0][0]
396 else:
397 return None
mblighe9cf9d42007-08-31 08:56:00 +0000398
399
mbligh432bad42007-10-09 19:56:07 +0000400 def insert_kernel(self, kernel, commit = None):
mbligh9bb92fe2007-09-12 15:54:23 +0000401 kver = self.lookup_kernel(kernel)
mbligh237bed32007-09-05 13:05:57 +0000402 if kver:
403 return kver
apw7a7316b2008-02-21 17:42:05 +0000404
405 # If this kernel has any significant patches, append their hash
406 # as diferentiator.
407 printable = kernel.base
408 patch_count = 0
409 for patch in kernel.patches:
410 match = re.match(r'.*(-mm[0-9]+|-git[0-9]+)\.(bz2|gz)$',
411 patch.reference)
412 if not match:
413 patch_count += 1
414
mbligh432bad42007-10-09 19:56:07 +0000415 self.insert('kernels',
416 {'base':kernel.base,
417 'kernel_hash':kernel.kernel_hash,
apw7a7316b2008-02-21 17:42:05 +0000418 'printable':printable},
mbligh432bad42007-10-09 19:56:07 +0000419 commit=commit)
mblighe12b8612008-02-12 20:58:14 +0000420 kver = self.get_last_autonumber_value()
apw7a7316b2008-02-21 17:42:05 +0000421
422 if patch_count > 0:
423 printable += ' p%d' % (kver)
424 self.update('kernels',
425 {'printable':printable},
426 {'kernel_idx':kver})
427
mbligh237bed32007-09-05 13:05:57 +0000428 for patch in kernel.patches:
mbligh432bad42007-10-09 19:56:07 +0000429 self.insert_patch(kver, patch, commit=commit)
mbligh237bed32007-09-05 13:05:57 +0000430 return kver
431
432
mbligh432bad42007-10-09 19:56:07 +0000433 def insert_patch(self, kver, patch, commit = None):
mbligh237bed32007-09-05 13:05:57 +0000434 print patch.reference
435 name = os.path.basename(patch.reference)[:80]
mbligh432bad42007-10-09 19:56:07 +0000436 self.insert('patches',
437 {'kernel_idx': kver,
438 'name':name,
439 'url':patch.reference,
440 'hash':patch.hash},
441 commit=commit)
mbligh056d0d32006-10-08 22:31:10 +0000442
mbligh048e1c92007-10-07 00:10:33 +0000443
mbligh2bd48872007-09-20 18:32:25 +0000444 def find_test(self, job_idx, subdir):
445 where = { 'job_idx':job_idx , 'subdir':subdir }
446 rows = self.select('test_idx', 'tests', where)
447 if rows:
448 return rows[0][0]
449 else:
450 return None
451
mbligh056d0d32006-10-08 22:31:10 +0000452
mbligh96b9a5a2007-11-24 19:32:20 +0000453 def find_tests(self, job_idx):
454 where = { 'job_idx':job_idx }
455 rows = self.select('test_idx', 'tests', where)
456 if rows:
457 return [row[0] for row in rows]
458 else:
mbligh2e57e7b2007-11-29 16:10:50 +0000459 return []
mbligh96b9a5a2007-11-24 19:32:20 +0000460
461
mbligh056d0d32006-10-08 22:31:10 +0000462 def find_job(self, tag):
mbligh608c3252007-08-31 13:53:00 +0000463 rows = self.select('job_idx', 'jobs', {'tag': tag})
464 if rows:
465 return rows[0][0]
466 else:
467 return None
mblighaf25f062007-12-03 17:48:35 +0000468
469
mbligh96cf0512008-04-17 15:25:38 +0000470def _get_db_type():
471 """Get the database type name to use from the global config."""
472 get_value = global_config.global_config.get_config_value
473 return "db_" + get_value("TKO", "db_type", default="mysql")
mblighaf25f062007-12-03 17:48:35 +0000474
mbligh96cf0512008-04-17 15:25:38 +0000475
476def _get_error_class(class_name):
477 """Retrieves the appropriate error class by name from the database
478 module."""
479 db_module = __import__("autotest_lib.tko." + _get_db_type(),
480 globals(), locals(), ["driver"])
481 return getattr(db_module.driver, class_name)
482
483
484def db(*args, **dargs):
485 """Creates an instance of the database class with the arguments
486 provided in args and dargs, using the database type specified by
487 the global configuration (defaulting to mysql)."""
488 db_type = _get_db_type()
489 db_module = __import__("autotest_lib.tko." + db_type, globals(),
490 locals(), [db_type])
491 db = getattr(db_module, db_type)(*args, **dargs)
mblighaf25f062007-12-03 17:48:35 +0000492 return db