56 return get_connection(driver, dbhost, dbname, user, password=password, |
56 return get_connection(driver, dbhost, dbname, user, password=password, |
57 port=source.get('db-port')) |
57 port=source.get('db-port')) |
58 |
58 |
59 def system_source_cnx(source, dbms_system_base=False, |
59 def system_source_cnx(source, dbms_system_base=False, |
60 special_privs='CREATE/DROP DATABASE', verbose=True): |
60 special_privs='CREATE/DROP DATABASE', verbose=True): |
61 """shortcut to get a connextion to the application system database |
61 """shortcut to get a connextion to the instance system database |
62 defined in the given config. If <dbms_system_base> is True, |
62 defined in the given config. If <dbms_system_base> is True, |
63 connect to the dbms system database instead (for task such as |
63 connect to the dbms system database instead (for task such as |
64 create/drop the application database) |
64 create/drop the instance database) |
65 """ |
65 """ |
66 if dbms_system_base: |
66 if dbms_system_base: |
67 from logilab.common.adbh import get_adv_func_helper |
67 from logilab.common.adbh import get_adv_func_helper |
68 system_db = get_adv_func_helper(source['db-driver']).system_database() |
68 system_db = get_adv_func_helper(source['db-driver']).system_database() |
69 return source_cnx(source, system_db, special_privs=special_privs, verbose=verbose) |
69 return source_cnx(source, system_db, special_privs=special_privs, verbose=verbose) |
117 class RepositoryCreateHandler(CommandHandler): |
117 class RepositoryCreateHandler(CommandHandler): |
118 cmdname = 'create' |
118 cmdname = 'create' |
119 cfgname = 'repository' |
119 cfgname = 'repository' |
120 |
120 |
121 def bootstrap(self, cubes, inputlevel=0): |
121 def bootstrap(self, cubes, inputlevel=0): |
122 """create an application by copying files from the given cube and by |
122 """create an instance by copying files from the given cube and by |
123 asking information necessary to build required configuration files |
123 asking information necessary to build required configuration files |
124 """ |
124 """ |
125 config = self.config |
125 config = self.config |
126 print underline_title('Configuring the repository') |
126 print underline_title('Configuring the repository') |
127 config.input_config('email', inputlevel) |
127 config.input_config('email', inputlevel) |
229 from cubicweb.server.repository import pyro_unregister |
229 from cubicweb.server.repository import pyro_unregister |
230 pyro_unregister(self.config) |
230 pyro_unregister(self.config) |
231 |
231 |
232 |
232 |
233 # repository specific commands ################################################ |
233 # repository specific commands ################################################ |
234 class CreateApplicationDBCommand(Command): |
234 class CreateInstanceDBCommand(Command): |
235 """Create the system database of an application (run after 'create'). |
235 """Create the system database of an instance (run after 'create'). |
236 |
236 |
237 You will be prompted for a login / password to use to connect to |
237 You will be prompted for a login / password to use to connect to |
238 the system database. The given user should have almost all rights |
238 the system database. The given user should have almost all rights |
239 on the database (ie a super user on the dbms allowed to create |
239 on the database (ie a super user on the dbms allowed to create |
240 database, users, languages...). |
240 database, users, languages...). |
241 |
241 |
242 <application> |
242 <instance> |
243 the identifier of the application to initialize. |
243 the identifier of the instance to initialize. |
244 """ |
244 """ |
245 name = 'db-create' |
245 name = 'db-create' |
246 arguments = '<application>' |
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, |
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 application 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) |
306 # install plpythonu/plpgsql language if not installed by the cube |
306 # install plpythonu/plpgsql language if not installed by the cube |
307 for extlang in ('plpythonu', 'plpgsql'): |
307 for extlang in ('plpythonu', 'plpgsql'): |
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 application %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 |
319 |
319 |
320 class InitApplicationCommand(Command): |
320 class InitInstanceCommand(Command): |
321 """Initialize the system database of an application (run after 'db-create'). |
321 """Initialize the system database of an instance (run after 'db-create'). |
322 |
322 |
323 You will be prompted for a login / password to use to connect to |
323 You will be prompted for a login / password to use to connect to |
324 the system database. The given user should have the create tables, |
324 the system database. The given user should have the create tables, |
325 and grant permissions. |
325 and grant permissions. |
326 |
326 |
327 <application> |
327 <instance> |
328 the identifier of the application to initialize. |
328 the identifier of the instance to initialize. |
329 """ |
329 """ |
330 name = 'db-init' |
330 name = 'db-init' |
331 arguments = '<application>' |
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, |
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 application 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 GrantUserOnApplicationCommand(Command): |
349 class GrantUserOnInstanceCommand(Command): |
350 """Grant a database user on a repository system database. |
350 """Grant a database user on a repository system database. |
351 |
351 |
352 <application> |
352 <instance> |
353 the identifier of the application |
353 the identifier of the instance |
354 <user> |
354 <user> |
355 the database's user requiring grant access |
355 the database's user requiring grant access |
356 """ |
356 """ |
357 name = 'db-grant-user' |
357 name = 'db-grant-user' |
358 arguments = '<application> <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, |
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 application 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') |
383 import traceback |
383 import traceback |
384 traceback.print_exc() |
384 traceback.print_exc() |
385 print '-> an error occured:', ex |
385 print '-> an error occured:', ex |
386 else: |
386 else: |
387 cnx.commit() |
387 cnx.commit() |
388 print '-> rights granted to %s on application %s.' % (appid, user) |
388 print '-> rights granted to %s on instance %s.' % (appid, user) |
389 |
389 |
390 class ResetAdminPasswordCommand(Command): |
390 class ResetAdminPasswordCommand(Command): |
391 """Reset the administrator password. |
391 """Reset the administrator password. |
392 |
392 |
393 <application> |
393 <instance> |
394 the identifier of the application |
394 the identifier of the instance |
395 """ |
395 """ |
396 name = 'reset-admin-pwd' |
396 name = 'reset-admin-pwd' |
397 arguments = '<application>' |
397 arguments = '<instance>' |
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 application 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: |
431 cnx.commit() |
431 cnx.commit() |
432 print '-> password reset, sources file regenerated.' |
432 print '-> password reset, sources file regenerated.' |
433 |
433 |
434 |
434 |
435 class StartRepositoryCommand(Command): |
435 class StartRepositoryCommand(Command): |
436 """Start an CubicWeb RQL server for a given application. |
436 """Start an CubicWeb RQL server for a given instance. |
437 |
437 |
438 The server will be accessible through pyro |
438 The server will be accessible through pyro |
439 |
439 |
440 <application> |
440 <instance> |
441 the identifier of the application to initialize. |
441 the identifier of the instance to initialize. |
442 """ |
442 """ |
443 name = 'start-repository' |
443 name = 'start-repository' |
444 arguments = '<application>' |
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 application 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) |
512 if not dbversions: |
512 if not dbversions: |
513 print "bad or missing version information in the database, don't upgrade file system" |
513 print "bad or missing version information in the database, don't upgrade file system" |
514 return |
514 return |
515 # version of installed software |
515 # version of installed software |
516 eversion = dbversions['cubicweb'] |
516 eversion = dbversions['cubicweb'] |
517 status = application_status(config, eversion, dbversions) |
517 status = instance_status(config, eversion, dbversions) |
518 # * database version > installed software |
518 # * database version > installed software |
519 if status == 'needsoftupgrade': |
519 if status == 'needsoftupgrade': |
520 print "database is using some earlier version than installed software!" |
520 print "database is using some earlier version than installed software!" |
521 print "please upgrade your software and then upgrade the instance" |
521 print "please upgrade your software and then upgrade the instance" |
522 print "using command 'cubicweb-ctl upgrade %s'" % config.appid |
522 print "using command 'cubicweb-ctl upgrade %s'" % config.appid |
530 return |
530 return |
531 # * database version = installed software, database version = instance fs version |
531 # * database version = installed software, database version = instance fs version |
532 # ok! |
532 # ok! |
533 |
533 |
534 |
534 |
535 def application_status(config, cubicwebapplversion, vcconf): |
535 def instance_status(config, cubicwebapplversion, vcconf): |
536 cubicwebversion = config.cubicweb_version() |
536 cubicwebversion = config.cubicweb_version() |
537 if cubicwebapplversion > cubicwebversion: |
537 if cubicwebapplversion > cubicwebversion: |
538 return 'needsoftupgrade' |
538 return 'needsoftupgrade' |
539 if cubicwebapplversion < cubicwebversion: |
539 if cubicwebapplversion < cubicwebversion: |
540 return 'needapplupgrade' |
540 return 'needapplupgrade' |
580 'help': 'Use sudo on the remote host.'} |
580 'help': 'Use sudo on the remote host.'} |
581 ), |
581 ), |
582 ) |
582 ) |
583 |
583 |
584 def run(self, args): |
584 def run(self, args): |
585 appid = pop_arg(args, 1, msg="No application specified !") |
585 appid = pop_arg(args, 1, msg="No instance specified !") |
586 if ':' in appid: |
586 if ':' in appid: |
587 host, appid = appid.split(':') |
587 host, appid = appid.split(':') |
588 _remote_dump(host, appid, self.config.output, self.config.sudo) |
588 _remote_dump(host, appid, self.config.output, self.config.sudo) |
589 else: |
589 else: |
590 _local_dump(appid, self.config.output) |
590 _local_dump(appid, self.config.output) |
591 |
591 |
592 |
592 |
593 class DBRestoreCommand(Command): |
593 class DBRestoreCommand(Command): |
594 """Restore the system database of an application. |
594 """Restore the system database of an instance. |
595 |
595 |
596 <application> |
596 <instance> |
597 the identifier of the application to restore |
597 the identifier of the instance to restore |
598 """ |
598 """ |
599 name = 'db-restore' |
599 name = 'db-restore' |
600 arguments = '<application> <backupfile>' |
600 arguments = '<instance> <backupfile>' |
601 |
601 |
602 options = ( |
602 options = ( |
603 ("no-drop", |
603 ("no-drop", |
604 {'short': 'n', 'action' : 'store_true', |
604 {'short': 'n', 'action' : 'store_true', |
605 'default' : False, |
605 'default' : False, |
607 'should not be dropped.'} |
607 'should not be dropped.'} |
608 ), |
608 ), |
609 ) |
609 ) |
610 |
610 |
611 def run(self, args): |
611 def run(self, args): |
612 appid = pop_arg(args, 1, msg="No application specified !") |
612 appid = pop_arg(args, 1, msg="No instance specified !") |
613 backupfile = pop_arg(args, msg="No backup file specified !") |
613 backupfile = pop_arg(args, msg="No backup file specified !") |
614 _local_restore(appid, backupfile, not self.config.no_drop) |
614 _local_restore(appid, backupfile, not self.config.no_drop) |
615 |
615 |
616 |
616 |
617 class DBCopyCommand(Command): |
617 class DBCopyCommand(Command): |
618 """Copy the system database of an application (backup and restore). |
618 """Copy the system database of an instance (backup and restore). |
619 |
619 |
620 <src-application> |
620 <src-instance> |
621 the identifier of the application to backup |
621 the identifier of the instance to backup |
622 format [[user@]host:]appname |
622 format [[user@]host:]appname |
623 |
623 |
624 <dest-application> |
624 <dest-instance> |
625 the identifier of the application to restore |
625 the identifier of the instance to restore |
626 """ |
626 """ |
627 name = 'db-copy' |
627 name = 'db-copy' |
628 arguments = '<src-application> <dest-application>' |
628 arguments = '<src-instance> <dest-instance>' |
629 |
629 |
630 options = ( |
630 options = ( |
631 ("no-drop", |
631 ("no-drop", |
632 {'short': 'n', 'action' : 'store_true', |
632 {'short': 'n', 'action' : 'store_true', |
633 'default' : False, |
633 'default' : False, |
646 ), |
646 ), |
647 ) |
647 ) |
648 |
648 |
649 def run(self, args): |
649 def run(self, args): |
650 import tempfile |
650 import tempfile |
651 srcappid = pop_arg(args, 1, msg="No source application specified !") |
651 srcappid = pop_arg(args, 1, msg="No source instance specified !") |
652 destappid = pop_arg(args, msg="No destination application specified !") |
652 destappid = pop_arg(args, msg="No destination instance specified !") |
653 _, output = tempfile.mkstemp() |
653 _, output = tempfile.mkstemp() |
654 if ':' in srcappid: |
654 if ':' in srcappid: |
655 host, srcappid = srcappid.split(':') |
655 host, srcappid = srcappid.split(':') |
656 _remote_dump(host, srcappid, output, self.config.sudo) |
656 _remote_dump(host, srcappid, output, self.config.sudo) |
657 else: |
657 else: |
662 else: |
662 else: |
663 os.remove(output) |
663 os.remove(output) |
664 |
664 |
665 |
665 |
666 class CheckRepositoryCommand(Command): |
666 class CheckRepositoryCommand(Command): |
667 """Check integrity of the system database of an application. |
667 """Check integrity of the system database of an instance. |
668 |
668 |
669 <application> |
669 <instance> |
670 the identifier of the application to check |
670 the identifier of the instance to check |
671 """ |
671 """ |
672 name = 'db-check' |
672 name = 'db-check' |
673 arguments = '<application>' |
673 arguments = '<instance>' |
674 |
674 |
675 options = ( |
675 options = ( |
676 ("checks", |
676 ("checks", |
677 {'short': 'c', 'type' : "csv", 'metavar' : '<check list>', |
677 {'short': 'c', 'type' : "csv", 'metavar' : '<check list>', |
678 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
678 'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'), |
700 |
700 |
701 ) |
701 ) |
702 |
702 |
703 def run(self, args): |
703 def run(self, args): |
704 from cubicweb.server.checkintegrity import check |
704 from cubicweb.server.checkintegrity import check |
705 appid = pop_arg(args, 1, msg="No application specified !") |
705 appid = pop_arg(args, 1, msg="No instance specified !") |
706 config = ServerConfiguration.config_for(appid) |
706 config = ServerConfiguration.config_for(appid) |
707 config.repairing = self.config.force |
707 config.repairing = self.config.force |
708 repo, cnx = repo_cnx(config) |
708 repo, cnx = repo_cnx(config) |
709 check(repo, cnx, |
709 check(repo, cnx, |
710 self.config.checks, self.config.reindex, self.config.autofix) |
710 self.config.checks, self.config.reindex, self.config.autofix) |
711 |
711 |
712 |
712 |
713 class RebuildFTICommand(Command): |
713 class RebuildFTICommand(Command): |
714 """Rebuild the full-text index of the system database of an application. |
714 """Rebuild the full-text index of the system database of an instance. |
715 |
715 |
716 <application> |
716 <instance> |
717 the identifier of the application to rebuild |
717 the identifier of the instance to rebuild |
718 """ |
718 """ |
719 name = 'db-rebuild-fti' |
719 name = 'db-rebuild-fti' |
720 arguments = '<application>' |
720 arguments = '<instance>' |
721 |
721 |
722 options = () |
722 options = () |
723 |
723 |
724 def run(self, args): |
724 def run(self, args): |
725 from cubicweb.server.checkintegrity import reindex_entities |
725 from cubicweb.server.checkintegrity import reindex_entities |
726 appid = pop_arg(args, 1, msg="No application specified !") |
726 appid = pop_arg(args, 1, msg="No instance specified !") |
727 config = ServerConfiguration.config_for(appid) |
727 config = ServerConfiguration.config_for(appid) |
728 repo, cnx = repo_cnx(config) |
728 repo, cnx = repo_cnx(config) |
729 session = repo._get_session(cnx.sessionid, setpool=True) |
729 session = repo._get_session(cnx.sessionid, setpool=True) |
730 reindex_entities(repo.schema, session) |
730 reindex_entities(repo.schema, session) |
731 cnx.commit() |
731 cnx.commit() |
732 |
732 |
733 |
733 |
734 class SynchronizeApplicationSchemaCommand(Command): |
734 class SynchronizeInstanceSchemaCommand(Command): |
735 """Synchronize persistent schema with cube schema. |
735 """Synchronize persistent schema with cube schema. |
736 |
736 |
737 Will synchronize common stuff between the cube schema and the |
737 Will synchronize common stuff between the cube schema and the |
738 actual persistent schema, but will not add/remove any entity or relation. |
738 actual persistent schema, but will not add/remove any entity or relation. |
739 |
739 |
740 <application> |
740 <instance> |
741 the identifier of the application to synchronize. |
741 the identifier of the instance to synchronize. |
742 """ |
742 """ |
743 name = 'schema-sync' |
743 name = 'schema-sync' |
744 arguments = '<application>' |
744 arguments = '<instance>' |
745 |
745 |
746 def run(self, args): |
746 def run(self, args): |
747 appid = pop_arg(args, msg="No application specified !") |
747 appid = pop_arg(args, msg="No instance specified !") |
748 config = ServerConfiguration.config_for(appid) |
748 config = ServerConfiguration.config_for(appid) |
749 mih = config.migration_handler() |
749 mih = config.migration_handler() |
750 mih.cmd_synchronize_schema() |
750 mih.cmd_synchronize_schema() |
751 |
751 |
752 |
752 |
753 register_commands( (CreateApplicationDBCommand, |
753 register_commands( (CreateInstanceDBCommand, |
754 InitApplicationCommand, |
754 InitInstanceCommand, |
755 GrantUserOnApplicationCommand, |
755 GrantUserOnInstanceCommand, |
756 ResetAdminPasswordCommand, |
756 ResetAdminPasswordCommand, |
757 StartRepositoryCommand, |
757 StartRepositoryCommand, |
758 DBDumpCommand, |
758 DBDumpCommand, |
759 DBRestoreCommand, |
759 DBRestoreCommand, |
760 DBCopyCommand, |
760 DBCopyCommand, |
761 CheckRepositoryCommand, |
761 CheckRepositoryCommand, |
762 RebuildFTICommand, |
762 RebuildFTICommand, |
763 SynchronizeApplicationSchemaCommand, |
763 SynchronizeInstanceSchemaCommand, |
764 ) ) |
764 ) ) |