On 02/25/2013 10:37 AM, Cleber Rosa wrote:
A wrapper for managing database instances. Currently implementations
for managing database instances exist for:
* MySQL
* PostgreSQL
^ A comment here: I decided to pull the PostgreSQL support, since it
was another thing to support. This will be added later once this series
is merged and stable.
So, disregard the commit message about PostgreSQL support.
* SQLite (via "Dummy" manager)
Signed-off-by: Cleber Rosa <[email protected]>
---
installation_support/database_manager/__init__.py | 74 ++++++++++++
installation_support/database_manager/base.py | 93 +++++++++++++++
installation_support/database_manager/dummy.py | 45 ++++++++
installation_support/database_manager/mysql.py | 132 ++++++++++++++++++++++
4 files changed, 344 insertions(+)
create mode 100644 installation_support/__init__.py
create mode 100644 installation_support/database_manager/__init__.py
create mode 100644 installation_support/database_manager/base.py
create mode 100644 installation_support/database_manager/dummy.py
create mode 100644 installation_support/database_manager/mysql.py
diff --git a/installation_support/__init__.py b/installation_support/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/installation_support/database_manager/__init__.py
b/installation_support/database_manager/__init__.py
new file mode 100644
index 0000000..8062333
--- /dev/null
+++ b/installation_support/database_manager/__init__.py
@@ -0,0 +1,74 @@
+"""
+Abstraction layer for managing different database systems
+
+This is not a reinvention or reimplementation of Python's DB API, but a simple
+API for for creating, populating and removing instances transparently.
+"""
+
+import logging
+
+from autotest.client.shared import settings
+
+from mysql import MySQLDatabaseManager
+from dummy import DummyDatabaseManager
+
+
+#
+# FIXME: this should not be a static registry assigment but a dynamic import
+# because the way it's done now, it requires all python database interface
+# modules to be present
+#
+RDBMS_REGISTRY = {'mysql': MySQLDatabaseManager}
+
+
+def get_manager_class(rdbms_type):
+ '''
+ Returns a manager class for a given RDBMS type
+ '''
+ klass = RDBMS_REGISTRY.get(rdbms_type, None)
+ if klass is None:
+ logging.info('There\'s no database manager specific for "%s"',
+ rdbms_type)
+ klass = DummyDatabaseManager
+ return klass
+
+
+def engine_to_rdbms_type(django_engine):
+ '''
+ Returns a RDBMS type for a given django engine name
+ '''
+ rdbms_type = django_engine.split('.')[-1]
+
+ if rdbms_type == 'afe':
+ rdbms_type = 'mysql'
+ elif rdbms_type.startswith('afe_'):
+ rdbms_type = rdbms_type[4:]
+ return rdbms_type
+
+
+def get_manager_class_from_engine(django_engine):
+ '''
+ Returns a manager class for a given django engine
+ '''
+ rdbms_type = engine_to_rdbms_type(django_engine)
+ return get_manager_class(rdbms_type)
+
+
+def get_manager_from_config(admin_password=None, rdbms_type=None):
+ '''
+ Returns a manager instance from the information on the configuration file
+ '''
+ sect = 'AUTOTEST_WEB'
+ name = settings.settings.get_value(sect, 'database')
+ user = settings.settings.get_value(sect, 'user')
+ password = settings.settings.get_value(sect, 'password')
+ host = settings.settings.get_value(sect, 'host')
+
+ if rdbms_type is None:
+ rdbms_type = settings.settings.get_value(sect, 'db_type')
+
+ klass = get_manager_class(rdbms_type)
+ manager = klass(name, admin_password=admin_password, user=user,
+ password=password, host=host)
+
+ return manager
diff --git a/installation_support/database_manager/base.py
b/installation_support/database_manager/base.py
new file mode 100644
index 0000000..d45cc23
--- /dev/null
+++ b/installation_support/database_manager/base.py
@@ -0,0 +1,93 @@
+"""
+Abstraction layer for managing different database systems
+
+This is not a reinvention or reimplementation of Python's DB API, but a simple
+API for for creating, populating and removing instances transparently.
+
+This module implements the base interface that database specific modules should
+implement.
+"""
+
+
+class BaseDatabaseManager(object):
+ '''
+ Base class for mananging database instances
+
+ Different RDMS have different ways of crearing instances, checking for
+ their existance, etc.
+ '''
+ def __init__(self, name, admin=None, admin_password=None, user=None,
+ password=None, host=None):
+ '''
+ Creates a new instance
+ '''
+ self.name = name
+
+ self.admin = admin
+
+ if admin_password is None:
+ self.admin_password = ''
+ else:
+ self.admin_password = admin_password
+
+ if user is not None:
+ self.user = user
+ else:
+ self.user = self.admin
+
+ if password is not None:
+ self.password = password
+ else:
+ self.password = admin_password
+
+ self.host = host
+
+
+ def config_as_dict(self):
+ '''
+ Returns relevant database configuration as a dictionary
+ '''
+ return {'name' : self.name,
+ 'admin' : self.admin,
+ 'admin_password' : self.admin_password,
+ 'user' : self.user,
+ 'password' : self.password,
+ 'host' : self.host}
+
+
+ def exists(self):
+ '''
+ Checks if the database instance exists
+ '''
+ raise NotImplementedError
+
+
+ def admin_credentials_valid(self):
+ '''
+ Checks if the admin user credentials are valid system wide
+
+ What this means is that we won't try to connect to a specific database
+ instance, but to the RDBMS as a whole (where appropriate)
+ '''
+ raise NotImplementedError
+
+
+ def create_instance(self):
+ '''
+ Creates the database instance
+ '''
+ raise NotImplementedError
+
+
+ def grant_privileges(self):
+ '''
+ Grants necessary privileges to the database user
+ '''
+ return True
+
+
+ def setup(self):
+ '''
+ Performs all the steps neede to completely setup a database instance
+ '''
+ raise NotImplementedError
diff --git a/installation_support/database_manager/dummy.py
b/installation_support/database_manager/dummy.py
new file mode 100644
index 0000000..8012ef3
--- /dev/null
+++ b/installation_support/database_manager/dummy.py
@@ -0,0 +1,45 @@
+"""
+Dummy implementation of the database manager interface
+"""
+
+import logging
+
+from autotest.installation_support.database_manager import base
+
+
+class DummyDatabaseManager(base.BaseDatabaseManager):
+ '''
+ Dummy class that manages no database
+ '''
+ def exists(self):
+ '''
+ Checks if the database instance exists
+ '''
+ return False
+
+
+ def admin_credentials_valid(self):
+ '''
+ Checks if the admin user credentials are valid system wide
+
+ What this means is that we won't try to connect to a specific database
+ instance, but to the RDBMS as a whole (where appropriate)
+ '''
+ return True
+
+
+ def create_instance(self):
+ '''
+ Creates the database instance
+ '''
+ return True
+
+
+ def grant_privileges(self):
+ return True
+
+ def setup(self):
+ '''
+ Performs all the steps neede to completely setup a database instance
+ '''
+ return True
diff --git a/installation_support/database_manager/mysql.py
b/installation_support/database_manager/mysql.py
new file mode 100644
index 0000000..258029a
--- /dev/null
+++ b/installation_support/database_manager/mysql.py
@@ -0,0 +1,132 @@
+"""
+MySQL implementation of the database manager interface
+"""
+
+import logging
+
+import MySQLdb
+
+from autotest.installation_support.database_manager import base
+
+
+class MySQLDatabaseManager(base.BaseDatabaseManager):
+ '''
+ Class that manages MySQL database instances
+ '''
+ def __init__(self, name, admin='root', admin_password=None, user=None,
+ password=None, host=None):
+ '''
+ Creates a new instance
+ '''
+ super(MySQLDatabaseManager, self).__init__(name, admin, admin_password,
+ user, password, host)
+
+ if self.admin_credentials_valid():
+ self.admin_connection = MySQLdb.connect(user=self.admin,
+ passwd=self.admin_password)
+ else:
+ self.admin_connection = None
+ logging.error("Failed to logon as the database admin user")
+
+
+ def exists(self):
+ '''
+ Checks if the database instance exists
+ '''
+ try:
+ MySQLdb.connect(user=self.admin,
+ passwd=self.admin_password,
+ db=self.name,
+ host=self.host)
+ return True
+ except MySQLdb.OperationalError:
+ return False
+
+
+ def admin_credentials_valid(self):
+ '''
+ Checks if the admin user credentials are valid system wide
+
+ What this means is that we won't try to connect to a specific database
+ instance, but to the RDBMS as a whole (where appropriate)
+ '''
+ try:
+ MySQLdb.connect(user=self.admin,
+ passwd=self.admin_password)
+ return True
+ except MySQLdb.OperationalError:
+ return False
+
+
+ def create_instance(self):
+ '''
+ Creates the database instance
+ '''
+ if self.admin_connection is None:
+ return False
+
+ if self.exists():
+ logging.info("Database already exists, doing nothing")
+ return True
+
+ cursor = self.admin_connection.cursor()
+ try:
+ cursor.execute("CREATE DATABASE %s" % self.name)
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError):
+ self.admin_connection.rollback()
+ return False
+
+ return True
+
+
+ def grant_privileges(self):
+ '''
+ Attempts to create database AND users AND set privileges
+ '''
+ grant_cmds = ["grant all privileges on %(name)s.* TO "
+ "'%(user)s'@'localhost' identified by '%(password)s'",
+ "grant SELECT on %(name)s.* TO 'nobody'@'%%'",
+ "grant SELECT on %(name)s.* TO 'nobody'@'localhost'",
+ "FLUSH PRIVILEGES"]
+
+ if self.admin_connection is None:
+ return False
+
+ if not self.exists():
+ return False
+
+ self.admin_connection.begin()
+ cursor = self.admin_connection.cursor()
+
+ for cmd in grant_cmds:
+ cmd_ = cmd % self.config_as_dict()
+ try:
+ cursor.execute(cmd_)
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError):
+ self.admin_connection.rollback()
+ return False
+
+ self.admin_connection.commit()
+ return True
+
+
+ def setup(self):
+ '''
+ Performs all the steps neede to completely setup a database instance
+ '''
+ if self.admin_connection is None:
+ logging.error("Failed to logon as the database admin user")
+ return False
+
+ self.admin_connection.begin()
+
+ if not self.create_instance():
+ self.admin_connection.rollback()
+ return False
+
+ if not self.grant_privileges():
+ self.admin_connection.rollback()
+ return False
+
+ self.admin_connection.commit()
+ return True
_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel