# HG changeset patch # User Sylvain Thénault # Date 1454429953 -3600 # Node ID 9bacaf91afbcd3ae668a121072bdecb5af765f1f # Parent e717da3dc1646b23dae8f1a4db7e786564cbdca1 flake8 serverctl diff -r e717da3dc164 -r 9bacaf91afbc cubicweb/server/serverctl.py --- a/cubicweb/server/serverctl.py Tue Feb 16 10:52:00 2016 +0100 +++ b/cubicweb/server/serverctl.py Tue Feb 02 17:19:13 2016 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -26,13 +26,10 @@ import sys import os from contextlib import contextmanager -import logging -import subprocess from six import string_types from six.moves import input -from logilab.common import nullobject from logilab.common.configuration import Configuration, merge_options from logilab.common.shellutils import ASK, generate_password @@ -46,6 +43,7 @@ USER_OPTIONS, ServerConfiguration, SourceConfiguration, ask_source_config, generate_source_config) + # utility functions ########################################################### def source_cnx(source, dbname=None, special_privs=False, interactive=True): @@ -102,6 +100,7 @@ cnx.logged_user = user return cnx + def system_source_cnx(source, dbms_system_base=False, special_privs='CREATE/DROP DATABASE', interactive=True): """shortcut to get a connextion to the instance system database @@ -116,14 +115,13 @@ return source_cnx(source, special_privs=special_privs, interactive=interactive) + def _db_sys_cnx(source, special_privs, interactive=True): """return a connection on the RDMS system table (to create/drop a user or a database) """ import logilab.common as lgp lgp.USE_MX_DATETIME = False - driver = source['db-driver'] - helper = get_db_helper(driver) # connect on the dbms system base to create our base cnx = system_source_cnx(source, True, special_privs=special_privs, interactive=interactive) @@ -135,6 +133,7 @@ set_isolation_level(0) return cnx + def repo_cnx(config): """return a in-memory repository and a repoapi connection to it""" from cubicweb import repoapi @@ -170,8 +169,7 @@ if not automatic: print(underline_title('Configuring the repository')) config.input_config('email', inputlevel) - print('\n'+underline_title('Configuring the sources')) - sourcesfile = config.sources_file() + print('\n' + underline_title('Configuring the sources')) # hack to make Method('default_instance_id') usable in db option defs # (in native.py) sconfig = SourceConfiguration(config, @@ -249,13 +247,11 @@ print('-> database schema %s dropped' % db_namespace) 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) @@ -326,14 +322,14 @@ min_args = max_args = 1 options = ( ('automatic', - {'short': 'a', 'action' : 'store_true', + {'short': 'a', 'action': 'store_true', 'default': False, 'help': 'automatic mode: never ask and use default answer to every ' 'question. this may require that your login match a database super ' 'user (allowed to create database & all).', }), ('config-level', - {'short': 'l', 'type' : 'int', 'metavar': '', + {'short': 'l', 'type': 'int', 'metavar': '', 'default': 0, 'help': 'configuration level (0..2): 0 will ask for essential ' 'configuration parameters only while 2 will ask for all parameters', @@ -343,7 +339,7 @@ 'default': True, 'help': 'create the database (yes by default)' }), - ) + ) def run(self, args): """run the command with its specific arguments""" @@ -357,11 +353,11 @@ helper = get_db_helper(driver) if driver == 'sqlite': if os.path.exists(dbname) and ( - automatic or - ASK.confirm('Database %s already exists. Drop it?' % dbname)): + automatic or + ASK.confirm('Database %s already exists. Drop it?' % dbname)): os.unlink(dbname) elif self.config.create_db: - print('\n'+underline_title('Creating the system database')) + print('\n' + underline_title('Creating the system database')) # connect on the dbms system base to create our base dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER', interactive=not automatic) @@ -369,12 +365,14 @@ try: if helper.users_support: user = source['db-user'] - if not helper.user_exists(cursor, user) and (automatic or \ - ASK.confirm('Create db user %s ?' % user, default_is_yes=False)): + if not helper.user_exists(cursor, user) and ( + automatic or + ASK.confirm('Create db user %s ?' % user, default_is_yes=False)): helper.create_user(source['db-user'], source.get('db-password')) print('-> user %s created.' % user) if dbname in helper.list_databases(cursor): - if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname): + if automatic or ASK.confirm('Database %s already exists -- ' + 'do you want to drop it ?' % dbname): cursor.execute('DROP DATABASE "%s"' % dbname) else: print('you may want to run "cubicweb-ctl db-init ' @@ -405,7 +403,8 @@ helper.create_language(cursor, extlang) except Exception as exc: print('-> ERROR:', exc) - print('-> could not create language %s, some stored procedures might be unusable' % extlang) + print('-> could not create language %s, ' + 'some stored procedures might be unusable' % extlang) cnx.rollback() else: cnx.commit() @@ -436,7 +435,7 @@ min_args = max_args = 1 options = ( ('automatic', - {'short': 'a', 'action' : 'store_true', + {'short': 'a', 'action': 'store_true', 'default': False, 'help': 'automatic mode: never ask and use default answer to every ' 'question.', @@ -452,11 +451,11 @@ 'help': 'insert drop statements to remove previously existant ' 'tables, indexes... (no by default)' }), - ) + ) def run(self, args): check_options_consistency(self.config) - print('\n'+underline_title('Initializing the system database')) + print('\n' + underline_title('Initializing the system database')) from cubicweb.server import init_repository appid = args[0] config = ServerConfiguration.config_for(appid) @@ -471,7 +470,7 @@ schema=system.get('db-namespace'), **extra) except Exception as ex: raise ConfigurationError( - 'You seem to have provided wrong connection information in '\ + 'You seem to have provided wrong connection information in ' 'the %s file. Resolve this first (error: %s).' % (config.sources_file(), str(ex).strip())) init_repository(config, drop=self.config.drop) @@ -495,7 +494,7 @@ {'short': 'l', 'type': 'int', 'default': 1, 'help': 'level threshold for questions asked when configuring another source' }), - ) + ) def run(self, args): appid = args[0] @@ -508,7 +507,7 @@ cubes = repo.get_cubes() while True: type = input('source type (%s): ' - % ', '.join(sorted(SOURCE_TYPES))) + % ', '.join(sorted(SOURCE_TYPES))) if type not in SOURCE_TYPES: print('-> unknown source type, use one of the available types.') continue @@ -518,20 +517,20 @@ sourcecube = SOURCE_TYPES[type].module.split('.', 2)[1] # if the source adapter is coming from an external component, # ensure it's specified in used cubes - if not sourcecube in cubes: + if sourcecube not in cubes: print ('-> this source type require the %s cube which is ' 'not used by the instance.') continue break while True: parser = input('parser type (%s): ' - % ', '.join(sorted(repo.vreg['parsers']))) + % ', '.join(sorted(repo.vreg['parsers']))) if parser in repo.vreg['parsers']: break print('-> unknown parser identifier, use one of the available types.') while True: sourceuri = input('source identifier (a unique name used to ' - 'tell sources apart): ').strip() + 'tell sources apart): ').strip() if not sourceuri: print('-> mandatory.') else: @@ -565,11 +564,12 @@ min_args = max_args = 2 options = ( ('set-owner', - {'short': 'o', 'type' : 'yn', 'metavar' : '', - 'default' : False, + {'short': 'o', 'type': 'yn', 'metavar': '', + 'default': False, 'help': 'Set the user as tables owner if yes (no by default).'} ), - ) + ) + def run(self, args): """run the command with its specific arguments""" from cubicweb.server.sqlutils import sqlexec, sqlgrants @@ -604,13 +604,13 @@ min_args = max_args = 1 options = ( ('password', - {'short': 'p', 'type' : 'string', 'metavar' : '', - 'default' : None, + {'short': 'p', 'type': 'string', 'metavar': '', + 'default': None, 'help': 'Use this password instead of prompt for one.\n' '/!\ THIS IS AN INSECURE PRACTICE /!\ \n' 'the password will appear in shell history'} ), - ) + ) def run(self, args): """run the command with its specific arguments""" @@ -661,7 +661,6 @@ cnx.close() - def _remote_dump(host, appid, output, sudo=False): # XXX generate unique/portable file name from datetime import date @@ -682,8 +681,8 @@ rmcmd = 'ssh -t %s "rm -f /tmp/%s"' % (host, filename) print(rmcmd) if os.system(rmcmd) and not ASK.confirm( - 'An error occurred while deleting remote dump at /tmp/%s. ' - 'Continue anyway?' % filename): + 'An error occurred while deleting remote dump at /tmp/%s. ' + 'Continue anyway?' % filename): raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename) @@ -694,9 +693,10 @@ mih.backup_database(output, askconfirm=False, format=format) mih.shutdown() + def _local_restore(appid, backupfile, drop, format='native'): config = ServerConfiguration.config_for(appid) - config.verbosity = 1 # else we won't be asked for confirmation on problems + config.verbosity = 1 # else we won't be asked for confirmation on problems config.quick_start = True mih = config.migration_handler(connect=False, verbosity=1) mih.restore_database(backupfile, drop, askconfirm=False, format=format) @@ -726,6 +726,7 @@ # * database version = installed software, database version = instance fs version # ok! + def instance_status(config, cubicwebapplversion, vcconf): cubicwebversion = config.cubicweb_version() if cubicwebapplversion > cubicwebversion: @@ -736,7 +737,8 @@ try: softversion = config.cube_version(cube) except ConfigurationError: - print('-> Error: no cube version information for %s, please check that the cube is installed.' % cube) + print('-> Error: no cube version information for %s, ' + 'please check that the cube is installed.' % cube) continue try: applversion = vcconf[cube] @@ -764,13 +766,13 @@ min_args = max_args = 1 options = ( ('output', - {'short': 'o', 'type' : 'string', 'metavar' : '', - 'default' : None, + {'short': 'o', 'type': 'string', 'metavar': '', + 'default': None, 'help': 'Specify the backup file where the backup will be stored.'} ), ('sudo', - {'short': 's', 'action' : 'store_true', - 'default' : False, + {'short': 's', 'action': 'store_true', + 'default': False, 'help': 'Use sudo on the remote host.'} ), ('format', @@ -779,7 +781,7 @@ 'help': '"native" format uses db backend utilities to dump the database. ' '"portable" format uses a database independent format'} ), - ) + ) def run(self, args): appid = args[0] @@ -790,8 +792,6 @@ _local_dump(appid, self.config.output, format=self.config.format) - - class DBRestoreCommand(Command): """Restore the system database of an instance. @@ -804,7 +804,7 @@ options = ( ('no-drop', - {'short': 'n', 'action' : 'store_true', 'default' : False, + {'short': 'n', 'action': 'store_true', 'default': False, 'help': 'for some reason the database doesn\'t exist and so ' 'should not be dropped.'} ), @@ -812,7 +812,7 @@ {'short': 'f', 'default': 'native', 'type': 'choice', 'choices': ('native', 'portable'), 'help': 'the format used when dumping the database'}), - ) + ) def run(self, args): appid, backupfile = args @@ -851,19 +851,19 @@ min_args = max_args = 2 options = ( ('no-drop', - {'short': 'n', 'action' : 'store_true', - 'default' : False, + {'short': 'n', 'action': 'store_true', + 'default': False, 'help': 'For some reason the database doesn\'t exist and so ' 'should not be dropped.'} ), ('keep-dump', - {'short': 'k', 'action' : 'store_true', - 'default' : False, + {'short': 'k', 'action': 'store_true', + 'default': False, 'help': 'Specify that the dump file should not be automatically removed.'} ), ('sudo', - {'short': 's', 'action' : 'store_true', - 'default' : False, + {'short': 's', 'action': 'store_true', + 'default': False, 'help': 'Use sudo on the remote host.'} ), ('format', @@ -872,7 +872,7 @@ 'help': '"native" format uses db backend utilities to dump the database. ' '"portable" format uses a database independent format'} ), - ) + ) def run(self, args): import tempfile @@ -903,34 +903,33 @@ min_args = max_args = 1 options = ( ('checks', - {'short': 'c', 'type' : 'csv', 'metavar' : '', - 'default' : ('entities', 'relations', - 'mandatory_relations', 'mandatory_attributes', - 'metadata', 'schema', 'text_index'), + {'short': 'c', 'type': 'csv', 'metavar': '', + 'default': ('entities', 'relations', + 'mandatory_relations', 'mandatory_attributes', + 'metadata', 'schema', 'text_index'), 'help': 'Comma separated list of check to run. By default run all \ checks, i.e. entities, relations, mandatory_relations, mandatory_attributes, \ metadata, text_index and schema.'} ), ('autofix', - {'short': 'a', 'type' : 'yn', 'metavar' : '', - 'default' : False, + {'short': 'a', 'type': 'yn', 'metavar': '', + 'default': False, 'help': 'Automatically correct integrity problems if this option \ is set to "y" or "yes", else only display them'} ), ('reindex', - {'short': 'r', 'type' : 'yn', 'metavar' : '', - 'default' : False, + {'short': 'r', 'type': 'yn', 'metavar': '', + 'default': False, 'help': 're-indexes the database for full text search if this \ option is set to "y" or "yes" (may be long for large database).'} ), ('force', - {'short': 'f', 'action' : 'store_true', - 'default' : False, + {'short': 'f', 'action': 'store_true', + 'default': False, 'help': 'don\'t check instance is up to date.'} ), - - ) + ) def run(self, args): from cubicweb.server.checkintegrity import check @@ -981,10 +980,10 @@ arguments = ' ' min_args = max_args = 2 options = ( - ('loglevel', - {'short': 'l', 'type' : 'choice', 'metavar': '', - 'default': 'info', 'choices': ('debug', 'info', 'warning', 'error'), - }), + ('loglevel', + {'short': 'l', 'type': 'choice', 'metavar': '', + 'default': 'info', 'choices': ('debug', 'info', 'warning', 'error')}, + ), ) def run(self, args): @@ -1010,9 +1009,7 @@ print(key, ':', val) - def permissionshandler(relation, perms): - from yams.schema import RelationDefinitionSchema from yams.buildobjs import DEFAULT_ATTRPERMS from cubicweb.schema import (PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS, RO_REL_PERMS, RO_ATTR_PERMS) @@ -1063,21 +1060,24 @@ db_options = ( ('db', - {'short': 'd', 'type' : 'named', 'metavar' : '[section1.]key1:value1,[section2.]key2:value2', + {'short': 'd', 'type': 'named', 'metavar': '[section1.]key1:value1,[section2.]key2:value2', 'default': None, - 'help': '''set in
to in "source" configuration file. If
is not specified, it defaults to "system". + 'help': '''set in
to in "source" configuration file. If +
is not specified, it defaults to "system". Beware that changing admin.login or admin.password using this command will NOT update the database with new admin credentials. Use the reset-admin-pwd command instead. ''', }), - ) +) ConfigureInstanceCommand.options = merge_options( - ConfigureInstanceCommand.options + db_options) + ConfigureInstanceCommand.options + db_options) configure_instance = ConfigureInstanceCommand.configure_instance + + def configure_instance2(self, appid): configure_instance(self, appid) if self.config.db is not None: @@ -1091,10 +1091,12 @@ try: srccfg[section][key] = value except KeyError: - raise ConfigurationError('unknown configuration key "%s" in section "%s" for source' % (key, section)) + raise ConfigurationError('unknown configuration key "%s" in section "%s" for source' + % (key, section)) admcfg = Configuration(options=USER_OPTIONS) admcfg['login'] = srccfg['admin']['login'] admcfg['password'] = srccfg['admin']['password'] srccfg['admin'] = admcfg appcfg.write_sources_file(srccfg) + ConfigureInstanceCommand.configure_instance = configure_instance2