Database / MySQL API support added (test suite will access DB to store test results)

pull/475/head
Przemek Wirkus 2014-08-18 10:39:43 +01:00
parent f304c6ba83
commit 1e4ac92f2a
2 changed files with 264 additions and 3 deletions

View File

@ -19,9 +19,7 @@ Author: Przemyslaw Wirkus <Przemyslaw.wirkus@arm.com>
import os
import re
import sys
import json
import time
import pprint
import random
import optparse
@ -478,7 +476,7 @@ class SingleTestRunner(object):
if images_config is not None:
# For different targets additional configuration file has to be changed
# Here we select target and proper function to handle configuration change
if target == 'ARM_MPS2':
if target_name == 'ARM_MPS2':
images_cfg_path = images_config
image0file_path = os.path.join(disk, image_dest, basename(image_path))
mps2_set_board_image_file(disk, images_cfg_path, image0file_path)
@ -1166,6 +1164,87 @@ class CLITestLogger(TestLogger):
pass
return log_line_str
class BaseDBAccess():
""" Class used to connect with test database and store test results
"""
def __init__(self):
self.db_object = None
self.TABLE_BUILD_ID = 'mtest_build_id'
self.TABLE_TARGET = 'mtest_target'
self.TABLE_TEST_ENTRY = 'mtest_test_entry'
self.TABLE_TEST_ID = 'mtest_test_id'
self.TABLE_TEST_RESULT = 'mtest_test_result'
self.TABLE_TEST_TYPE = 'mtest_test_type'
self.TABLE_TOOLCHAIN = 'mtest_toolchain'
def parse_db_connection_string(self, str):
""" Parsing SQL DB connection string. String should contain:
- DB Name, user name, password, URL (DB host), name
Function should return tuple with parsed (host, user, passwd, db) or None if error
E.g. connection string: 'mysql://buildbot:buildbot@127.0.0.1/buildbot'
"""
PATTERN = '^([\w]+)://([\w]+):([\w]*)@(.*)/([\w]+)'
result = re.match(PATTERN, str)
if result is not None:
result = result.groups() # Tuple (db_name, host, user, passwd, db)
return result
def is_connected(self):
""" Returns True if we are connected to database
"""
pass
def connect(self, host, user, passwd, db):
""" Connects to DB and returns DB object
"""
pass
def disconnect(self):
""" Close DB connection
"""
pass
def escape_string(self, str):
""" Escapes string so it can be put in SQL query between quotes
"""
pass
def select_all(self, query):
""" Execute SELECT query and get all results
"""
pass
def insert(self, query, commit=True):
""" Execute INSERT query, define if you want to commit
"""
pass
def get_next_build_id(self, name, desc=''):
""" Insert new build_id (DB unique build like ID number to send all test results)
"""
pass
def get_table_entry_pk(self, table, column, value, update_db=True):
""" Checks for entries in tables with two columns (<TABLE_NAME>_pk, <column>)
If update_db is True updates table entry if value in specified column doesn't exist
"""
pass
def update_table_entry(self, table, column, value):
""" Updates table entry if value in specified column doesn't exist
Locks table to perform atomic read + update
"""
pass
def insert_test_entry(self, next_build_id, target, toolchain, test_type, test_id, test_result, test_time, test_timeout, test_loop, test_extra=''):
""" Inserts test result entry to database. All checks regarding existing
toolchain names in DB are performed.
If some data is missing DB will be updated
"""
pass
def get_default_test_options_parser():
""" Get common test script options used by CLI, webservices etc.
"""

View File

