A wrapper for managing database instances. Currently implementations
for managing database instances exist for:

 * MySQL
 * PostgreSQL
 * 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
-- 
1.7.11.7

_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel

Reply via email to