summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2014-09-26 17:41:04 -0400
committerPatrick Uiterwijk <puiterwijk@redhat.com>2014-10-06 20:55:08 +0200
commit086bb2e420fbf4f0fe7fd0ec4667737c063f2e0e (patch)
tree41587b383c80727c7de7c70fda628659aad163d7
parenta6ed2bba137df5fb8a9fb2931ccb2d92ca3fa0e0 (diff)
downloadipsilon-086bb2e420fbf4f0fe7fd0ec4667737c063f2e0e.tar.gz
ipsilon-086bb2e420fbf4f0fe7fd0ec4667737c063f2e0e.tar.xz
ipsilon-086bb2e420fbf4f0fe7fd0ec4667737c063f2e0e.zip
Additional data store refactoring
Use sqlalchemy to access Sql databases, which are the only implemented database backends for now. If no database type is specified we assume a sqlite3 database file path is configured (this is backwards compatible with current configuration statements) Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
-rwxr-xr-xipsilon/util/data.py323
1 files changed, 153 insertions, 170 deletions
diff --git a/ipsilon/util/data.py b/ipsilon/util/data.py
index e6bca10..78fa5ab 100755
--- a/ipsilon/util/data.py
+++ b/ipsilon/util/data.py
@@ -17,9 +17,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import sqlite3
import cherrypy
from ipsilon.util.log import Log
+from sqlalchemy import create_engine
+from sqlalchemy import MetaData, Table, Column, Text
+from sqlalchemy.sql import select
import uuid
@@ -27,81 +29,105 @@ OPTIONS_COLUMNS = ['name', 'option', 'value']
UNIQUE_DATA_COLUMNS = ['uuid', 'name', 'value']
+class SqlStore(Log):
+
+ def __init__(self, name):
+ if name not in cherrypy.config:
+ raise NameError('Unknown database %s' % name)
+ engine_name = cherrypy.config[name]
+ if '://' not in engine_name:
+ engine_name = 'sqlite:///' + engine_name
+ self._dbengine = create_engine(engine_name)
+
+ def engine(self):
+ return self._dbengine
+
+ def connection(self):
+ return self._dbengine.connect()
+
+
+def SqlAutotable(f):
+ def at(self, *args, **kwargs):
+ if self.autotable:
+ self.create()
+ return f(self, *args, **kwargs)
+ return at
+
+
+class SqlQuery(Log):
+
+ def __init__(self, db_obj, table, columns, autotable=True, trans=True):
+ self._db = db_obj
+ self.autotable = autotable
+ self._con = self._db.connection()
+ self._trans = self._con.begin() if trans else None
+ self._table = self._get_table(table, columns)
+
+ def _get_table(self, name, columns):
+ table = Table(name, MetaData(self._db.engine()))
+ for c in columns:
+ table.append_column(Column(c, Text()))
+ return table
+
+ def _where(self, kvfilter):
+ where = None
+ if kvfilter is not None:
+ for k in kvfilter:
+ w = self._table.columns[k] == kvfilter[k]
+ if where is None:
+ where = w
+ else:
+ where = where & w
+ return where
+
+ def _columns(self, columns=None):
+ cols = None
+ if columns is not None:
+ cols = []
+ for c in columns:
+ cols.append(self._table.columns[c])
+ else:
+ cols = self._table.columns
+ return cols
+
+ def rollback(self):
+ self._trans.rollback()
+
+ def commit(self):
+ self._trans.commit()
+
+ def create(self):
+ self._table.create(checkfirst=True)
+
+ def drop(self):
+ self._table.drop(checkfirst=True)
+
+ @SqlAutotable
+ def select(self, kvfilter=None, columns=None):
+ return self._con.execute(select(self._columns(columns),
+ self._where(kvfilter)))
+
+ @SqlAutotable
+ def insert(self, values):
+ self._con.execute(self._table.insert(values))
+
+ @SqlAutotable
+ def update(self, values, kvfilter):
+ self._con.execute(self._table.update(self._where(kvfilter), values))
+
+ @SqlAutotable
+ def delete(self, kvfilter):
+ self._con.execute(self._table.delete(self._where(kvfilter)))
+
+
class Store(Log):
def __init__(self, config_name):
- if config_name not in cherrypy.config:
- raise NameError('Unknown database type %s' % config_name)
- self._dbname = cherrypy.config[config_name]
-
- def _build_where(self, kvfilter, kvout):
- where = ""
- sep = "WHERE"
- for k in kvfilter:
- mk = "where_%s" % k
- kvout[mk] = kvfilter[k]
- where += "%s %s=:%s" % (sep, k, mk)
- sep = " AND"
- return where
+ self._db = SqlStore(config_name)
+ self._query = SqlQuery
- def _build_select(self, table, kvfilter=None, kvout=None, columns=None):
- SELECT = "SELECT %(cols)s FROM %(table)s %(where)s"
- cols = "*"
- if columns:
- cols = ",".join(columns)
- where = ""
- if kvfilter is not None:
- where = self._build_where(kvfilter, kvout)
- return SELECT % {'table': table, 'cols': cols, 'where': where}
-
- def _select(self, cursor, table, kvfilter=None, columns=None):
- kv = dict()
- select = self._build_select(table, kvfilter, kv, columns)
- cursor.execute(select, kv)
- return cursor.fetchall()
-
- def _create(self, cursor, table, columns):
- CREATE = "CREATE TABLE IF NOT EXISTS %(table)s(%(cols)s)"
- cols = ",".join(columns)
- create = CREATE % {'table': table, 'cols': cols}
- cursor.execute(create)
-
- def _drop(self, cursor, table):
- cursor.execute("DROP TABLE IF EXISTS " + table)
-
- def _update(self, cursor, table, values, kvfilter):
- UPDATE = "UPDATE %(table)s SET %(setval)s %(where)s"
- kv = dict()
-
- setval = ""
- sep = ""
- for k in values:
- mk = "setval_%s" % k
- kv[mk] = values[k]
- setval += "%s%s=:%s" % (sep, k, mk)
- sep = " , "
-
- where = self._build_where(kvfilter, kv)
-
- update = UPDATE % {'table': table, 'setval': setval, 'where': where}
- cursor.execute(update, kv)
-
- def _insert(self, cursor, table, values):
- INSERT = "INSERT INTO %(table)s VALUES(%(values)s)"
- vals = ""
- sep = ""
- for _ in values:
- vals += "%s?" % sep
- sep = ","
- insert = INSERT % {'table': table, 'values': vals}
- cursor.execute(insert, values)
-
- def _delete(self, cursor, table, kvfilter):
- DELETE = "DELETE FROM %(table)s %(where)s"
- kv = dict()
- where = self._build_where(kvfilter, kv)
- delete = DELETE % {'table': table, 'where': where}
- cursor.execute(delete, kv)
+ def new_query(self, table, columns=None, autotable=True, autocommit=True):
+ return self._query(self._db, table, columns, autotable, autocommit)
def _row_to_dict_tree(self, data, row):
name = row[0]
@@ -127,109 +153,83 @@ class Store(Log):
self._row_to_dict_tree(data, r)
return data
- def _load_data(self, table, columns, kvfilter=None):
- con = None
+ def load_data(self, table, columns, kvfilter=None):
rows = []
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._create(cur, table, columns)
- rows = self._select(cur, table, kvfilter)
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
+ q = self._query(self._db, table, columns, trans=False)
+ rows = q.select(kvfilter)
+ except Exception, e: # pylint: disable=broad-except
self.error("Failed to load data for table %s: [%s]" % (table, e))
- finally:
- if con:
- con.close()
-
return self._rows_to_dict_tree(rows)
def load_config(self):
table = 'config'
columns = ['name', 'value']
- return self._load_data(table, columns)
+ return self.load_data(table, columns)
def load_options(self, table, name=None):
kvfilter = dict()
if name:
kvfilter['name'] = name
- options = self._load_data(table, OPTIONS_COLUMNS, kvfilter)
+ options = self.load_data(table, OPTIONS_COLUMNS, kvfilter)
if name and name in options:
return options[name]
return options
def save_options(self, table, name, options):
curvals = dict()
- con = None
+ q = None
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._create(cur, table, OPTIONS_COLUMNS)
- rows = self._select(cur, table, {'name': name},
- ['option', 'value'])
+ q = self._query(self._db, table, OPTIONS_COLUMNS)
+ rows = q.select({'name': name}, ['option', 'value'])
for row in rows:
curvals[row[0]] = row[1]
for opt in options:
if opt in curvals:
- self._update(cur, table,
- {'value': options[opt]},
- {'name': name, 'option': opt})
+ q.update({'value': options[opt]},
+ {'name': name, 'option': opt})
else:
- self._insert(cur, table, (name, opt, options[opt]))
+ q.insert((name, opt, options[opt]))
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
- self.error("Failed to store config: [%s]" % e)
+ q.commit()
+ except Exception, e: # pylint: disable=broad-except
+ if q:
+ q.rollback()
+ self.error("Failed to save options: [%s]" % e)
raise
- finally:
- if con:
- con.close()
def delete_options(self, table, name, options=None):
kvfilter = {'name': name}
+ q = None
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._create(cur, table, OPTIONS_COLUMNS)
+ q = self._query(self._db, table, OPTIONS_COLUMNS)
if options is None:
- self._delete(cur, table, kvfilter)
+ q.delete(kvfilter)
else:
for opt in options:
kvfilter['option'] = opt
- self._delete(cur, table, kvfilter)
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
+ q.delete(kvfilter)
+ q.commit()
+ except Exception, e: # pylint: disable=broad-except
+ if q:
+ q.rollback()
self.error("Failed to delete from %s: [%s]" % (table, e))
raise
- finally:
- if con:
- con.close()
def new_unique_data(self, table, data):
- con = None
+ newid = str(uuid.uuid4())
+ q = None
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._create(cur, table, UNIQUE_DATA_COLUMNS)
- newid = str(uuid.uuid4())
+ q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
for name in data:
- self._insert(cur, table, (newid, name, data[name]))
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
- cherrypy.log.error("Failed to store %s data: [%s]" % (table, e))
+ q.insert((newid, name, data[name]))
+ q.commit()
+ except Exception, e: # pylint: disable=broad-except
+ if q:
+ q.rollback()
+ self.error("Failed to store %s data: [%s]" % (table, e))
raise
- finally:
- if con:
- con.close()
return newid
def get_unique_data(self, table, uuidval=None, name=None, value=None):
@@ -240,68 +240,51 @@ class Store(Log):
kvfilter['name'] = name
if value:
kvfilter['value'] = value
- return self._load_data(table, UNIQUE_DATA_COLUMNS, kvfilter)
+ return self.load_data(table, UNIQUE_DATA_COLUMNS, kvfilter)
def save_unique_data(self, table, data):
- curvals = dict()
- con = None
+ q = None
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._create(cur, table, UNIQUE_DATA_COLUMNS)
+ q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
for uid in data:
curvals = dict()
- rows = self._select(cur, table, {'uuid': uid},
- ['name', 'value'])
+ rows = q.select({'uuid': uid}, ['name', 'value'])
for r in rows:
curvals[r[0]] = r[1]
datum = data[uid]
for name in datum:
if name in curvals:
- self._update(cur, table,
- {'value': datum[name]},
- {'uuid': uid, 'name': name})
+ q.update({'value': datum[name]},
+ {'uuid': uid, 'name': name})
else:
- self._insert(cur, table, (uid, name, datum[name]))
+ q.insert((uid, name, datum[name]))
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
+ q.commit()
+ except Exception, e: # pylint: disable=broad-except
+ if q:
+ q.rollback()
self.error("Failed to store data in %s: [%s]" % (table, e))
raise
- finally:
- if con:
- con.close()
def del_unique_data(self, table, uuidval):
kvfilter = {'uuid': uuidval}
- con = None
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._delete(cur, table, kvfilter)
- except sqlite3.Error, e:
+ q = self._query(self._db, table, UNIQUE_DATA_COLUMNS, trans=False)
+ q.delete(kvfilter)
+ except Exception, e: # pylint: disable=broad-except
self.error("Failed to delete data from %s: [%s]" % (table, e))
- finally:
- if con:
- con.close()
def reset_data(self, table):
try:
- con = sqlite3.connect(self._dbname)
- cur = con.cursor()
- self._drop(cur, table)
- self._create(cur, table, UNIQUE_DATA_COLUMNS)
- con.commit()
- except sqlite3.Error, e:
- if con:
- con.rollback()
+ q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
+ q.drop()
+ q.create()
+ q.commit()
+ except Exception, e: # pylint: disable=broad-except
+ if q:
+ q.rollback()
self.error("Failed to erase all data from %s: [%s]" % (table, e))
- finally:
- if con:
- con.close()
class AdminStore(Store):