Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
server installation: introduce autotest-database-turnkey
Browse files Browse the repository at this point in the history
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.

Changes from v1:
 * Use the global_config module AS-IS, reloading it completely.

Signed-off-by: Cleber Rosa <crosa@redhat.com>
  • Loading branch information
clebergnu committed Dec 27, 2012
1 parent 67b021b commit f355fdf
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 41 deletions.
50 changes: 9 additions & 41 deletions contrib/install-autotest-server.sh
Expand Up @@ -324,27 +324,25 @@ 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
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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
279 changes: 279 additions & 0 deletions 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()
reload(global_config)
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)
14 changes: 14 additions & 0 deletions 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")

0 comments on commit f355fdf

Please sign in to comment.