# HG changeset patch # User Adrien Di Mascio # Date 1364495086 -3600 # Node ID c390c444de06bbd68d5fe2b2480da698a509586e # Parent 67c947b4d2c99dae98945b62603fbd11fa11d0e9 [server] refactor cleanup() code used by "cubicweb-ctl delete" Try to make each logical step (i.e. "delete database" and "delete user") a bit more visible and explicit. This is a preparatory work to easily plug the "delete namespace" feature. diff -r 67c947b4d2c9 -r c390c444de06 server/serverctl.py --- a/server/serverctl.py Mon May 05 11:09:51 2014 +0200 +++ b/server/serverctl.py Thu Mar 28 19:24:46 2013 +0100 @@ -24,6 +24,7 @@ # completion). So import locally in command helpers. import sys import os +from contextlib import contextmanager import logging import subprocess @@ -31,6 +32,8 @@ from logilab.common.configuration import Configuration, merge_options from logilab.common.shellutils import ASK, generate_password +from logilab.database import get_db_helper, get_connection + from cubicweb import AuthenticationError, ExecutionError, ConfigurationError from cubicweb.toolsutils import Command, CommandHandler, underline_title from cubicweb.cwctl import CWCTL, check_options_consistency, ConfigureInstanceCommand @@ -47,7 +50,6 @@ given server.serverconfig """ from getpass import getpass - from logilab.database import get_connection, get_db_helper dbhost = source.get('db-host') if dbname is None: dbname = source['db-name'] @@ -104,7 +106,6 @@ create/drop the instance database) """ if dbms_system_base: - from logilab.database import get_db_helper system_db = get_db_helper(source['db-driver']).system_database() return source_cnx(source, system_db, special_privs=special_privs, interactive=interactive) @@ -116,7 +117,6 @@ database) """ import logilab.common as lgp - from logilab.database import get_db_helper lgp.USE_MX_DATETIME = False driver = source['db-driver'] helper = get_db_helper(driver) @@ -205,56 +205,71 @@ print ('-> nevermind, you can do it later with ' '"cubicweb-ctl db-create %s".' % self.config.appid) -ERROR = nullobject() -def confirm_on_error_or_die(msg, func, *args, **kwargs): +@contextmanager +def db_sys_transaction(source, privilege): + """Open a transaction to the system database""" + cnx = _db_sys_cnx(source, privilege) + cursor = cnx.cursor() try: - return func(*args, **kwargs) - except Exception as ex: - print 'ERROR', ex - if not ASK.confirm('An error occurred while %s. Continue anyway?' % msg): - raise ExecutionError(str(ex)) - return ERROR + yield cursor + except: + cnx.rollback() + cnx.close() + raise + else: + cnx.commit() + cnx.close() + class RepositoryDeleteHandler(CommandHandler): cmdname = 'delete' cfgname = 'repository' + def _drop_database(self, source): + dbname = source['db-name'] + if source['db-driver'] == 'sqlite': + print 'deleting database file %(db-name)s' % source + os.unlink(source['db-name']) + print '-> database %(db-name)s dropped.' % source + else: + helper = get_db_helper(source['db-driver']) + with db_sys_transaction(source, privilege='DROP DATABASE') as cursor: + print 'dropping database %(db-name)s' % source + cursor.execute('DROP DATABASE "%(db-name)s"' % source) + print '-> database %(db-name)s dropped.' % source + + def _drop_user(self, source): + user = source['db-user'] or None + if user is not None: + with db_sys_transaction(source, privilege='DROP USER') as cursor: + print 'dropping user %s' % user + cursor.execute('DROP USER %s' % user) + + def _cleanup_steps(self, source): + # 1/ delete database + yield ('Delete database "%(db-name)s"' % source, + self._drop_database, True) + # 2/ delete user + helper = get_db_helper(source['db-driver']) + if source['db-user'] and helper.users_support: + # XXX should check we are not connected as user + yield ('Delete user "%(db-user)s"' % source, + self._drop_user, False) + def cleanup(self): """remove instance's configuration and database""" - from logilab.database import get_db_helper source = self.config.system_source_config - dbname = source['db-name'] - helper = get_db_helper(source['db-driver']) - if ASK.confirm('Delete database %s ?' % dbname): - if source['db-driver'] == 'sqlite': - if confirm_on_error_or_die( - 'deleting database file %s' % dbname, - os.unlink, source['db-name']) is not ERROR: - print '-> database %s dropped.' % dbname - return - user = source['db-user'] or None - cnx = confirm_on_error_or_die('connecting to database %s' % dbname, - _db_sys_cnx, source, 'DROP DATABASE') - if cnx is ERROR: - return - cursor = cnx.cursor() - try: - if confirm_on_error_or_die( - 'dropping database %s' % dbname, - cursor.execute, 'DROP DATABASE "%s"' % dbname) is not ERROR: - print '-> database %s dropped.' % dbname - # XXX should check we are not connected as user - if user and helper.users_support and \ - ASK.confirm('Delete user %s ?' % user, default_is_yes=False): - if confirm_on_error_or_die( - 'dropping user %s' % user, - cursor.execute, 'DROP USER %s' % user) is not ERROR: - print '-> user %s dropped.' % user - cnx.commit() - except BaseException: - cnx.rollback() - raise + for msg, step, default in self._cleanup_steps(source): + if ASK.confirm(msg, default_is_yes=default): + try: + step(source) + except Exception as exc: + print 'ERROR', exc + if ASK.confirm('An error occurred. Continue anyway?', + default_is_yes=False): + continue + raise ExecutionError(str(exc)) class RepositoryStartHandler(CommandHandler): @@ -294,6 +309,7 @@ helper.create_database(cursor, source['db-name'], dbencoding=source['db-encoding'], **kwargs) + class CreateInstanceDBCommand(Command): """Create the system database of an instance (run after 'create'). @@ -331,7 +347,6 @@ def run(self, args): """run the command with its specific arguments""" - from logilab.database import get_db_helper check_options_consistency(self.config) automatic = self.get('automatic') appid = args.pop() @@ -439,7 +454,6 @@ check_options_consistency(self.config) print '\n'+underline_title('Initializing the system database') from cubicweb.server import init_repository - from logilab.database import get_connection appid = args[0] config = ServerConfiguration.config_for(appid) try: @@ -603,7 +617,6 @@ sys.exit(1) cnx = source_cnx(sourcescfg['system']) driver = sourcescfg['system']['db-driver'] - from logilab.database import get_db_helper dbhelper = get_db_helper(driver) cursor = cnx.cursor() # check admin exists