25 import sys |
25 import sys |
26 import os |
26 import os |
27 |
27 |
28 from logilab.common import nullobject |
28 from logilab.common import nullobject |
29 from logilab.common.configuration import Configuration |
29 from logilab.common.configuration import Configuration |
30 from logilab.common.shellutils import ASK |
30 from logilab.common.shellutils import ASK, generate_password |
31 |
31 |
32 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError |
32 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError |
33 from cubicweb.toolsutils import Command, CommandHandler, underline_title |
33 from cubicweb.toolsutils import Command, CommandHandler, underline_title |
34 from cubicweb.cwctl import CWCTL |
34 from cubicweb.cwctl import CWCTL, check_options_consistency |
35 from cubicweb.server import SOURCE_TYPES |
35 from cubicweb.server import SOURCE_TYPES |
36 from cubicweb.server.serverconfig import ( |
36 from cubicweb.server.serverconfig import ( |
37 USER_OPTIONS, ServerConfiguration, SourceConfiguration, |
37 USER_OPTIONS, ServerConfiguration, SourceConfiguration, |
38 ask_source_config, generate_source_config) |
38 ask_source_config, generate_source_config) |
39 |
39 |
152 |
152 |
153 class RepositoryCreateHandler(CommandHandler): |
153 class RepositoryCreateHandler(CommandHandler): |
154 cmdname = 'create' |
154 cmdname = 'create' |
155 cfgname = 'repository' |
155 cfgname = 'repository' |
156 |
156 |
157 def bootstrap(self, cubes, inputlevel=0): |
157 def bootstrap(self, cubes, automatic=False, inputlevel=0): |
158 """create an instance by copying files from the given cube and by asking |
158 """create an instance by copying files from the given cube and by asking |
159 information necessary to build required configuration files |
159 information necessary to build required configuration files |
160 """ |
160 """ |
161 config = self.config |
161 config = self.config |
162 print underline_title('Configuring the repository') |
162 if not automatic: |
163 config.input_config('email', inputlevel) |
163 print underline_title('Configuring the repository') |
164 # ask for pyro configuration if pyro is activated and we're not using a |
164 config.input_config('email', inputlevel) |
165 # all-in-one config, in which case this is done by the web side command |
165 # ask for pyro configuration if pyro is activated and we're not |
166 # handler |
166 # using a all-in-one config, in which case this is done by the web |
167 if config.pyro_enabled() and config.name != 'all-in-one': |
167 # side command handler |
168 config.input_config('pyro', inputlevel) |
168 if config.pyro_enabled() and config.name != 'all-in-one': |
169 print '\n'+underline_title('Configuring the sources') |
169 config.input_config('pyro', inputlevel) |
|
170 print '\n'+underline_title('Configuring the sources') |
170 sourcesfile = config.sources_file() |
171 sourcesfile = config.sources_file() |
171 # XXX hack to make Method('default_instance_id') usable in db option |
172 # hack to make Method('default_instance_id') usable in db option defs |
172 # defs (in native.py) |
173 # (in native.py) |
173 sconfig = SourceConfiguration(config, |
174 sconfig = SourceConfiguration(config, |
174 options=SOURCE_TYPES['native'].options) |
175 options=SOURCE_TYPES['native'].options) |
175 sconfig.input_config(inputlevel=inputlevel) |
176 if not automatic: |
|
177 sconfig.input_config(inputlevel=inputlevel) |
|
178 print |
176 sourcescfg = {'system': sconfig} |
179 sourcescfg = {'system': sconfig} |
177 print |
180 if automatic: |
178 sconfig = Configuration(options=USER_OPTIONS) |
181 # XXX modify a copy |
179 sconfig.input_config(inputlevel=inputlevel) |
182 password = generate_password() |
|
183 print 'Administration account is admin / %s' % password |
|
184 USER_OPTIONS[1][1]['default'] = password |
|
185 sconfig = Configuration(options=USER_OPTIONS) |
|
186 else: |
|
187 sconfig = Configuration(options=USER_OPTIONS) |
|
188 sconfig.input_config(inputlevel=inputlevel) |
180 sourcescfg['admin'] = sconfig |
189 sourcescfg['admin'] = sconfig |
181 config.write_sources_file(sourcescfg) |
190 config.write_sources_file(sourcescfg) |
182 # remember selected cubes for later initialization of the database |
191 # remember selected cubes for later initialization of the database |
183 config.write_bootstrap_cubes_file(cubes) |
192 config.write_bootstrap_cubes_file(cubes) |
184 |
193 |
185 def postcreate(self): |
194 def postcreate(self, automatic=False, inputlevel=0): |
186 if ASK.confirm('Run db-create to create the system database ?'): |
195 if automatic: |
187 verbosity = (self.config.mode == 'installed') and 'y' or 'n' |
196 CWCTL.run(['db-create', '--automatic', self.config.appid]) |
188 CWCTL.run(['db-create', self.config.appid]) |
197 elif ASK.confirm('Run db-create to create the system database ?'): |
|
198 CWCTL.run(['db-create', '--config-level', str(inputlevel), |
|
199 self.config.appid]) |
189 else: |
200 else: |
190 print ('-> nevermind, you can do it later with ' |
201 print ('-> nevermind, you can do it later with ' |
191 '"cubicweb-ctl db-create %s".' % self.config.appid) |
202 '"cubicweb-ctl db-create %s".' % self.config.appid) |
192 |
203 |
193 ERROR = nullobject() |
204 ERROR = nullobject() |
291 """ |
302 """ |
292 name = 'db-create' |
303 name = 'db-create' |
293 arguments = '<instance>' |
304 arguments = '<instance>' |
294 min_args = max_args = 1 |
305 min_args = max_args = 1 |
295 options = ( |
306 options = ( |
|
307 ('automatic', |
|
308 {'short': 'a', 'action' : 'store_true', |
|
309 'default': False, |
|
310 'help': 'automatic mode: never ask and use default answer to every ' |
|
311 'question. this may require that your login match a database super ' |
|
312 'user (allowed to create database & all).', |
|
313 }), |
|
314 ('config-level', |
|
315 {'short': 'l', 'type' : 'int', 'metavar': '<level>', |
|
316 'default': 0, |
|
317 'help': 'configuration level (0..2): 0 will ask for essential ' |
|
318 'configuration parameters only while 2 will ask for all parameters', |
|
319 }), |
296 ('create-db', |
320 ('create-db', |
297 {'short': 'c', 'type': 'yn', 'metavar': '<y or n>', |
321 {'short': 'c', 'type': 'yn', 'metavar': '<y or n>', |
298 'default': True, |
322 'default': True, |
299 'help': 'create the database (yes by default)'}), |
323 'help': 'create the database (yes by default)' |
300 ('quiet', |
|
301 {'short': 'q', 'action' : 'store_true', |
|
302 'default': False, |
|
303 'help': 'be quiet. Suppose database user in the sources file is a ' |
|
304 'super user and don\'t ask for alternate login.', |
|
305 }), |
324 }), |
306 ('automatic', |
|
307 {'short': 'a', 'type' : 'yn', 'metavar': '<auto>', |
|
308 'default': 'n', |
|
309 'help': 'automatic mode: never ask and use default answer to every question', |
|
310 } |
|
311 ), |
|
312 ) |
325 ) |
|
326 |
313 def run(self, args): |
327 def run(self, args): |
314 """run the command with its specific arguments""" |
328 """run the command with its specific arguments""" |
315 from logilab.database import get_db_helper |
329 from logilab.database import get_db_helper |
316 quiet = self.get('quiet') |
330 check_options_consistency(self.config) |
317 automatic = self.get('automatic') |
331 automatic = self.get('automatic') |
318 appid = args.pop() |
332 appid = args.pop() |
319 config = ServerConfiguration.config_for(appid) |
333 config = ServerConfiguration.config_for(appid) |
320 source = config.sources()['system'] |
334 source = config.sources()['system'] |
321 dbname = source['db-name'] |
335 dbname = source['db-name'] |
328 os.unlink(dbname) |
342 os.unlink(dbname) |
329 elif self.config.create_db: |
343 elif self.config.create_db: |
330 print '\n'+underline_title('Creating the system database') |
344 print '\n'+underline_title('Creating the system database') |
331 # connect on the dbms system base to create our base |
345 # connect on the dbms system base to create our base |
332 dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER', |
346 dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER', |
333 interactive=not quiet) |
347 interactive=not automatic) |
334 cursor = dbcnx.cursor() |
348 cursor = dbcnx.cursor() |
335 try: |
349 try: |
336 if helper.users_support: |
350 if helper.users_support: |
337 user = source['db-user'] |
351 user = source['db-user'] |
338 if not helper.user_exists(cursor, user) and (automatic or \ |
352 if not helper.user_exists(cursor, user) and (automatic or \ |
341 print '-> user %s created.' % user |
355 print '-> user %s created.' % user |
342 if dbname in helper.list_databases(cursor): |
356 if dbname in helper.list_databases(cursor): |
343 if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname): |
357 if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname): |
344 cursor.execute('DROP DATABASE %s' % dbname) |
358 cursor.execute('DROP DATABASE %s' % dbname) |
345 else: |
359 else: |
|
360 print ('you may want to run "cubicweb-ctl db-init ' |
|
361 '--drop %s" manually to continue.' % config.appid) |
346 return |
362 return |
347 createdb(helper, source, dbcnx, cursor) |
363 createdb(helper, source, dbcnx, cursor) |
348 dbcnx.commit() |
364 dbcnx.commit() |
349 print '-> database %s created.' % dbname |
365 print '-> database %s created.' % dbname |
350 except: |
366 except: |
351 dbcnx.rollback() |
367 dbcnx.rollback() |
352 raise |
368 raise |
353 cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE', |
369 cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE', |
354 interactive=not quiet) |
370 interactive=not automatic) |
355 cursor = cnx.cursor() |
371 cursor = cnx.cursor() |
356 helper.init_fti_extensions(cursor) |
372 helper.init_fti_extensions(cursor) |
357 # postgres specific stuff |
373 # postgres specific stuff |
358 if driver == 'postgres': |
374 if driver == 'postgres': |
359 # install plpythonu/plpgsql language if not installed by the cube |
375 # install plpythonu/plpgsql language if not installed by the cube |
362 helper.create_language(cursor, extlang) |
378 helper.create_language(cursor, extlang) |
363 cursor.close() |
379 cursor.close() |
364 cnx.commit() |
380 cnx.commit() |
365 print '-> database for instance %s created and necessary extensions installed.' % appid |
381 print '-> database for instance %s created and necessary extensions installed.' % appid |
366 print |
382 print |
367 if automatic or ASK.confirm('Run db-init to initialize the system database ?'): |
383 if automatic: |
368 CWCTL.run(['db-init', config.appid]) |
384 CWCTL.run(['db-init', '--automatic', '--config-level', '0', |
|
385 config.appid]) |
|
386 elif ASK.confirm('Run db-init to initialize the system database ?'): |
|
387 CWCTL.run(['db-init', '--config-level', |
|
388 str(self.config.config_level), config.appid]) |
369 else: |
389 else: |
370 print ('-> nevermind, you can do it later with ' |
390 print ('-> nevermind, you can do it later with ' |
371 '"cubicweb-ctl db-init %s".' % config.appid) |
391 '"cubicweb-ctl db-init %s".' % config.appid) |
372 |
392 |
373 |
393 |
382 """ |
402 """ |
383 name = 'db-init' |
403 name = 'db-init' |
384 arguments = '<instance>' |
404 arguments = '<instance>' |
385 min_args = max_args = 1 |
405 min_args = max_args = 1 |
386 options = ( |
406 options = ( |
|
407 ('automatic', |
|
408 {'short': 'a', 'action' : 'store_true', |
|
409 'default': False, |
|
410 'help': 'automatic mode: never ask and use default answer to every ' |
|
411 'question.', |
|
412 }), |
|
413 ('config-level', |
|
414 {'short': 'l', 'type': 'int', 'default': 1, |
|
415 'help': 'level threshold for questions asked when configuring ' |
|
416 'another source' |
|
417 }), |
387 ('drop', |
418 ('drop', |
388 {'short': 'd', 'action': 'store_true', |
419 {'short': 'd', 'action': 'store_true', |
389 'default': False, |
420 'default': False, |
390 'help': 'insert drop statements to remove previously existant \ |
421 'help': 'insert drop statements to remove previously existant ' |
391 tables, indexes... (no by default)'}), |
422 'tables, indexes... (no by default)' |
392 ('config-level', |
|
393 {'short': 'l', 'type': 'int', 'default': 1, |
|
394 'help': 'level threshold for questions asked when configuring another source' |
|
395 }), |
423 }), |
396 ) |
424 ) |
397 |
425 |
398 def run(self, args): |
426 def run(self, args): |
|
427 check_options_consistency(self.config) |
399 print '\n'+underline_title('Initializing the system database') |
428 print '\n'+underline_title('Initializing the system database') |
400 from cubicweb.server import init_repository |
429 from cubicweb.server import init_repository |
401 from logilab.database import get_connection |
430 from logilab.database import get_connection |
402 appid = args[0] |
431 appid = args[0] |
403 config = ServerConfiguration.config_for(appid) |
432 config = ServerConfiguration.config_for(appid) |
414 raise ConfigurationError( |
443 raise ConfigurationError( |
415 'You seem to have provided wrong connection information in '\ |
444 'You seem to have provided wrong connection information in '\ |
416 'the %s file. Resolve this first (error: %s).' |
445 'the %s file. Resolve this first (error: %s).' |
417 % (config.sources_file(), str(ex).strip())) |
446 % (config.sources_file(), str(ex).strip())) |
418 init_repository(config, drop=self.config.drop) |
447 init_repository(config, drop=self.config.drop) |
419 while ASK.confirm('Enter another source ?', default_is_yes=False): |
448 if not self.config.automatic: |
420 CWCTL.run(['add-source', '--config-level', self.config.config_level, config.appid]) |
449 while ASK.confirm('Enter another source ?', default_is_yes=False): |
|
450 CWCTL.run(['add-source', '--config-level', |
|
451 str(self.config.config_level), config.appid]) |
421 |
452 |
422 |
453 |
423 class AddSourceCommand(Command): |
454 class AddSourceCommand(Command): |
424 """Add a data source to an instance. |
455 """Add a data source to an instance. |
425 |
456 |