[c-c create] unification of c-c create and its subcommands handling stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 30 Mar 2011 11:17:21 +0200
branchstable
changeset 7140 ba51dac1115d
parent 7139 20807d3d7cf6
child 7141 ef29d3ea3909
child 7142 c47381851a3e
[c-c create] unification of c-c create and its subcommands handling * create/db-create/db-init uniformly accept --automatic and --config-level options, properly passed along the way * --automatic option fixed so it doesn't need yes or no argument * closes ##1537265 on the way
cwctl.py
dbapi.py
etwist/twctl.py
server/serverctl.py
utils.py
web/webctl.py
--- a/cwctl.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/cwctl.py	Wed Mar 30 11:17:21 2011 +0200
@@ -300,6 +300,11 @@
                     print '* cube %s version %s is installed, but version %s is required by %s' % (
                         cube, cfgpb.cubes[cube], version, src)
 
+def check_options_consistency(config):
+    if config.automatic and config.config_level > 0:
+        raise BadCommandUsage('--automatic and --config-level should not be '
+                              'used together')
+
 class CreateInstanceCommand(Command):
     """Create an instance from a cube. This is an unified
     command which can handle web / server / all-in-one installation
@@ -309,7 +314,7 @@
     <cube>
       the name of cube to use (list available cube names using
       the "list" command). You can use several cubes by separating
-      them using comma (e.g. 'jpl,eemail')
+      them using comma (e.g. 'jpl,email')
     <instance>
       an identifier for the instance to create
     """
@@ -317,28 +322,34 @@
     arguments = '<cube> <instance>'
     min_args = max_args = 2
     options = (
-        ("config-level",
+        ('automatic',
+         {'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': '<level>',
           'default': 0,
-          'help': 'configuration level (0..2): 0 will ask for essential \
-configuration parameters only while 2 will ask for all parameters',
-          }
-         ),
-        ("config",
+          'help': 'configuration level (0..2): 0 will ask for essential '
+          'configuration parameters only while 2 will ask for all parameters',
+          }),
+        ('config',
          {'short': 'c', 'type' : 'choice', 'metavar': '<install type>',
           'choices': ('all-in-one', 'repository', 'twisted'),
           'default': 'all-in-one',
-          'help': 'installation type, telling which part of an instance \
-should be installed. You can list available configurations using the "list" \
-command. Default to "all-in-one", e.g. an installation embedding both the RQL \
-repository and the web server.',
-          }
-         ),
+          'help': 'installation type, telling which part of an instance '
+          'should be installed. You can list available configurations using the'
+          ' "list" command. Default to "all-in-one", e.g. an installation '
+          'embedding both the RQL repository and the web server.',
+          }),
         )
 
     def run(self, args):
         """run the command with its specific arguments"""
         from logilab.common.textutils import splitstrip
+        check_options_consistency(self.config)
         configname = self.config.config
         cubes, appid = args
         cubes = splitstrip(cubes)
@@ -360,17 +371,19 @@
         print '\n'+underline_title('Creating the instance %s' % appid)
         create_dir(config.apphome)
         # cubicweb-ctl configuration
-        print '\n'+underline_title('Configuring the instance (%s.conf)' % configname)
-        config.input_config('main', self.config.config_level)
+        if not self.config.automatic:
+            print '\n'+underline_title('Configuring the instance (%s.conf)'
+                                       % configname)
+            config.input_config('main', self.config.config_level)
         # configuration'specific stuff
         print
-        helper.bootstrap(cubes, self.config.config_level)
+        helper.bootstrap(cubes, self.config.automatic, self.config.config_level)
         # input for cubes specific options
         sections = set(sect.lower() for sect, opt, odict in config.all_options()
                        if 'type' in odict
                        and odict.get('level') <= self.config.config_level)
         for section in sections:
-            if section not in ('main', 'email', 'pyro'):
+            if section not in ('main', 'email', 'pyro', 'web'):
                 print '\n' + underline_title('%s options' % section)
                 config.input_config(section, self.config.config_level)
         # write down configuration
@@ -385,8 +398,9 @@
         errors = config.i18ncompile(langs)
         if errors:
             print '\n'.join(errors)
-            if not ASK.confirm('error while compiling message catalogs, '
-                               'continue anyway ?'):
+            if self.config.automatic \
+                   or not ASK.confirm('error while compiling message catalogs, '
+                                      'continue anyway ?'):
                 print 'creation not completed'
                 return
         # create the additional data directory for this instance
@@ -399,7 +413,7 @@
             print 'set %s as owner of the data directory' % config['uid']
             chown(config.appdatahome, config['uid'])
         print '\n-> creation done for %r.\n' % config.apphome
-        helper.postcreate()
+        helper.postcreate(self.config.automatic)
 
     def _handle_win32(self, config, appid):
         if sys.platform != 'win32':
