24 # completion). So import locally in command helpers. |
24 # completion). So import locally in command helpers. |
25 import sys |
25 import sys |
26 import os |
26 import os |
27 |
27 |
28 from logilab.common.configuration import Configuration |
28 from logilab.common.configuration import Configuration |
29 from logilab.common.clcommands import register_commands, cmd_run, pop_arg |
|
30 from logilab.common.shellutils import ASK |
29 from logilab.common.shellutils import ASK |
31 |
30 |
32 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError |
31 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError |
33 from cubicweb.toolsutils import Command, CommandHandler, underline_title |
32 from cubicweb.toolsutils import Command, CommandHandler, underline_title |
|
33 from cubicweb.cwctl import CWCTL |
34 from cubicweb.server import SOURCE_TYPES |
34 from cubicweb.server import SOURCE_TYPES |
35 from cubicweb.server.serverconfig import (USER_OPTIONS, ServerConfiguration, |
35 from cubicweb.server.serverconfig import (USER_OPTIONS, ServerConfiguration, |
36 SourceConfiguration) |
36 SourceConfiguration) |
37 |
37 |
38 # utility functions ########################################################### |
38 # utility functions ########################################################### |
215 config.write_bootstrap_cubes_file(cubes) |
215 config.write_bootstrap_cubes_file(cubes) |
216 |
216 |
217 def postcreate(self): |
217 def postcreate(self): |
218 if ASK.confirm('Run db-create to create the system database ?'): |
218 if ASK.confirm('Run db-create to create the system database ?'): |
219 verbosity = (self.config.mode == 'installed') and 'y' or 'n' |
219 verbosity = (self.config.mode == 'installed') and 'y' or 'n' |
220 cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity) |
220 CWCTL.run(['db-create', self.config.appid, '--verbose=%s' % verbosity]) |
221 else: |
221 else: |
222 print ('-> nevermind, you can do it later with ' |
222 print ('-> nevermind, you can do it later with ' |
223 '"cubicweb-ctl db-create %s".' % self.config.appid) |
223 '"cubicweb-ctl db-create %s".' % self.config.appid) |
224 |
224 |
225 |
225 |
297 <instance> |
297 <instance> |
298 the identifier of the instance to initialize. |
298 the identifier of the instance to initialize. |
299 """ |
299 """ |
300 name = 'db-create' |
300 name = 'db-create' |
301 arguments = '<instance>' |
301 arguments = '<instance>' |
302 |
302 min_args = max_args = 1 |
303 options = ( |
303 options = ( |
304 ('create-db', |
304 ('create-db', |
305 {'short': 'c', 'type': 'yn', 'metavar': '<y or n>', |
305 {'short': 'c', 'type': 'yn', 'metavar': '<y or n>', |
306 'default': True, |
306 'default': True, |
307 'help': 'create the database (yes by default)'}), |
307 'help': 'create the database (yes by default)'}), |
321 def run(self, args): |
321 def run(self, args): |
322 """run the command with its specific arguments""" |
322 """run the command with its specific arguments""" |
323 from logilab.database import get_db_helper |
323 from logilab.database import get_db_helper |
324 verbose = self.get('verbose') |
324 verbose = self.get('verbose') |
325 automatic = self.get('automatic') |
325 automatic = self.get('automatic') |
326 appid = pop_arg(args, msg='No instance specified !') |
326 appid = args.pop() |
327 config = ServerConfiguration.config_for(appid) |
327 config = ServerConfiguration.config_for(appid) |
328 source = config.sources()['system'] |
328 source = config.sources()['system'] |
329 dbname = source['db-name'] |
329 dbname = source['db-name'] |
330 driver = source['db-driver'] |
330 driver = source['db-driver'] |
331 helper = get_db_helper(driver) |
331 helper = get_db_helper(driver) |
369 cursor.close() |
369 cursor.close() |
370 cnx.commit() |
370 cnx.commit() |
371 print '-> database for instance %s created and necessary extensions installed.' % appid |
371 print '-> database for instance %s created and necessary extensions installed.' % appid |
372 print |
372 print |
373 if automatic or ASK.confirm('Run db-init to initialize the system database ?'): |
373 if automatic or ASK.confirm('Run db-init to initialize the system database ?'): |
374 cmd_run('db-init', config.appid) |
374 CWCTL.run(['db-init', config.appid]) |
375 else: |
375 else: |
376 print ('-> nevermind, you can do it later with ' |
376 print ('-> nevermind, you can do it later with ' |
377 '"cubicweb-ctl db-init %s".' % config.appid) |
377 '"cubicweb-ctl db-init %s".' % config.appid) |
378 |
378 |
379 |
379 |
387 <instance> |
387 <instance> |
388 the identifier of the instance to initialize. |
388 the identifier of the instance to initialize. |
389 """ |
389 """ |
390 name = 'db-init' |
390 name = 'db-init' |
391 arguments = '<instance>' |
391 arguments = '<instance>' |
392 |
392 min_args = max_args = 1 |
393 options = ( |
393 options = ( |
394 ('drop', |
394 ('drop', |
395 {'short': 'd', 'action': 'store_true', |
395 {'short': 'd', 'action': 'store_true', |
396 'default': False, |
396 'default': False, |
397 'help': 'insert drop statements to remove previously existant \ |
397 'help': 'insert drop statements to remove previously existant \ |
400 |
400 |
401 def run(self, args): |
401 def run(self, args): |
402 print '\n'+underline_title('Initializing the system database') |
402 print '\n'+underline_title('Initializing the system database') |
403 from cubicweb.server import init_repository |
403 from cubicweb.server import init_repository |
404 from logilab.database import get_connection |
404 from logilab.database import get_connection |
405 appid = pop_arg(args, msg='No instance specified !') |
405 appid = args[0] |
406 config = ServerConfiguration.config_for(appid) |
406 config = ServerConfiguration.config_for(appid) |
407 try: |
407 try: |
408 system = config.sources()['system'] |
408 system = config.sources()['system'] |
409 extra_args=system.get('db-extra-arguments') |
409 extra_args=system.get('db-extra-arguments') |
410 extra = extra_args and {'extra_args': extra_args} or {} |
410 extra = extra_args and {'extra_args': extra_args} or {} |
429 <user> |
429 <user> |
430 the database's user requiring grant access |
430 the database's user requiring grant access |
431 """ |
431 """ |
432 name = 'db-grant-user' |
432 name = 'db-grant-user' |
433 arguments = '<instance> <user>' |
433 arguments = '<instance> <user>' |
434 |
434 min_args = max_args = 2 |
435 options = ( |
435 options = ( |
436 ('set-owner', |
436 ('set-owner', |
437 {'short': 'o', 'type' : 'yn', 'metavar' : '<yes or no>', |
437 {'short': 'o', 'type' : 'yn', 'metavar' : '<yes or no>', |
438 'default' : False, |
438 'default' : False, |
439 'help': 'Set the user as tables owner if yes (no by default).'} |
439 'help': 'Set the user as tables owner if yes (no by default).'} |
440 ), |
440 ), |
441 ) |
441 ) |
442 def run(self, args): |
442 def run(self, args): |
443 """run the command with its specific arguments""" |
443 """run the command with its specific arguments""" |
444 from cubicweb.server.sqlutils import sqlexec, sqlgrants |
444 from cubicweb.server.sqlutils import sqlexec, sqlgrants |
445 appid = pop_arg(args, 1, msg='No instance specified !') |
445 appid, user = args |
446 user = pop_arg(args, msg='No user specified !') |
|
447 config = ServerConfiguration.config_for(appid) |
446 config = ServerConfiguration.config_for(appid) |
448 source = config.sources()['system'] |
447 source = config.sources()['system'] |
449 set_owner = self.config.set_owner |
448 set_owner = self.config.set_owner |
450 cnx = system_source_cnx(source, special_privs='GRANT') |
449 cnx = system_source_cnx(source, special_privs='GRANT') |
451 cursor = cnx.cursor() |
450 cursor = cnx.cursor() |
455 set_owner=set_owner), cursor) |
454 set_owner=set_owner), cursor) |
456 except Exception, ex: |
455 except Exception, ex: |
457 cnx.rollback() |
456 cnx.rollback() |
458 import traceback |
457 import traceback |
459 traceback.print_exc() |
458 traceback.print_exc() |
460 print '-> an error occured:', ex |
459 print '-> an error occurred:', ex |
461 else: |
460 else: |
462 cnx.commit() |
461 cnx.commit() |
463 print '-> rights granted to %s on instance %s.' % (appid, user) |
462 print '-> rights granted to %s on instance %s.' % (appid, user) |
464 |
463 |
465 |
464 |
473 arguments = '<instance>' |
472 arguments = '<instance>' |
474 |
473 |
475 def run(self, args): |
474 def run(self, args): |
476 """run the command with its specific arguments""" |
475 """run the command with its specific arguments""" |
477 from cubicweb.server.utils import crypt_password, manager_userpasswd |
476 from cubicweb.server.utils import crypt_password, manager_userpasswd |
478 appid = pop_arg(args, 1, msg='No instance specified !') |
477 appid = args[0] |
479 config = ServerConfiguration.config_for(appid) |
478 config = ServerConfiguration.config_for(appid) |
480 sourcescfg = config.read_sources_file() |
479 sourcescfg = config.read_sources_file() |
481 try: |
480 try: |
482 adminlogin = sourcescfg['admin']['login'] |
481 adminlogin = sourcescfg['admin']['login'] |
483 except KeyError: |
482 except KeyError: |
507 config.write_sources_file(sourcescfg) |
506 config.write_sources_file(sourcescfg) |
508 except Exception, ex: |
507 except Exception, ex: |
509 cnx.rollback() |
508 cnx.rollback() |
510 import traceback |
509 import traceback |
511 traceback.print_exc() |
510 traceback.print_exc() |
512 print '-> an error occured:', ex |
511 print '-> an error occurred:', ex |
513 else: |
512 else: |
514 cnx.commit() |
513 cnx.commit() |
515 print '-> password reset, sources file regenerated.' |
514 print '-> password reset, sources file regenerated.' |
516 cnx.close() |
515 cnx.close() |
517 |
516 |
524 <instance> |
523 <instance> |
525 the identifier of the instance to initialize. |
524 the identifier of the instance to initialize. |
526 """ |
525 """ |
527 name = 'start-repository' |
526 name = 'start-repository' |
528 arguments = '<instance>' |
527 arguments = '<instance>' |
529 |
528 min_args = max_args = 1 |
530 options = ( |
529 options = ( |
531 ('debug', |
530 ('debug', |
532 {'short': 'D', 'action' : 'store_true', |
531 {'short': 'D', 'action' : 'store_true', |
533 'help': 'start server in debug mode.'}), |
532 'help': 'start server in debug mode.'}), |
534 ('loglevel', |
533 ('loglevel', |
540 |
539 |
541 def run(self, args): |
540 def run(self, args): |
542 from logilab.common.daemon import daemonize |
541 from logilab.common.daemon import daemonize |
543 from cubicweb.cwctl import init_cmdline_log_threshold |
542 from cubicweb.cwctl import init_cmdline_log_threshold |
544 from cubicweb.server.server import RepositoryServer |
543 from cubicweb.server.server import RepositoryServer |
545 appid = pop_arg(args, msg='No instance specified !') |
544 appid = args[0] |
546 debug = self['debug'] |
545 debug = self['debug'] |
547 if sys.platform == 'win32' and not debug: |
546 if sys.platform == 'win32' and not debug: |
548 from logging import getLogger |
547 from logging import getLogger |
549 logger = getLogger('cubicweb.ctl') |
548 logger = getLogger('cubicweb.ctl') |
550 logger.info('Forcing debug mode on win32 platform') |
549 logger.info('Forcing debug mode on win32 platform') |
593 if os.system(cmd): |
592 if os.system(cmd): |
594 raise ExecutionError('Error while retrieving the dump at /tmp/%s' % filename) |
593 raise ExecutionError('Error while retrieving the dump at /tmp/%s' % filename) |
595 rmcmd = 'ssh -t %s "rm -f /tmp/%s"' % (host, filename) |
594 rmcmd = 'ssh -t %s "rm -f /tmp/%s"' % (host, filename) |
596 print rmcmd |
595 print rmcmd |
597 if os.system(rmcmd) and not ASK.confirm( |
596 if os.system(rmcmd) and not ASK.confirm( |
598 'An error occured while deleting remote dump at /tmp/%s. ' |
597 'An error occurred while deleting remote dump at /tmp/%s. ' |
599 'Continue anyway?' % filename): |
598 'Continue anyway?' % filename): |
600 raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename) |
599 raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename) |
601 |
600 |
602 def _local_dump(appid, output): |
601 def _local_dump(appid, output): |
603 config = ServerConfiguration.config_for(appid) |
602 config = ServerConfiguration.config_for(appid) |
671 the identifier of the instance to backup |
670 the identifier of the instance to backup |
672 format [[user@]host:]appname |
671 format [[user@]host:]appname |
673 """ |
672 """ |
674 name = 'db-dump' |
673 name = 'db-dump' |
675 arguments = '<instance>' |
674 arguments = '<instance>' |
676 |
675 min_args = max_args = 1 |
677 options = ( |
676 options = ( |
678 ('output', |
677 ('output', |
679 {'short': 'o', 'type' : 'string', 'metavar' : '<file>', |
678 {'short': 'o', 'type' : 'string', 'metavar' : '<file>', |
680 'default' : None, |
679 'default' : None, |
681 'help': 'Specify the backup file where the backup will be stored.'} |
680 'help': 'Specify the backup file where the backup will be stored.'} |
686 'help': 'Use sudo on the remote host.'} |
685 'help': 'Use sudo on the remote host.'} |
687 ), |
686 ), |
688 ) |
687 ) |
689 |
688 |
690 def run(self, args): |
689 def run(self, args): |
691 appid = pop_arg(args, 1, msg='No instance specified !') |
690 appid = args[0] |
692 if ':' in appid: |
691 if ':' in appid: |
693 host, appid = appid.split(':') |
692 host, appid = appid.split(':') |
694 _remote_dump(host, appid, self.config.output, self.config.sudo) |
693 _remote_dump(host, appid, self.config.output, self.config.sudo) |
695 else: |
694 else: |
696 _local_dump(appid, self.config.output) |
695 _local_dump(appid, self.config.output) |
702 <instance> |
701 <instance> |
703 the identifier of the instance to restore |
702 the identifier of the instance to restore |
704 """ |
703 """ |
705 name = 'db-restore' |
704 name = 'db-restore' |
706 arguments = '<instance> <backupfile>' |
705 arguments = '<instance> <backupfile>' |
|
706 min_args = max_args = 2 |
707 |
707 |
708 options = ( |
708 options = ( |
709 ('no-drop', |
709 ('no-drop', |
710 {'short': 'n', 'action' : 'store_true', 'default' : False, |
710 {'short': 'n', 'action' : 'store_true', 'default' : False, |
711 'help': 'for some reason the database doesn\'t exist and so ' |
711 'help': 'for some reason the database doesn\'t exist and so ' |
719 'timestamp of the backup to restore, not a file'} |
719 'timestamp of the backup to restore, not a file'} |
720 ), |
720 ), |
721 ) |
721 ) |
722 |
722 |
723 def run(self, args): |
723 def run(self, args): |
724 appid = pop_arg(args, 1, msg='No instance specified !') |
724 appid, backupfile = args |
725 backupfile = pop_arg(args, msg='No backup file or timestamp specified !') |
|
726 _local_restore(appid, backupfile, |
725 _local_restore(appid, backupfile, |
727 drop=not self.config.no_drop, |
726 drop=not self.config.no_drop, |
728 systemonly=not self.config.restore_all) |
727 systemonly=not self.config.restore_all) |
729 |
728 |
730 |
729 |
738 <dest-instance> |
737 <dest-instance> |
739 the identifier of the instance to restore |
738 the identifier of the instance to restore |
740 """ |
739 """ |
741 name = 'db-copy' |
740 name = 'db-copy' |
742 arguments = '<src-instance> <dest-instance>' |
741 arguments = '<src-instance> <dest-instance>' |
743 |
742 min_args = max_args = 2 |
744 options = ( |
743 options = ( |
745 ('no-drop', |
744 ('no-drop', |
746 {'short': 'n', 'action' : 'store_true', |
745 {'short': 'n', 'action' : 'store_true', |
747 'default' : False, |
746 'default' : False, |
748 'help': 'For some reason the database doesn\'t exist and so ' |
747 'help': 'For some reason the database doesn\'t exist and so ' |
760 ), |
759 ), |
761 ) |
760 ) |
762 |
761 |
763 def run(self, args): |
762 def run(self, args): |
764 import tempfile |
763 import tempfile |
765 srcappid = pop_arg(args, 1, msg='No source instance specified !') |
764 srcappid, destappid = args |
766 destappid = pop_arg(args, msg='No destination instance specified !') |
|
767 fd, output = tempfile.mkstemp() |
765 fd, output = tempfile.mkstemp() |
768 os.close(fd) |
766 os.close(fd) |
769 if ':' in srcappid: |
767 if ':' in srcappid: |
770 host, srcappid = srcappid.split(':') |
768 host, srcappid = srcappid.split(':') |
771 _remote_dump(host, srcappid, output, self.config.sudo) |
769 _remote_dump(host, srcappid, output, self.config.sudo) |
784 <instance> |
782 <instance> |
785 the identifier of the instance to check |
783 the identifier of the instance to check |
786 """ |
784 """ |
787 name = 'db-check' |
785 name = 'db-check' |
788 arguments = '<instance>' |
786 arguments = '<instance>' |
789 |
787 min_args = max_args = 1 |
790 options = ( |
788 options = ( |
791 ('checks', |
789 ('checks', |
792 {'short': 'c', 'type' : 'csv', 'metavar' : '<check list>', |
790 {'short': 'c', 'type' : 'csv', 'metavar' : '<check list>', |
793 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
791 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
794 'help': 'Comma separated list of check to run. By default run all \ |
792 'help': 'Comma separated list of check to run. By default run all \ |
814 ), |
812 ), |
815 |
813 |
816 ) |
814 ) |
817 |
815 |
818 def run(self, args): |
816 def run(self, args): |
819 from cubicweb.server.checkintegrity import check |
817 appid = args[0] |
820 appid = pop_arg(args, 1, msg='No instance specified !') |
|
821 config = ServerConfiguration.config_for(appid) |
818 config = ServerConfiguration.config_for(appid) |
822 config.repairing = self.config.force |
819 config.repairing = self.config.force |
823 repo, cnx = repo_cnx(config) |
820 repo, cnx = repo_cnx(config) |
824 check(repo, cnx, |
821 check(repo, cnx, |
825 self.config.checks, self.config.reindex, self.config.autofix) |
822 self.config.checks, self.config.reindex, self.config.autofix) |
831 <instance> |
828 <instance> |
832 the identifier of the instance to rebuild |
829 the identifier of the instance to rebuild |
833 """ |
830 """ |
834 name = 'db-rebuild-fti' |
831 name = 'db-rebuild-fti' |
835 arguments = '<instance>' |
832 arguments = '<instance>' |
836 |
833 min_args = max_args = 1 |
837 options = () |
|
838 |
834 |
839 def run(self, args): |
835 def run(self, args): |
840 from cubicweb.server.checkintegrity import reindex_entities |
836 from cubicweb.server.checkintegrity import reindex_entities |
841 appid = pop_arg(args, 1, msg='No instance specified !') |
837 appid = args[0] |
842 config = ServerConfiguration.config_for(appid) |
838 config = ServerConfiguration.config_for(appid) |
843 repo, cnx = repo_cnx(config) |
839 repo, cnx = repo_cnx(config) |
844 session = repo._get_session(cnx.sessionid, setpool=True) |
840 session = repo._get_session(cnx.sessionid, setpool=True) |
845 reindex_entities(repo.schema, session) |
841 reindex_entities(repo.schema, session) |
846 cnx.commit() |
842 cnx.commit() |
855 <instance> |
851 <instance> |
856 the identifier of the instance to synchronize. |
852 the identifier of the instance to synchronize. |
857 """ |
853 """ |
858 name = 'schema-sync' |
854 name = 'schema-sync' |
859 arguments = '<instance>' |
855 arguments = '<instance>' |
860 |
856 min_args = max_args = 1 |
861 def run(self, args): |
857 |
862 appid = pop_arg(args, msg='No instance specified !') |
858 def run(self, args): |
|
859 appid = args[0] |
863 config = ServerConfiguration.config_for(appid) |
860 config = ServerConfiguration.config_for(appid) |
864 mih = config.migration_handler() |
861 mih = config.migration_handler() |
865 mih.cmd_synchronize_schema() |
862 mih.cmd_synchronize_schema() |
866 |
863 |
867 |
864 |
868 register_commands( (CreateInstanceDBCommand, |
865 class CheckMappingCommand(Command): |
869 InitInstanceCommand, |
866 """Check content of the mapping file of an external source. |
870 GrantUserOnInstanceCommand, |
867 |
871 ResetAdminPasswordCommand, |
868 The mapping is checked against the instance's schema, searching for |
872 StartRepositoryCommand, |
869 inconsistencies or stuff you may have forgotten. It's higly recommanded to |
873 DBDumpCommand, |
870 run it when you setup a multi-sources instance. |
874 DBRestoreCommand, |
871 |
875 DBCopyCommand, |
872 <instance> |
876 CheckRepositoryCommand, |
873 the identifier of the instance. |
877 RebuildFTICommand, |
874 |
878 SynchronizeInstanceSchemaCommand, |
875 <mapping file> |
879 ) ) |
876 the mapping file to check. |
|
877 """ |
|
878 name = 'check-mapping' |
|
879 arguments = '<instance> <mapping file>' |
|
880 min_args = max_args = 2 |
|
881 |
|
882 def run(self, args): |
|
883 from cubicweb.server.checkintegrity import check_mapping |
|
884 from cubicweb.server.sources.pyrorql import load_mapping_file |
|
885 appid, mappingfile = args |
|
886 config = ServerConfiguration.config_for(appid) |
|
887 config.quick_start = True |
|
888 mih = config.migration_handler(connect=False, verbosity=1) |
|
889 repo = mih.repo_connect() # necessary to get cubes |
|
890 check_mapping(config.load_schema(), load_mapping_file(mappingfile)) |
|
891 |
|
892 for cmdclass in (CreateInstanceDBCommand, InitInstanceCommand, |
|
893 GrantUserOnInstanceCommand, ResetAdminPasswordCommand, |
|
894 StartRepositoryCommand, |
|
895 DBDumpCommand, DBRestoreCommand, DBCopyCommand, |
|
896 CheckRepositoryCommand, RebuildFTICommand, |
|
897 SynchronizeInstanceSchemaCommand, |
|
898 CheckMappingCommand, |
|
899 ): |
|
900 CWCTL.register(cmdclass) |