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
