A utility designed to perform a complete autotest database creation
and initialization at once.

Currently, to get an autotest server running one needs to either run
the "install-autotest-server.sh" script, or follow a long list of
steps. This tool will allow users of say, autotest-server distro
specific packages to just run a command and have it setup.

Signed-off-by: Cleber Rosa <[email protected]>
---
 contrib/install-autotest-server.sh             |  50 +----
 installation_support/autotest-database-turnkey | 279 +++++++++++++++++++++++++
 installation_support/common.py                 |  14 ++
 3 files changed, 302 insertions(+), 41 deletions(-)
 create mode 100755 installation_support/autotest-database-turnkey
 create mode 100644 installation_support/common.py

diff --git a/contrib/install-autotest-server.sh 
b/contrib/install-autotest-server.sh
index 83dbe2b..1c2d8c3 100755
--- a/contrib/install-autotest-server.sh
+++ b/contrib/install-autotest-server.sh
@@ -324,11 +324,12 @@ chmod 775 $ATHOME
 }
 
 check_mysql_password() {
-print_log "INFO" "Verifying MySQL root password"
+print_log "INFO" "Setting MySQL root password"
 mysqladmin -u root password $MYSQLPW > /dev/null 2>&1
 
-DB=$(echo "use autotest_web;" | mysql --user=root --password=$MYSQLPW 2>&1)
-if [ "$(echo $DB | grep 'Access denied')" != "" ]
+print_log "INFO" "Verifying MySQL root password"
+$ATHOME/installation_support/autotest-database-turnkey --check-credentials 
--root-password=$MYSQLPW
+if [ $? != 0 ]
 then
     print_log "ERROR" "MySQL already has a different root password"
     exit 1
@@ -336,15 +337,12 @@ fi
 }
 
 create_autotest_database() {
-if [ "$(echo $DB | grep 'Unknown database')" != "" ]
+print_log "INFO" "Creating MySQL databases for autotest"
+$ATHOME/installation_support/autotest-database-turnkey -s 
--root-password=$MYSQLPW -p $MYSQLPW > /dev/null 2>&1
+if [ $? != 0 ]
 then
-    print_log "INFO" "Creating MySQL databases for autotest"
-    cat << SQLEOF | mysql --user=root --password=$MYSQLPW >> $LOG
-create database autotest_web;
-grant all privileges on autotest_web.* TO 'autotest'@'localhost' identified by 
"$MYSQLPW";
-grant SELECT on autotest_web.* TO 'nobody'@'%';
-grant SELECT on autotest_web.* TO 'nobody'@'localhost';
-SQLEOF
+    print_log "ERROR" "Error creating MySQL database"
+    exit 1
 fi
 }
 
@@ -388,32 +386,6 @@ else
 fi
 }
 
