This repository has been archived by the owner on Jan 8, 2020. It is now read-only.
forked from autotest/autotest
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
server installation: introduce autotest-database-turnkey
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
Showing
3 changed files
with
302 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |