3 :organization: Logilab |
3 :organization: Logilab |
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
7 """ |
7 """ |
8 __docformat__ = "restructuredtext en" |
8 __docformat__ = 'restructuredtext en' |
9 |
9 |
10 import sys |
10 import sys |
11 import os |
11 import os |
12 |
12 |
13 from logilab.common.configuration import Configuration |
13 from logilab.common.configuration import Configuration |
167 config.write_sources_file(sourcescfg) |
167 config.write_sources_file(sourcescfg) |
168 # remember selected cubes for later initialization of the database |
168 # remember selected cubes for later initialization of the database |
169 config.write_bootstrap_cubes_file(cubes) |
169 config.write_bootstrap_cubes_file(cubes) |
170 |
170 |
171 def postcreate(self): |
171 def postcreate(self): |
172 if confirm('Do you want to run db-create to create the "system database" ?'): |
172 if confirm('Do you want to run db-create to create the system database ?'): |
173 verbosity = (self.config.mode == 'installed') and 'y' or 'n' |
173 verbosity = (self.config.mode == 'installed') and 'y' or 'n' |
174 cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity) |
174 cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity) |
175 else: |
175 else: |
176 print ('-> nevermind, you can do it later with ' |
176 print ('-> nevermind, you can do it later with ' |
177 '"cubicweb-ctl db-create %s".' % self.config.appid) |
177 '"cubicweb-ctl db-create %s".' % self.config.appid) |
244 """ |
244 """ |
245 name = 'db-create' |
245 name = 'db-create' |
246 arguments = '<instance>' |
246 arguments = '<instance>' |
247 |
247 |
248 options = ( |
248 options = ( |
249 ("create-db", |
249 ('create-db', |
250 {'short': 'c', 'type': "yn", 'metavar': '<y or n>', |
250 {'short': 'c', 'type': 'yn', 'metavar': '<y or n>', |
251 'default': True, |
251 'default': True, |
252 'help': 'create the database (yes by default)'}), |
252 'help': 'create the database (yes by default)'}), |
253 ("verbose", |
253 ('verbose', |
254 {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>', |
254 {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>', |
255 'default': 'n', |
255 'default': 'n', |
256 'help': 'verbose mode: will ask all possible configuration questions', |
256 'help': 'verbose mode: will ask all possible configuration questions', |
257 } |
257 } |
258 ), |
258 ), |
260 def run(self, args): |
260 def run(self, args): |
261 """run the command with its specific arguments""" |
261 """run the command with its specific arguments""" |
262 from logilab.common.adbh import get_adv_func_helper |
262 from logilab.common.adbh import get_adv_func_helper |
263 from indexer import get_indexer |
263 from indexer import get_indexer |
264 verbose = self.get('verbose') |
264 verbose = self.get('verbose') |
265 appid = pop_arg(args, msg="No instance specified !") |
265 appid = pop_arg(args, msg='No instance specified !') |
266 config = ServerConfiguration.config_for(appid) |
266 config = ServerConfiguration.config_for(appid) |
267 create_db = self.config.create_db |
267 create_db = self.config.create_db |
268 source = config.sources()['system'] |
268 source = config.sources()['system'] |
269 driver = source['db-driver'] |
269 driver = source['db-driver'] |
270 helper = get_adv_func_helper(driver) |
270 helper = get_adv_func_helper(driver) |
271 if create_db: |
271 if create_db: |
272 print '\n'+underline_title('Creating the "system database"') |
272 print '\n'+underline_title('Creating the system database') |
273 # connect on the dbms system base to create our base |
273 # connect on the dbms system base to create our base |
274 dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose) |
274 dbcnx = _db_sys_cnx(source, 'CREATE DATABASE and / or USER', verbose=verbose) |
275 cursor = dbcnx.cursor() |
275 cursor = dbcnx.cursor() |
276 try: |
276 try: |
277 if helper.users_support: |
277 if helper.users_support: |
308 helper.create_language(cursor, extlang) |
308 helper.create_language(cursor, extlang) |
309 cursor.close() |
309 cursor.close() |
310 cnx.commit() |
310 cnx.commit() |
311 print '-> database for instance %s created and necessary extensions installed.' % appid |
311 print '-> database for instance %s created and necessary extensions installed.' % appid |
312 print |
312 print |
313 if confirm('Do you want to run db-init to initialize the "system database" ?'): |
313 if confirm('Do you want to run db-init to initialize the system database ?'): |
314 cmd_run('db-init', config.appid) |
314 cmd_run('db-init', config.appid) |
315 else: |
315 else: |
316 print ('-> nevermind, you can do it later with ' |
316 print ('-> nevermind, you can do it later with ' |
317 '"cubicweb-ctl db-init %s".' % self.config.appid) |
317 '"cubicweb-ctl db-init %s".' % self.config.appid) |
318 |
318 |
329 """ |
329 """ |
330 name = 'db-init' |
330 name = 'db-init' |
331 arguments = '<instance>' |
331 arguments = '<instance>' |
332 |
332 |
333 options = ( |
333 options = ( |
334 ("drop", |
334 ('drop', |
335 {'short': 'd', 'action': 'store_true', |
335 {'short': 'd', 'action': 'store_true', |
336 'default': False, |
336 'default': False, |
337 'help': 'insert drop statements to remove previously existant \ |
337 'help': 'insert drop statements to remove previously existant \ |
338 tables, indexes... (no by default)'}), |
338 tables, indexes... (no by default)'}), |
339 ) |
339 ) |
340 |
340 |
341 def run(self, args): |
341 def run(self, args): |
342 print '\n'+underline_title('Initializing the "system database"') |
342 print '\n'+underline_title('Initializing the system database') |
343 from cubicweb.server import init_repository |
343 from cubicweb.server import init_repository |
344 appid = pop_arg(args, msg="No instance specified !") |
344 appid = pop_arg(args, msg='No instance specified !') |
345 config = ServerConfiguration.config_for(appid) |
345 config = ServerConfiguration.config_for(appid) |
346 init_repository(config, drop=self.config.drop) |
346 init_repository(config, drop=self.config.drop) |
347 |
347 |
348 |
348 |
349 class GrantUserOnInstanceCommand(Command): |
349 class GrantUserOnInstanceCommand(Command): |
356 """ |
356 """ |
357 name = 'db-grant-user' |
357 name = 'db-grant-user' |
358 arguments = '<instance> <user>' |
358 arguments = '<instance> <user>' |
359 |
359 |
360 options = ( |
360 options = ( |
361 ("set-owner", |
361 ('set-owner', |
362 {'short': 'o', 'type' : "yn", 'metavar' : '<yes or no>', |
362 {'short': 'o', 'type' : 'yn', 'metavar' : '<yes or no>', |
363 'default' : False, |
363 'default' : False, |
364 'help': 'Set the user as tables owner if yes (no by default).'} |
364 'help': 'Set the user as tables owner if yes (no by default).'} |
365 ), |
365 ), |
366 ) |
366 ) |
367 def run(self, args): |
367 def run(self, args): |
368 """run the command with its specific arguments""" |
368 """run the command with its specific arguments""" |
369 from cubicweb.server.sqlutils import sqlexec, sqlgrants |
369 from cubicweb.server.sqlutils import sqlexec, sqlgrants |
370 appid = pop_arg(args, 1, msg="No instance specified !") |
370 appid = pop_arg(args, 1, msg='No instance specified !') |
371 user = pop_arg(args, msg="No user specified !") |
371 user = pop_arg(args, msg='No user specified !') |
372 config = ServerConfiguration.config_for(appid) |
372 config = ServerConfiguration.config_for(appid) |
373 source = config.sources()['system'] |
373 source = config.sources()['system'] |
374 set_owner = self.config.set_owner |
374 set_owner = self.config.set_owner |
375 cnx = system_source_cnx(source, special_privs='GRANT') |
375 cnx = system_source_cnx(source, special_privs='GRANT') |
376 cursor = cnx.cursor() |
376 cursor = cnx.cursor() |
398 |
398 |
399 def run(self, args): |
399 def run(self, args): |
400 """run the command with its specific arguments""" |
400 """run the command with its specific arguments""" |
401 from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX |
401 from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX |
402 from cubicweb.server.utils import crypt_password, manager_userpasswd |
402 from cubicweb.server.utils import crypt_password, manager_userpasswd |
403 appid = pop_arg(args, 1, msg="No instance specified !") |
403 appid = pop_arg(args, 1, msg='No instance specified !') |
404 config = ServerConfiguration.config_for(appid) |
404 config = ServerConfiguration.config_for(appid) |
405 sourcescfg = config.read_sources_file() |
405 sourcescfg = config.read_sources_file() |
406 try: |
406 try: |
407 adminlogin = sourcescfg['admin']['login'] |
407 adminlogin = sourcescfg['admin']['login'] |
408 except KeyError: |
408 except KeyError: |
442 """ |
442 """ |
443 name = 'start-repository' |
443 name = 'start-repository' |
444 arguments = '<instance>' |
444 arguments = '<instance>' |
445 |
445 |
446 options = ( |
446 options = ( |
447 ("debug", |
447 ('debug', |
448 {'short': 'D', 'action' : 'store_true', |
448 {'short': 'D', 'action' : 'store_true', |
449 'help': 'start server in debug mode.'}), |
449 'help': 'start server in debug mode.'}), |
450 ) |
450 ) |
451 |
451 |
452 def run(self, args): |
452 def run(self, args): |
453 from cubicweb.server.server import RepositoryServer |
453 from cubicweb.server.server import RepositoryServer |
454 appid = pop_arg(args, msg="No instance specified !") |
454 appid = pop_arg(args, msg='No instance specified !') |
455 config = ServerConfiguration.config_for(appid) |
455 config = ServerConfiguration.config_for(appid) |
456 debug = self.config.debug |
456 debug = self.config.debug |
457 # create the server |
457 # create the server |
458 server = RepositoryServer(config, debug) |
458 server = RepositoryServer(config, debug) |
459 # go ! (don't daemonize in debug mode) |
459 # go ! (don't daemonize in debug mode) |
471 server.connect(config['host'], 0) |
471 server.connect(config['host'], 0) |
472 server.run() |
472 server.run() |
473 |
473 |
474 |
474 |
475 def _remote_dump(host, appid, output, sudo=False): |
475 def _remote_dump(host, appid, output, sudo=False): |
|
476 # XXX generate unique/portable file name |
476 dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s.dump %s' % (appid, appid) |
477 dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s.dump %s' % (appid, appid) |
477 if sudo: |
478 if sudo: |
478 dmpcmd = 'sudo %s' % (dmpcmd) |
479 dmpcmd = 'sudo %s' % (dmpcmd) |
479 dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd) |
480 dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd) |
480 print dmpcmd |
481 print dmpcmd |
495 raise ExecutionError('Error while deleting remote dump') |
496 raise ExecutionError('Error while deleting remote dump') |
496 |
497 |
497 def _local_dump(appid, output): |
498 def _local_dump(appid, output): |
498 config = ServerConfiguration.config_for(appid) |
499 config = ServerConfiguration.config_for(appid) |
499 # schema=1 to avoid unnecessary schema loading |
500 # schema=1 to avoid unnecessary schema loading |
500 mih = config.migration_handler(connect=False, schema=1) |
501 mih = config.migration_handler(connect=False, schema=1, verbosity=1) |
501 mih.backup_database(output, askconfirm=False) |
502 mih.backup_database(output, askconfirm=False) |
502 |
503 mih.shutdown() |
503 def _local_restore(appid, backupfile, drop): |
504 |
|
505 def _local_restore(appid, backupfile, drop, systemonly=True): |
504 config = ServerConfiguration.config_for(appid) |
506 config = ServerConfiguration.config_for(appid) |
|
507 config.verbosity = 1 # else we won't be asked for confirmation on problems |
505 # schema=1 to avoid unnecessary schema loading |
508 # schema=1 to avoid unnecessary schema loading |
506 mih = config.migration_handler(connect=False, schema=1) |
509 mih = config.migration_handler(connect=False, schema=1, verbosity=1) |
507 mih.restore_database(backupfile, drop) |
510 mih.restore_database(backupfile, drop, systemonly, askconfirm=False) |
508 repo = mih.repo_connect() |
511 repo = mih.repo_connect() |
509 # version of the database |
512 # version of the database |
510 dbversions = repo.get_versions() |
513 dbversions = repo.get_versions() |
511 mih.shutdown() |
514 mih.shutdown() |
512 if not dbversions: |
515 if not dbversions: |
540 return 'needapplupgrade' |
543 return 'needapplupgrade' |
541 for cube in config.cubes(): |
544 for cube in config.cubes(): |
542 try: |
545 try: |
543 softversion = config.cube_version(cube) |
546 softversion = config.cube_version(cube) |
544 except ConfigurationError: |
547 except ConfigurationError: |
545 print "-> Error: no cube version information for %s, please check that the cube is installed." % cube |
548 print '-> Error: no cube version information for %s, please check that the cube is installed.' % cube |
546 continue |
549 continue |
547 try: |
550 try: |
548 applversion = vcconf[cube] |
551 applversion = vcconf[cube] |
549 except KeyError: |
552 except KeyError: |
550 print "-> Error: no cube version information for %s in version configuration." % cube |
553 print '-> Error: no cube version information for %s in version configuration.' % cube |
551 continue |
554 continue |
552 if softversion == applversion: |
555 if softversion == applversion: |
553 continue |
556 continue |
554 if softversion > applversion: |
557 if softversion > applversion: |
555 return 'needsoftupgrade' |
558 return 'needsoftupgrade' |
580 'help': 'Use sudo on the remote host.'} |
583 'help': 'Use sudo on the remote host.'} |
581 ), |
584 ), |
582 ) |
585 ) |
583 |
586 |
584 def run(self, args): |
587 def run(self, args): |
585 appid = pop_arg(args, 1, msg="No instance specified !") |
588 appid = pop_arg(args, 1, msg='No instance specified !') |
586 if ':' in appid: |
589 if ':' in appid: |
587 host, appid = appid.split(':') |
590 host, appid = appid.split(':') |
588 _remote_dump(host, appid, self.config.output, self.config.sudo) |
591 _remote_dump(host, appid, self.config.output, self.config.sudo) |
589 else: |
592 else: |
590 _local_dump(appid, self.config.output) |
593 _local_dump(appid, self.config.output) |
598 """ |
601 """ |
599 name = 'db-restore' |
602 name = 'db-restore' |
600 arguments = '<instance> <backupfile>' |
603 arguments = '<instance> <backupfile>' |
601 |
604 |
602 options = ( |
605 options = ( |
603 ("no-drop", |
606 ('no-drop', |
604 {'short': 'n', 'action' : 'store_true', |
607 {'short': 'n', 'action' : 'store_true', 'default' : False, |
605 'default' : False, |
|
606 'help': 'for some reason the database doesn\'t exist and so ' |
608 'help': 'for some reason the database doesn\'t exist and so ' |
607 'should not be dropped.'} |
609 'should not be dropped.'} |
608 ), |
610 ), |
|
611 ('restore-all', |
|
612 {'short': 'r', 'action' : 'store_true', 'default' : False, |
|
613 'help': 'restore everything, eg not only the system source database ' |
|
614 'but also data for all sources supporting backup/restore and custom ' |
|
615 'instance data. In that case, <backupfile> is expected to be the ' |
|
616 'timestamp of the backup to restore, not a file'} |
|
617 ), |
609 ) |
618 ) |
610 |
619 |
611 def run(self, args): |
620 def run(self, args): |
612 appid = pop_arg(args, 1, msg="No instance specified !") |
621 appid = pop_arg(args, 1, msg='No instance specified !') |
613 backupfile = pop_arg(args, msg="No backup file specified !") |
622 backupfile = pop_arg(args, msg='No backup file or timestamp specified !') |
614 _local_restore(appid, backupfile, not self.config.no_drop) |
623 _local_restore(appid, backupfile, |
|
624 drop=not self.config.no_drop, |
|
625 systemonly=not self.config.restore_all) |
615 |
626 |
616 |
627 |
617 class DBCopyCommand(Command): |
628 class DBCopyCommand(Command): |
618 """Copy the system database of an instance (backup and restore). |
629 """Copy the system database of an instance (backup and restore). |
619 |
630 |
626 """ |
637 """ |
627 name = 'db-copy' |
638 name = 'db-copy' |
628 arguments = '<src-instance> <dest-instance>' |
639 arguments = '<src-instance> <dest-instance>' |
629 |
640 |
630 options = ( |
641 options = ( |
631 ("no-drop", |
642 ('no-drop', |
632 {'short': 'n', 'action' : 'store_true', |
643 {'short': 'n', 'action' : 'store_true', |
633 'default' : False, |
644 'default' : False, |
634 'help': 'For some reason the database doesn\'t exist and so ' |
645 'help': 'For some reason the database doesn\'t exist and so ' |
635 'should not be dropped.'} |
646 'should not be dropped.'} |
636 ), |
647 ), |
637 ("keep-dump", |
648 ('keep-dump', |
638 {'short': 'k', 'action' : 'store_true', |
649 {'short': 'k', 'action' : 'store_true', |
639 'default' : False, |
650 'default' : False, |
640 'help': 'Specify that the dump file should not be automatically removed.'} |
651 'help': 'Specify that the dump file should not be automatically removed.'} |
641 ), |
652 ), |
642 ('sudo', |
653 ('sudo', |
646 ), |
657 ), |
647 ) |
658 ) |
648 |
659 |
649 def run(self, args): |
660 def run(self, args): |
650 import tempfile |
661 import tempfile |
651 srcappid = pop_arg(args, 1, msg="No source instance specified !") |
662 srcappid = pop_arg(args, 1, msg='No source instance specified !') |
652 destappid = pop_arg(args, msg="No destination instance specified !") |
663 destappid = pop_arg(args, msg='No destination instance specified !') |
653 _, output = tempfile.mkstemp() |
664 _, output = tempfile.mkstemp() |
654 if ':' in srcappid: |
665 if ':' in srcappid: |
655 host, srcappid = srcappid.split(':') |
666 host, srcappid = srcappid.split(':') |
656 _remote_dump(host, srcappid, output, self.config.sudo) |
667 _remote_dump(host, srcappid, output, self.config.sudo) |
657 else: |
668 else: |
671 """ |
682 """ |
672 name = 'db-check' |
683 name = 'db-check' |
673 arguments = '<instance>' |
684 arguments = '<instance>' |
674 |
685 |
675 options = ( |
686 options = ( |
676 ("checks", |
687 ('checks', |
677 {'short': 'c', 'type' : "csv", 'metavar' : '<check list>', |
688 {'short': 'c', 'type' : 'csv', 'metavar' : '<check list>', |
678 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
689 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
679 'help': 'Comma separated list of check to run. By default run all \ |
690 'help': 'Comma separated list of check to run. By default run all \ |
680 checks, i.e. entities, relations, text_index and metadata.'} |
691 checks, i.e. entities, relations, text_index and metadata.'} |
681 ), |
692 ), |
682 |
693 |
683 ("autofix", |
694 ('autofix', |
684 {'short': 'a', 'type' : "yn", 'metavar' : '<yes or no>', |
695 {'short': 'a', 'type' : 'yn', 'metavar' : '<yes or no>', |
685 'default' : False, |
696 'default' : False, |
686 'help': 'Automatically correct integrity problems if this option \ |
697 'help': 'Automatically correct integrity problems if this option \ |
687 is set to "y" or "yes", else only display them'} |
698 is set to "y" or "yes", else only display them'} |
688 ), |
699 ), |
689 ("reindex", |
700 ('reindex', |
690 {'short': 'r', 'type' : "yn", 'metavar' : '<yes or no>', |
701 {'short': 'r', 'type' : 'yn', 'metavar' : '<yes or no>', |
691 'default' : False, |
702 'default' : False, |
692 'help': 're-indexes the database for full text search if this \ |
703 'help': 're-indexes the database for full text search if this \ |
693 option is set to "y" or "yes" (may be long for large database).'} |
704 option is set to "y" or "yes" (may be long for large database).'} |
694 ), |
705 ), |
695 ("force", |
706 ('force', |
696 {'short': 'f', 'action' : "store_true", |
707 {'short': 'f', 'action' : 'store_true', |
697 'default' : False, |
708 'default' : False, |
698 'help': 'don\'t check instance is up to date.'} |
709 'help': 'don\'t check instance is up to date.'} |
699 ), |
710 ), |
700 |
711 |
701 ) |
712 ) |
702 |
713 |
703 def run(self, args): |
714 def run(self, args): |
704 from cubicweb.server.checkintegrity import check |
715 from cubicweb.server.checkintegrity import check |
705 appid = pop_arg(args, 1, msg="No instance specified !") |
716 appid = pop_arg(args, 1, msg='No instance specified !') |
706 config = ServerConfiguration.config_for(appid) |
717 config = ServerConfiguration.config_for(appid) |
707 config.repairing = self.config.force |
718 config.repairing = self.config.force |
708 repo, cnx = repo_cnx(config) |
719 repo, cnx = repo_cnx(config) |
709 check(repo, cnx, |
720 check(repo, cnx, |
710 self.config.checks, self.config.reindex, self.config.autofix) |
721 self.config.checks, self.config.reindex, self.config.autofix) |
721 |
732 |
722 options = () |
733 options = () |
723 |
734 |
724 def run(self, args): |
735 def run(self, args): |
725 from cubicweb.server.checkintegrity import reindex_entities |
736 from cubicweb.server.checkintegrity import reindex_entities |
726 appid = pop_arg(args, 1, msg="No instance specified !") |
737 appid = pop_arg(args, 1, msg='No instance specified !') |
727 config = ServerConfiguration.config_for(appid) |
738 config = ServerConfiguration.config_for(appid) |
728 repo, cnx = repo_cnx(config) |
739 repo, cnx = repo_cnx(config) |
729 session = repo._get_session(cnx.sessionid, setpool=True) |
740 session = repo._get_session(cnx.sessionid, setpool=True) |
730 reindex_entities(repo.schema, session) |
741 reindex_entities(repo.schema, session) |
731 cnx.commit() |
742 cnx.commit() |