On Thu, Dec 27, 2012 at 11:36 AM, Cleber Rosa <[email protected]> wrote:
> On 12/23/2012 02:42 PM, Lucas Meneghel Rodrigues wrote:
>>
>> On 12/21/2012 01:41 PM, Cleber Rosa wrote:
>>>
>>> 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.
>>
>>
>> This is awesome, slowly we're making the install process a lot better. I
>> have some comments below:
>>
>>> 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
>>
>>
>> Catching all exceptions here might mask the fact that there are other
>> problems going on with the database rather than just invalid credentials, as
>> I see. Isn't it better to catch a more specific error and handle other
>> problems differently (maybe even terminating the script, and saying
>> something about the error)?
>
>
> Maybe the name of the function is a bit misleading, but the idea is to check
> if we can connect to the database using the given root password. That
> function is used as a sanity check on other functions (create_database,
> database_setup) and also when calling:
>
> # autotest-database-turnkey --root-password=<password> --check-credentials
>
> And in all these case, a proper error message is displayed and the script is
> terminated.
>
> BTW, I decided not to have many error conditions out of these functions for
> the sole reason that it would complicate the handling by scripts. Currently,
> it exits with either 0 or -1. If you think it'd be necessary or desirable to
> have more fine grained error codes, let me know.
Fair enough, this looks fine, thanks for the explanation.
>
>>
>>> +
>>> +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()
>>
>>
>> ^ What's the purpose of passing locals() to the db command?
>
>
> Because every command is a different line that needs parameter expansion,
> and the parameters are not always the same. Either I had to do that or have
> a more lines of code for individual parameter expansion. I guess it's just a
> matter of style and I'm not sure what's the downside of this approach.
Makes sense, it looks fine.
>
>>
>>> + 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
>>> + '''
>>
>>
>> global_config.ini is a .ini file, that can be parsed and modified using
>> ConfigParser (which seems much simpler and robust), so I didn't quite
>> understand why to do things this way. I'm taking your word that this was
>> really necessary... but I'd like to read some words on the why.
>>
>>
>
> ^ Well, using ConfigParser was the obvious thing to do, and actually the
> first thing that I did. But there's a caveat: ConfigParser won't preserve
> comments and does not keep the original order of sections and key/values.
>
> So, for users of an installation based on the contrib script, they would get
> no comments in the config file, and a completely different file when doing a
> 'git diff'.
Ooooh, I've completely missed that one. Looks great, thanks for the explanation.
_______________________________________________
Autotest-kernel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/autotest-kernel