-configure_autotest() {
-print_log "INFO" "Setting up the autotest configuration files"
-
-# TODO: notify_email in [SCHEDULER] section of global_config.ini
-
-cat << EOF | su - autotest >> $LOG 2>&1
-/usr/local/bin/substitute please_set_this_password "$MYSQLPW" 
$ATHOME/global_config.ini
-EOF
-}
-
-setup_databse_schema() {
-TABLES=$(echo "use autotest_web; show tables;" | mysql --user=root 
--password=$MYSQLPW 2>&1)
-
-if [ "$(echo $TABLES | grep tko_test_view_outer_joins)" = "" ]
-then
-    print_log "INFO" "Setting up the database schemas"
-    cat << EOF | su - autotest >> $LOG 2>&1
-yes yes | $ATHOME/database/migrate.py --database=AUTOTEST_WEB sync
-yes no | /usr/local/autotest/frontend/manage.py syncdb
-/usr/local/autotest/frontend/manage.py syncdb
-EOF
-else
-    print_log "INFO" "Database schemas are already in place"
-fi
-}
-
 restart_mysql_deb() {
 print_log "INFO" "Re-starting MySQL server"
 service mysql restart >> $LOG
@@ -601,8 +573,6 @@ full_install() {
             create_autotest_database
             build_external_packages
             configure_webserver_rh
-            configure_autotest
-            setup_databse_schema
             restart_mysql_rh
             patch_python27_bug
             build_web_rpc_client
@@ -622,8 +592,6 @@ full_install() {
             create_autotest_database
             build_external_packages
             configure_webserver_deb
-            configure_autotest
-            setup_databse_schema
             restart_mysql_deb
             build_web_rpc_client
             import_tests
diff --git a/installation_support/autotest-database-turnkey 
b/installation_support/autotest-database-turnkey
new file mode 100755
index 0000000..a981b9a
--- /dev/null
+++ b/installation_support/autotest-database-turnkey
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+
+"""
+This script attemps to make it trivial to create the Autotest server database
+and populate with the needed schema, all in one simple command
+"""
+
+
+import os
+import re
+import logging
+import optparse
+
+import MySQLdb
+import django.core.management
+
+try:
+    import autotest.common as common
+except ImportError:
+    import common
+
+from autotest.client.shared import global_config
+from autotest.database.migrate import MigrationManager
+from autotest.database.database_connection import DatabaseConnection
+from autotest.frontend import setup_django_environment
+
+
+DB_ADMIN_USER = 'root'
+
+
+def database_already_exists(database, user, password, hostname='localhost'):
+    '''
+    Detects if the given password exist by trying to connect to it
+    '''
+    try:
+        connection = MySQLdb.connect(user=user, passwd=password, db=database,
+                                     host=hostname)
+        return True
+    except MySQLdb.OperationalError:
+        return False
+
+
+def admin_credentials_valid(password, hostname='localhost'):
+    '''
+    Checks if the database admin (root) credential is valid
+    '''
+    try:
+        connection = MySQLdb.connect(user=DB_ADMIN_USER, passwd=password)
+        return True
+    except:
+        return False
+
+
+def create_database(database, root_password='',
+                    hostname='localhost', conn=None, transaction=False):
+    if conn is None:
+        if not admin_credentials_valid(root_password, hostname):
+            logging.error("Failed to logon as the database admin user")
+            return False
+        conn = MySQLdb.connect(user=DB_ADMIN_USER, passwd=root_password)
+
+    if database_already_exists(database, DB_ADMIN_USER, root_password,
+                               hostname):
+        logging.info("Database already exists, doing nothing")
+        return True
+
+    if transaction:
+        conn.begin()
+
+    curs = conn.cursor()
+    try:
+        curs.execute("CREATE DATABASE %s" % database)
+    except MySQLdb.ProgrammingError, MySQLdb.OperationalError:
+        conn.rollback()
+        return False
+
+    if transaction:
+        conn.commit()
+
+    return True
+
+
+def database_setup(database, user, password, root_password,
+                   hostname='localhost'):
+    '''
+    Attempts to create database AND users AND set privileges
+    '''
+    # To actually give the privileges, we use the root user
+    if not admin_credentials_valid(root_password, hostname):
+        logging.error("Failed to logon as the database admin user")
+        return False
+
+    conn = MySQLdb.connect(user=DB_ADMIN_USER, passwd=root_password)
+    conn.begin()
+    curs = conn.cursor()
+
+    if not create_database(database, hostname=hostname,
+                           root_password=root_password, conn=conn):
+        conn.rollback()
+        return False
+
+
+    GRANT_CMDS = ["grant all privileges on %(database)s.* TO "
+                  "'%(user)s'@'localhost' identified by '%(password)s'",
+                  "grant SELECT on %(database)s.* TO 'nobody'@'%%'",
+                  "grant SELECT on %(database)s.* TO 'nobody'@'localhost'"]
+    for cmd in GRANT_CMDS:
+        cmd_ = cmd % locals()
+        try:
+            curs.execute(cmd_)
+        except:
+            conn.rollback()
+            return False
+
+    conn.commit()
+    return True
+
+
+def set_global_config_value(section, key, value):
+    '''
+    Sets a value on the configuration file
+
+    It does so by reading all lines an rewriting the one needed. This is
+    far from efficient and should only be used to perform changes to a
+    handful of configuration values
+    '''
+    section_found = False
+    section_re = re.compile(r'^\[%s\]$' % section)
+    key_re = re.compile(r'^%s:\s+(.*)$' % key)
+
+    current_lines = open(global_config.global_config.config_file).readlines()
+    output_file = open(global_config.global_config.config_file, 'wb')
+
+    for line in current_lines:
+        if section_re.match(line):
+            section_found = True
+            output_file.write('%s' % line)
+            continue
+
+        if section_found and key_re.match(line):
+            newline = '%s: %s\n' % (key, value)
+            output_file.write(newline)
+            section_found = False
+        else:
+            output_file.write('%s' % line)
+
+    output_file.close()
+    global_config.reload()
+    assert global_config.global_config.get_config_value(section, key) == value
+
+
+class OptionParser(optparse.OptionParser):
+
+    def __init__(self):
+        optparse.OptionParser.__init__(self, usage='Usage: %prog [options]')
+
+        self.add_option('-H', '--host', default='localhost',
+                        help=('Host name or IP address of the database server '
+                              '(defaults to "%default")'))
+
+        self.add_option('-u', '--username', default='autotest',
+                        help=('Name of user that will have read and write '
+                              'access to the database (defaults to '
+                              '"%default")'))
+
+        self.add_option('-p', '--password', default='please_set_this_password',
+                        help=('Password that will be assigned to the user '
+                              'with read and write access to the database '
+                              '(defaults to "%default")'))
+
+        self.add_option('-d', '--database', default='autotest_web',
+                        help=('Name of the database that will be created '
+                              '(defaults to "%default")'))
+
+        self.add_option('--root-password', default='',
+                        help=('The password currently assigned to the '
+                              'database administrator user (defaults '
+                              'to an empty string)'))
+
+        actions_grp = self.add_option_group('ACTIONS')
+        actions_grp.add_option('-s', '--setup', action='store_true',
+                               help=('Perform the complete database setup in '
+                                     'a single action'))
+
+        actions_grp.add_option('--check-credentials', action='store_true',
+                               help=('Check if the database admin password '
+                                     'is valid'))
+
+
+class App(object):
+    def __init__(self):
+        self.option_parser = OptionParser()
+
+
+    def update_config_file(self, opts):
+        '''
+        Update the global config file with values given in the command line
+        '''
+        try:
+            section = 'AUTOTEST_WEB'
+            set_global_config_value(section, 'host', opts.host)
+            set_global_config_value(section, 'database', opts.database)
+            set_global_config_value(section, 'user', opts.username)
+            set_global_config_value(section, 'password', opts.password)
+        except AssertionError:
+            return False
+
+        return True
+
+
+    def run(self):
+        result = False
+        opts, args = self.option_parser.parse_args()
+
+        if opts.check_credentials:
+            result = admin_credentials_valid(opts.root_password, opts.host)
+            if result:
+                return 0
+            else:
+                return -1
+
+        elif opts.setup:
+            # Write the configuration values to the global config file
+            config = self.update_config_file(opts)
+            if not config:
+                logging.error("Failure while setting the config file database "
+                              "values. Please check the current state of your "
+                              "autotest config file.")
+                return -1
+
+            # Perform the creation of the database
+            creation = database_setup(opts.database,
+                                      opts.username,
+                                      opts.password,
+                                      opts.root_password,
+                                      opts.host)
+            if not creation:
+                logging.error("Failure while creating the database "
+                              "and setting privileges")
+                return -1
+
+            # Run the migration manager
+            database = DatabaseConnection()
+            database.connect('mysql',
+                             opts.host,
+                             opts.username,
+                             opts.password,
+                             opts.database)
+            migrations_dir = os.path.join(os.path.dirname(__file__),
+                                          '..', 'frontend', 'migrations')
+            manager = MigrationManager(database,
+                                       migrations_dir=migrations_dir,
+                                       force=True)
+            try:
+                manager._migrate_from_base()
+                manager.do_sync_db(None)
+            except AssertionError:
+                logging.debug('Error while syncing database schema to '
+                              'latest version')
+                return -1
+
+
+            # Finally run Django's syncdb, yes, twice
+            # The current method is suboptimal, we may need to fetch the 
syncdb command module
+            argv = ['manage.py', 'syncdb', '-v0', '--noinput']
+            django.core.management.execute_from_command_line(argv)
+            django.core.management.execute_from_command_line(argv)
+            return 0
+
+        else:
+            self.option_parser.print_help()
+            return 0
+
+
+if __name__ == '__main__':
+    app = App()
+    result = app.run()
+    raise SystemExit(result)
diff --git a/installation_support/common.py b/installation_support/common.py
new file mode 100644
index 0000000..228568b
--- /dev/null
+++ b/installation_support/common.py
@@ -0,0 +1,14 @@
+import os, sys
+try:
+    import autotest.client.setup_modules as setup_modules
+    dirname = os.path.dirname(setup_modules.__file__)
+    autotest_dir = os.path.join(dirname, "..")
+except ImportError:
+    dirname = os.path.dirname(sys.modules[__name__].__file__)
+    autotest_dir = os.path.abspath(os.path.join(dirname, ".."))
+    client_dir = os.path.join(autotest_dir, "client")
+    sys.path.insert(0, client_dir)
+    import setup_modules
+    sys.path.pop(0)
+
+setup_modules.setup(base_path=autotest_dir, root_module_name="autotest")
-- 
1.7.11.7

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

Reply via email to