@ -0,0 +1,182 @@
"""
mbed SDK
Copyright (c) 2011-2014 ARM Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: Przemyslaw Wirkus <Przemyslaw.wirkus@arm.com>
"""
import re
import MySQLdb as mdb
# Imports from TEST API
from workspace_tools.test_api import BaseDBAccess
class MySQLDBAccess(BaseDBAccess):
""" Wrapper for MySQL DB access for common test suite interface
"""
def __init__(self):
BaseDBAccess.__init__(self)
def parse_db_connection_string(self, str):
""" Parsing SQL DB connection string. String should contain:
- DB Name, user name, password, URL (DB host), name
Function should return tuple with parsed (host, user, passwd, db) or None if error
E.g. connection string: 'mysql://buildbot:buildbot@127.0.0.1/buildbot'
"""
result = BaseDBAccess.parse_db_connection_string(str)
if result is not None and result[0] != 'mysql':
result = None
return result
def is_connected(self):
""" Returns True if we are connected to database
"""
return self.db_object is not None
def connect(self, host, user, passwd, db):
""" Connects to DB and returns DB object
"""
try:
self.db_object = mdb.connect(host=host, user=user, passwd=passwd, db=db)
except mdb.Error, e:
print "Error %d: %s"% (e.args[0], e.args[1])
self.db_object = None
def disconnect(self):
""" Close DB connection
"""
if self.db_object:
self.db_object.close()
def escape_string(self, str):
""" Escapes string so it can be put in SQL query between quotes
"""
con = self.db_object
result = con.escape_string(str)
return result if result else ''
def select_all(self, query):
""" Execute SELECT query and get all results
"""
con = self.db_object
cur = con.cursor(mdb.cursors.DictCursor)
cur.execute(query)
rows = cur.fetchall()
return rows
def insert(self, query, commit=True):
""" Execute INSERT query, define if you want to commit
"""
con = self.db_object
cur = con.cursor()
cur.execute(query)
if commit:
con.commit()
return cur.lastrowid
def get_next_build_id(self, name, desc=''):
""" Insert new build_id (DB unique build like ID number to send all test results)
"""
query = """INSERT INTO `%s` (%s_name, %s_desc)
VALUES ('%s', '%s')"""% (self.TABLE_BUILD_ID,
self.TABLE_BUILD_ID,
self.TABLE_BUILD_ID,
self.escape_string(name),
self.escape_string(desc))
index = self.insert(query) # Provide inserted record PK
return index
def get_table_entry_pk(self, table, column, value, update_db=True):
""" Checks for entries in tables with two columns (<TABLE_NAME>_pk, <column>)
If update_db is True updates table entry if value in specified column doesn't exist
"""
# TODO: table buffering
result = None
table_pk = '%s_pk'% table
query = """SELECT `%s`
FROM `%s`
WHERE `%s`='%s'"""% (table_pk, table, column, self.escape_string(value))
rows = self.select_all(query)
if len(rows) == 1:
result = rows[0][table_pk]
elif len(rows) == 0 and update_db:
# Update DB with new value
result = self.update_table_entry(table, column, value)
return result
def update_table_entry(self, table, column, value):
""" Updates table entry if value in specified column doesn't exist
Locks table to perform atomic read + update
"""
result = None
con = self.db_object
cur = con.cursor()
cur.execute("LOCK TABLES `%s` WRITE"% table)
table_pk = '%s_pk'% table
query = """SELECT `%s`
FROM `%s`
WHERE `%s`='%s'"""% (table_pk, table, column, self.escape_string(value))
cur.execute(query)
rows = cur.fetchall()
if len(rows) == 0:
query = """INSERT INTO `%s` (%s)
VALUES ('%s')"""% (table, column, self.escape_string(value))
cur.execute(query)
result = cur.lastrowid
con.commit()
cur.execute("UNLOCK TABLES")
return result
def insert_test_entry(self, next_build_id, target, toolchain, test_type, test_id, test_result, test_time, test_timeout, test_loop, test_extra=''):
""" Inserts test result entry to database. All checks regarding existing
toolchain names in DB are performed.
If some data is missing DB will be updated
"""
# Get all table FK and if entry is new try to insert new value
target_fk = self.get_table_entry_pk(self.TABLE_TARGET, self.TABLE_TARGET + '_name', target)
toolchain_fk = self.get_table_entry_pk(self.TABLE_TOOLCHAIN, self.TABLE_TOOLCHAIN + '_name', toolchain)
test_type_fk = self.get_table_entry_pk(self.TABLE_TEST_TYPE, self.TABLE_TEST_TYPE + '_name', test_type)
test_id_fk = self.get_table_entry_pk(self.TABLE_TEST_ID, self.TABLE_TEST_ID + '_name', test_id)
test_result_fk = self.get_table_entry_pk(self.TABLE_TEST_RESULT, self.TABLE_TEST_RESULT + '_name', test_result)
con = self.db_object
cur = con.cursor()
query = """ INSERT INTO `%s` (`mtest_build_id_fk`,
`mtest_target_fk`,
`mtest_toolchain_fk`,
`mtest_test_type_fk`,
`mtest_test_id_fk`,
`mtest_test_result_fk`,
`mtest_test_time`,
`mtest_test_timeout`,
`mtest_test_loop_no`,
`mtest_test_result_extra`)
VALUES (%d, %d, %d, %d, %d, %d, %.2f, %.2f, %d, '%s')"""% (self.TABLE_TEST_ENTRY,
next_build_id,
target_fk,
toolchain_fk,
test_type_fk,
test_id_fk,
test_result_fk,
test_time,
test_timeout,
test_loop,
self.escape_string(test_extra))
cur.execute(query)
con.commit()