--- a/dbapi.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/dbapi.py	Wed Mar 30 11:17:21 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/etwist/twctl.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/etwist/twctl.py	Wed Mar 30 11:17:21 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -51,10 +51,10 @@
         """
         cfgname = 'all-in-one'
 
-        def bootstrap(self, cubes, inputlevel=0):
+        def bootstrap(self, cubes, automatic=False, inputlevel=0):
             """bootstrap this configuration"""
-            serverctl.RepositoryCreateHandler.bootstrap(self, cubes, inputlevel)
-            TWCreateHandler.bootstrap(self, cubes, inputlevel)
+            serverctl.RepositoryCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
+            TWCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
 
     class AllInOneStartHandler(TWStartHandler):
         cmdname = 'start'
--- a/server/serverctl.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/server/serverctl.py	Wed Mar 30 11:17:21 2011 +0200
@@ -27,11 +27,11 @@
 
 from logilab.common import nullobject
 from logilab.common.configuration import Configuration
-from logilab.common.shellutils import ASK
+from logilab.common.shellutils import ASK, generate_password
 
 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
 from cubicweb.toolsutils import Command, CommandHandler, underline_title
-from cubicweb.cwctl import CWCTL
+from cubicweb.cwctl import CWCTL, check_options_consistency
 from cubicweb.server import SOURCE_TYPES
 from cubicweb.server.serverconfig import (
     USER_OPTIONS, ServerConfiguration, SourceConfiguration,
@@ -154,38 +154,49 @@
     cmdname = 'create'
     cfgname = 'repository'
 
-    def bootstrap(self, cubes, inputlevel=0):
+    def bootstrap(self, cubes, automatic=False, inputlevel=0):
         """create an instance by copying files from the given cube and by asking
         information necessary to build required configuration files
         """
         config = self.config
-        print underline_title('Configuring the repository')
-        config.input_config('email', inputlevel)
-        # ask for pyro configuration if pyro is activated and we're not using a
-        # all-in-one config, in which case this is done by the web side command
-        # handler
-        if config.pyro_enabled() and config.name != 'all-in-one':
-            config.input_config('pyro', inputlevel)
-        print '\n'+underline_title('Configuring the sources')
+        if not automatic:
+            print underline_title('Configuring the repository')
+            config.input_config('email', inputlevel)
+            # ask for pyro configuration if pyro is activated and we're not
+            # using a all-in-one config, in which case this is done by the web
+            # side command handler
+            if config.pyro_enabled() and config.name != 'all-in-one':
+                config.input_config('pyro', inputlevel)
+            print '\n'+underline_title('Configuring the sources')
         sourcesfile = config.sources_file()
-        # XXX hack to make Method('default_instance_id') usable in db option
-        # defs (in native.py)
+        # hack to make Method('default_instance_id') usable in db option defs
+        # (in native.py)
         sconfig = SourceConfiguration(config,
                                       options=SOURCE_TYPES['native'].options)
-        sconfig.input_config(inputlevel=inputlevel)
+        if not automatic:
+            sconfig.input_config(inputlevel=inputlevel)
+            print
         sourcescfg = {'system': sconfig}
-        print
-        sconfig = Configuration(options=USER_OPTIONS)
-        sconfig.input_config(inputlevel=inputlevel)
+        if automatic:
+            # XXX modify a copy
+            password = generate_password()
+            print 'Administration account is admin / %s' % password
+            USER_OPTIONS[1][1]['default'] = password
+            sconfig = Configuration(options=USER_OPTIONS)
+        else:
+            sconfig = Configuration(options=USER_OPTIONS)
+            sconfig.input_config(inputlevel=inputlevel)
         sourcescfg['admin'] = sconfig
         config.write_sources_file(sourcescfg)
         # remember selected cubes for later initialization of the database
         config.write_bootstrap_cubes_file(cubes)
 
-    def postcreate(self):
-        if ASK.confirm('Run db-create to create the system database ?'):
-            verbosity = (self.config.mode == 'installed') and 'y' or 'n'
-            CWCTL.run(['db-create', self.config.appid])
+    def postcreate(self, automatic=False, inputlevel=0):
+        if automatic:
+            CWCTL.run(['db-create', '--automatic', self.config.appid])
+        elif ASK.confirm('Run db-create to create the system database ?'):
+            CWCTL.run(['db-create', '--config-level', str(inputlevel),
+                       self.config.appid])
         else:
             print ('-> nevermind, you can do it later with '
                    '"cubicweb-ctl db-create %s".' % self.config.appid)
@@ -293,27 +304,30 @@
     arguments = '<instance>'
     min_args = max_args = 1
     options = (
+        ('automatic',
+         {'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': '<level>',
+          'default': 0,
+          'help': 'configuration level (0..2): 0 will ask for essential '
+          'configuration parameters only while 2 will ask for all parameters',
+          }),
         ('create-db',
          {'short': 'c', 'type': 'yn', 'metavar': '<y or n>',
           'default': True,
-          'help': 'create the database (yes by default)'}),
-        ('quiet',
-         {'short': 'q', 'action' : 'store_true',
-          'default': False,
-          'help': 'be quiet. Suppose database user in the sources file is a '
-          'super user and don\'t ask for alternate login.',
+          'help': 'create the database (yes by default)'
           }),
-        ('automatic',
-         {'short': 'a', 'type' : 'yn', 'metavar': '<auto>',
-          'default': 'n',
-          'help': 'automatic mode: never ask and use default answer to every question',
-          }
-         ),
         )
+
     def run(self, args):
         """run the command with its specific arguments"""
         from logilab.database import get_db_helper
-        quiet = self.get('quiet')
+        check_options_consistency(self.config)
         automatic = self.get('automatic')
         appid = args.pop()
         config = ServerConfiguration.config_for(appid)
@@ -330,7 +344,7 @@
             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 quiet)
+                                interactive=not automatic)
             cursor = dbcnx.cursor()
             try:
                 if helper.users_support:
@@ -343,6 +357,8 @@
                     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 '
+                               '--drop %s" manually to continue.' % config.appid)
                         return
                 createdb(helper, source, dbcnx, cursor)
                 dbcnx.commit()
@@ -351,7 +367,7 @@
                 dbcnx.rollback()
                 raise
         cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE',
-                                interactive=not quiet)
+                                interactive=not automatic)
         cursor = cnx.cursor()
         helper.init_fti_extensions(cursor)
         # postgres specific stuff
@@ -364,8 +380,12 @@
         cnx.commit()
         print '-> database for instance %s created and necessary extensions installed.' % appid
         print
-        if automatic or ASK.confirm('Run db-init to initialize the system database ?'):
-            CWCTL.run(['db-init', config.appid])
+        if automatic:
+            CWCTL.run(['db-init', '--automatic', '--config-level', '0',
+                       config.appid])
+        elif ASK.confirm('Run db-init to initialize the system database ?'):
+            CWCTL.run(['db-init', '--config-level',
+                       str(self.config.config_level), config.appid])
         else:
             print ('-> nevermind, you can do it later with '
                    '"cubicweb-ctl db-init %s".' % config.appid)
@@ -384,18 +404,27 @@
     arguments = '<instance>'
     min_args = max_args = 1
     options = (
+        ('automatic',
+         {'short': 'a', 'action' : 'store_true',
+          'default': False,
+          'help': 'automatic mode: never ask and use default answer to every '
+          'question.',
+          }),
+        ('config-level',
+         {'short': 'l', 'type': 'int', 'default': 1,
+          'help': 'level threshold for questions asked when configuring '
+          'another source'
+          }),
         ('drop',
          {'short': 'd', 'action': 'store_true',
           'default': False,
-          'help': 'insert drop statements to remove previously existant \
-tables, indexes... (no by default)'}),
-        ('config-level',
-         {'short': 'l', 'type': 'int', 'default': 1,
-          'help': 'level threshold for questions asked when configuring another source'
+          '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')
         from cubicweb.server import init_repository
         from logilab.database import get_connection
@@ -416,8 +445,10 @@
                 'the %s file. Resolve this first (error: %s).'
                 % (config.sources_file(), str(ex).strip()))
         init_repository(config, drop=self.config.drop)
-        while ASK.confirm('Enter another source ?', default_is_yes=False):
-            CWCTL.run(['add-source', '--config-level', self.config.config_level, config.appid])
+        if not self.config.automatic:
+            while ASK.confirm('Enter another source ?', default_is_yes=False):
+                CWCTL.run(['add-source', '--config-level',
+                           str(self.config.config_level), config.appid])
 
 
 class AddSourceCommand(Command):
--- a/utils.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/utils.py	Wed Mar 30 11:17:21 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/web/webctl.py	Wed Mar 30 11:08:15 2011 +0200
+++ b/web/webctl.py	Wed Mar 30 11:17:21 2011 +0200
@@ -28,16 +28,18 @@
 class WebCreateHandler(CommandHandler):
     cmdname = 'create'
 
-    def bootstrap(self, cubes, inputlevel=0):
+    def bootstrap(self, cubes, automatic=False, inputlevel=0):
         """bootstrap this configuration"""
-        print '\n' + underline_title('Generic web configuration')
-        config = self.config
-        if config.repo_method == 'pyro' or config.pyro_enabled():
-            print '\n' + underline_title('Pyro configuration')
-            config.input_config('pyro', inputlevel)
-        if ASK.confirm('Allow anonymous access ?', False):
-            config.global_set_option('anonymous-user', 'anon')
-            config.global_set_option('anonymous-password', 'anon')
+        if not automatic:
+            print '\n' + underline_title('Generic web configuration')
+            config = self.config
+            if config.repo_method == 'pyro' or config.pyro_enabled():
+                print '\n' + underline_title('Pyro configuration')
+                config.input_config('pyro', inputlevel)
+            config.input_config('web', inputlevel)
+            if ASK.confirm('Allow anonymous access ?', False):
+                config.global_set_option('anonymous-user', 'anon')
+                config.global_set_option('anonymous-password', 'anon')
 
-    def postcreate(self):
+    def postcreate(self, *args, **kwargs):
         """hooks called once instance's initialization has been completed"""