server/serverctl.py
changeset 10589 7c23b7de2b8d
parent 10569 af47954c1015
child 10612 84468b90e9c1
equal deleted inserted replaced
10588:fdaa0e4b7eaf 10589:7c23b7de2b8d
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """cubicweb-ctl commands and command handlers specific to the repository"""
    18 """cubicweb-ctl commands and command handlers specific to the repository"""
       
    19 from __future__ import print_function
    19 
    20 
    20 __docformat__ = 'restructuredtext en'
    21 __docformat__ = 'restructuredtext en'
    21 
    22 
    22 # *ctl module should limit the number of import to be imported as quickly as
    23 # *ctl module should limit the number of import to be imported as quickly as
    23 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
    24 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
    53     if dbname is None:
    54     if dbname is None:
    54         dbname = source['db-name']
    55         dbname = source['db-name']
    55     driver = source['db-driver']
    56     driver = source['db-driver']
    56     dbhelper = get_db_helper(driver)
    57     dbhelper = get_db_helper(driver)
    57     if interactive:
    58     if interactive:
    58         print '-> connecting to %s database' % driver,
    59         print('-> connecting to %s database' % driver, end=' ')
    59         if dbhost:
    60         if dbhost:
    60             print '%s@%s' % (dbname, dbhost),
    61             print('%s@%s' % (dbname, dbhost), end=' ')
    61         else:
    62         else:
    62             print dbname,
    63             print(dbname, end=' ')
    63     if dbhelper.users_support:
    64     if dbhelper.users_support:
    64         if not interactive or (not special_privs and source.get('db-user')):
    65         if not interactive or (not special_privs and source.get('db-user')):
    65             user = source.get('db-user', os.environ.get('USER', ''))
    66             user = source.get('db-user', os.environ.get('USER', ''))
    66             if interactive:
    67             if interactive:
    67                 print 'as', user
    68                 print('as', user)
    68             password = source.get('db-password')
    69             password = source.get('db-password')
    69         else:
    70         else:
    70             print
    71             print()
    71             if special_privs:
    72             if special_privs:
    72                 print 'WARNING'
    73                 print('WARNING')
    73                 print ('the user will need the following special access rights '
    74                 print ('the user will need the following special access rights '
    74                        'on the database:')
    75                        'on the database:')
    75                 print special_privs
    76                 print(special_privs)
    76                 print
    77                 print()
    77             default_user = source.get('db-user', os.environ.get('USER', ''))
    78             default_user = source.get('db-user', os.environ.get('USER', ''))
    78             user = raw_input('Connect as user ? [%r]: ' % default_user)
    79             user = raw_input('Connect as user ? [%r]: ' % default_user)
    79             user = user.strip() or default_user
    80             user = user.strip() or default_user
    80             if user == source.get('db-user'):
    81             if user == source.get('db-user'):
    81                 password = source.get('db-password')
    82                 password = source.get('db-password')
   144         try:
   145         try:
   145             repo = repoapi.get_repository(config=config)
   146             repo = repoapi.get_repository(config=config)
   146             cnx = repoapi.connect(repo, login, password=pwd)
   147             cnx = repoapi.connect(repo, login, password=pwd)
   147             return repo, cnx
   148             return repo, cnx
   148         except AuthenticationError:
   149         except AuthenticationError:
   149             print '-> Error: wrong user/password.'
   150             print('-> Error: wrong user/password.')
   150             # reset cubes else we'll have an assertion error on next retry
   151             # reset cubes else we'll have an assertion error on next retry
   151             config._cubes = None
   152             config._cubes = None
   152         login, pwd = manager_userpasswd()
   153         login, pwd = manager_userpasswd()
   153 
   154 
   154 
   155 
   162         """create an instance by copying files from the given cube and by asking
   163         """create an instance by copying files from the given cube and by asking
   163         information necessary to build required configuration files
   164         information necessary to build required configuration files
   164         """
   165         """
   165         config = self.config
   166         config = self.config
   166         if not automatic:
   167         if not automatic:
   167             print underline_title('Configuring the repository')
   168             print(underline_title('Configuring the repository'))
   168             config.input_config('email', inputlevel)
   169             config.input_config('email', inputlevel)
   169             print '\n'+underline_title('Configuring the sources')
   170             print('\n'+underline_title('Configuring the sources'))
   170         sourcesfile = config.sources_file()
   171         sourcesfile = config.sources_file()
   171         # hack to make Method('default_instance_id') usable in db option defs
   172         # hack to make Method('default_instance_id') usable in db option defs
   172         # (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         if not automatic:
   176         if not automatic:
   176             sconfig.input_config(inputlevel=inputlevel)
   177             sconfig.input_config(inputlevel=inputlevel)
   177             print
   178             print()
   178         sourcescfg = {'system': sconfig}
   179         sourcescfg = {'system': sconfig}
   179         if automatic:
   180         if automatic:
   180             # XXX modify a copy
   181             # XXX modify a copy
   181             password = generate_password()
   182             password = generate_password()
   182             print '-> set administrator account to admin / %s' % password
   183             print('-> set administrator account to admin / %s' % password)
   183             USER_OPTIONS[1][1]['default'] = password
   184             USER_OPTIONS[1][1]['default'] = password
   184             sconfig = Configuration(options=USER_OPTIONS)
   185             sconfig = Configuration(options=USER_OPTIONS)
   185         else:
   186         else:
   186             sconfig = Configuration(options=USER_OPTIONS)
   187             sconfig = Configuration(options=USER_OPTIONS)
   187             sconfig.input_config(inputlevel=inputlevel)
   188             sconfig.input_config(inputlevel=inputlevel)
   195             CWCTL.run(['db-create', '--automatic', self.config.appid])
   196             CWCTL.run(['db-create', '--automatic', self.config.appid])
   196         elif ASK.confirm('Run db-create to create the system database ?'):
   197         elif ASK.confirm('Run db-create to create the system database ?'):
   197             CWCTL.run(['db-create', '--config-level', str(inputlevel),
   198             CWCTL.run(['db-create', '--config-level', str(inputlevel),
   198                        self.config.appid])
   199                        self.config.appid])
   199         else:
   200         else:
   200             print ('-> nevermind, you can do it later with '
   201             print('-> nevermind, you can do it later with '
   201                    '"cubicweb-ctl db-create %s".' % self.config.appid)
   202                   '"cubicweb-ctl db-create %s".' % self.config.appid)
   202 
   203 
   203 
   204 
   204 @contextmanager
   205 @contextmanager
   205 def db_transaction(source, privilege):
   206 def db_transaction(source, privilege):
   206     """Open a transaction to the instance database"""
   207     """Open a transaction to the instance database"""
   240     def _drop_namespace(self, source):
   241     def _drop_namespace(self, source):
   241         db_namespace = source.get('db-namespace')
   242         db_namespace = source.get('db-namespace')
   242         with db_transaction(source, privilege='DROP SCHEMA') as cursor:
   243         with db_transaction(source, privilege='DROP SCHEMA') as cursor:
   243             helper = get_db_helper(source['db-driver'])
   244             helper = get_db_helper(source['db-driver'])
   244             helper.drop_schema(cursor, db_namespace)
   245             helper.drop_schema(cursor, db_namespace)
   245             print '-> database schema %s dropped' % db_namespace
   246             print('-> database schema %s dropped' % db_namespace)
   246 
   247 
   247     def _drop_database(self, source):
   248     def _drop_database(self, source):
   248         dbname = source['db-name']
   249         dbname = source['db-name']
   249         if source['db-driver'] == 'sqlite':
   250         if source['db-driver'] == 'sqlite':
   250             print 'deleting database file %(db-name)s' % source
   251             print('deleting database file %(db-name)s' % source)
   251             os.unlink(source['db-name'])
   252             os.unlink(source['db-name'])
   252             print '-> database %(db-name)s dropped.' % source
   253             print('-> database %(db-name)s dropped.' % source)
   253         else:
   254         else:
   254             helper = get_db_helper(source['db-driver'])
   255             helper = get_db_helper(source['db-driver'])
   255             with db_sys_transaction(source, privilege='DROP DATABASE') as cursor:
   256             with db_sys_transaction(source, privilege='DROP DATABASE') as cursor:
   256                 print 'dropping database %(db-name)s' % source
   257                 print('dropping database %(db-name)s' % source)
   257                 cursor.execute('DROP DATABASE "%(db-name)s"' % source)
   258                 cursor.execute('DROP DATABASE "%(db-name)s"' % source)
   258                 print '-> database %(db-name)s dropped.' % source
   259                 print('-> database %(db-name)s dropped.' % source)
   259 
   260 
   260     def _drop_user(self, source):
   261     def _drop_user(self, source):
   261         user = source['db-user'] or None
   262         user = source['db-user'] or None
   262         if user is not None:
   263         if user is not None:
   263             with db_sys_transaction(source, privilege='DROP USER') as cursor:
   264             with db_sys_transaction(source, privilege='DROP USER') as cursor:
   264                 print 'dropping user %s' % user
   265                 print('dropping user %s' % user)
   265                 cursor.execute('DROP USER %s' % user)
   266                 cursor.execute('DROP USER %s' % user)
   266 
   267 
   267     def _cleanup_steps(self, source):
   268     def _cleanup_steps(self, source):
   268         # 1/ delete namespace if used
   269         # 1/ delete namespace if used
   269         db_namespace = source.get('db-namespace')
   270         db_namespace = source.get('db-namespace')
   286         for msg, step, default in self._cleanup_steps(source):
   287         for msg, step, default in self._cleanup_steps(source):
   287             if ASK.confirm(msg, default_is_yes=default):
   288             if ASK.confirm(msg, default_is_yes=default):
   288                 try:
   289                 try:
   289                     step(source)
   290                     step(source)
   290                 except Exception as exc:
   291                 except Exception as exc:
   291                     print 'ERROR', exc
   292                     print('ERROR', exc)
   292                     if ASK.confirm('An error occurred. Continue anyway?',
   293                     if ASK.confirm('An error occurred. Continue anyway?',
   293                                    default_is_yes=False):
   294                                    default_is_yes=False):
   294                         continue
   295                         continue
   295                     raise ExecutionError(str(exc))
   296                     raise ExecutionError(str(exc))
   296 
   297 
   355             if os.path.exists(dbname) and (
   356             if os.path.exists(dbname) and (
   356                 automatic or
   357                 automatic or
   357                 ASK.confirm('Database %s already exists. Drop it?' % dbname)):
   358                 ASK.confirm('Database %s already exists. Drop it?' % dbname)):
   358                 os.unlink(dbname)
   359                 os.unlink(dbname)
   359         elif self.config.create_db:
   360         elif self.config.create_db:
   360             print '\n'+underline_title('Creating the system database')
   361             print('\n'+underline_title('Creating the system database'))
   361             # connect on the dbms system base to create our base
   362             # connect on the dbms system base to create our base
   362             dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER',
   363             dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER',
   363                                 interactive=not automatic)
   364                                 interactive=not automatic)
   364             cursor = dbcnx.cursor()
   365             cursor = dbcnx.cursor()
   365             try:
   366             try:
   366                 if helper.users_support:
   367                 if helper.users_support:
   367                     user = source['db-user']
   368                     user = source['db-user']
   368                     if not helper.user_exists(cursor, user) and (automatic or \
   369                     if not helper.user_exists(cursor, user) and (automatic or \
   369                            ASK.confirm('Create db user %s ?' % user, default_is_yes=False)):
   370                            ASK.confirm('Create db user %s ?' % user, default_is_yes=False)):
   370                         helper.create_user(source['db-user'], source.get('db-password'))
   371                         helper.create_user(source['db-user'], source.get('db-password'))
   371                         print '-> user %s created.' % user
   372                         print('-> user %s created.' % user)
   372                 if dbname in helper.list_databases(cursor):
   373                 if dbname in helper.list_databases(cursor):
   373                     if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname):
   374                     if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname):
   374                         cursor.execute('DROP DATABASE "%s"' % dbname)
   375                         cursor.execute('DROP DATABASE "%s"' % dbname)
   375                     else:
   376                     else:
   376                         print ('you may want to run "cubicweb-ctl db-init '
   377                         print('you may want to run "cubicweb-ctl db-init '
   377                                '--drop %s" manually to continue.' % config.appid)
   378                               '--drop %s" manually to continue.' % config.appid)
   378                         return
   379                         return
   379                 createdb(helper, source, dbcnx, cursor)
   380                 createdb(helper, source, dbcnx, cursor)
   380                 dbcnx.commit()
   381                 dbcnx.commit()
   381                 print '-> database %s created.' % dbname
   382                 print('-> database %s created.' % dbname)
   382             except BaseException:
   383             except BaseException:
   383                 dbcnx.rollback()
   384                 dbcnx.rollback()
   384                 raise
   385                 raise
   385         cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE/SCHEMA',
   386         cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE/SCHEMA',
   386                                 interactive=not automatic)
   387                                 interactive=not automatic)
   398             for extlang in langs:
   399             for extlang in langs:
   399                 if automatic or ASK.confirm('Create language %s ?' % extlang):
   400                 if automatic or ASK.confirm('Create language %s ?' % extlang):
   400                     try:
   401                     try:
   401                         helper.create_language(cursor, extlang)
   402                         helper.create_language(cursor, extlang)
   402                     except Exception as exc:
   403                     except Exception as exc:
   403                         print '-> ERROR:', exc
   404                         print('-> ERROR:', exc)
   404                         print '-> could not create language %s, some stored procedures might be unusable' % extlang
   405                         print('-> could not create language %s, some stored procedures might be unusable' % extlang)
   405                         cnx.rollback()
   406                         cnx.rollback()
   406                     else:
   407                     else:
   407                         cnx.commit()
   408                         cnx.commit()
   408         print '-> database for instance %s created and necessary extensions installed.' % appid
   409         print('-> database for instance %s created and necessary extensions installed.' % appid)
   409         print
   410         print()
   410         if automatic:
   411         if automatic:
   411             CWCTL.run(['db-init', '--automatic', '--config-level', '0',
   412             CWCTL.run(['db-init', '--automatic', '--config-level', '0',
   412                        config.appid])
   413                        config.appid])
   413         elif ASK.confirm('Run db-init to initialize the system database ?'):
   414         elif ASK.confirm('Run db-init to initialize the system database ?'):
   414             CWCTL.run(['db-init', '--config-level',
   415             CWCTL.run(['db-init', '--config-level',
   415                        str(self.config.config_level), config.appid])
   416                        str(self.config.config_level), config.appid])
   416         else:
   417         else:
   417             print ('-> nevermind, you can do it later with '
   418             print('-> nevermind, you can do it later with '
   418                    '"cubicweb-ctl db-init %s".' % config.appid)
   419                   '"cubicweb-ctl db-init %s".' % config.appid)
   419 
   420 
   420 
   421 
   421 class InitInstanceCommand(Command):
   422 class InitInstanceCommand(Command):
   422     """Initialize the system database of an instance (run after 'db-create').
   423     """Initialize the system database of an instance (run after 'db-create').
   423 
   424 
   450           }),
   451           }),
   451         )
   452         )
   452 
   453 
   453     def run(self, args):
   454     def run(self, args):
   454         check_options_consistency(self.config)
   455         check_options_consistency(self.config)
   455         print '\n'+underline_title('Initializing the system database')
   456         print('\n'+underline_title('Initializing the system database'))
   456         from cubicweb.server import init_repository
   457         from cubicweb.server import init_repository
   457         appid = args[0]
   458         appid = args[0]
   458         config = ServerConfiguration.config_for(appid)
   459         config = ServerConfiguration.config_for(appid)
   459         try:
   460         try:
   460             system = config.system_source_config
   461             system = config.system_source_config
   504                 cubes = repo.get_cubes()
   505                 cubes = repo.get_cubes()
   505                 while True:
   506                 while True:
   506                     type = raw_input('source type (%s): '
   507                     type = raw_input('source type (%s): '
   507                                         % ', '.join(sorted(SOURCE_TYPES)))
   508                                         % ', '.join(sorted(SOURCE_TYPES)))
   508                     if type not in SOURCE_TYPES:
   509                     if type not in SOURCE_TYPES:
   509                         print '-> unknown source type, use one of the available types.'
   510                         print('-> unknown source type, use one of the available types.')
   510                         continue
   511                         continue
   511                     sourcemodule = SOURCE_TYPES[type].module
   512                     sourcemodule = SOURCE_TYPES[type].module
   512                     if not sourcemodule.startswith('cubicweb.'):
   513                     if not sourcemodule.startswith('cubicweb.'):
   513                         # module names look like cubes.mycube.themodule
   514                         # module names look like cubes.mycube.themodule
   514                         sourcecube = SOURCE_TYPES[type].module.split('.', 2)[1]
   515                         sourcecube = SOURCE_TYPES[type].module.split('.', 2)[1]
   522                 while True:
   523                 while True:
   523                     parser = raw_input('parser type (%s): '
   524                     parser = raw_input('parser type (%s): '
   524                                         % ', '.join(sorted(repo.vreg['parsers'])))
   525                                         % ', '.join(sorted(repo.vreg['parsers'])))
   525                     if parser in repo.vreg['parsers']:
   526                     if parser in repo.vreg['parsers']:
   526                         break
   527                         break
   527                     print '-> unknown parser identifier, use one of the available types.'
   528                     print('-> unknown parser identifier, use one of the available types.')
   528                 while True:
   529                 while True:
   529                     sourceuri = raw_input('source identifier (a unique name used to '
   530                     sourceuri = raw_input('source identifier (a unique name used to '
   530                                           'tell sources apart): ').strip()
   531                                           'tell sources apart): ').strip()
   531                     if not sourceuri:
   532                     if not sourceuri:
   532                         print '-> mandatory.'
   533                         print('-> mandatory.')
   533                     else:
   534                     else:
   534                         sourceuri = unicode(sourceuri, sys.stdin.encoding)
   535                         sourceuri = unicode(sourceuri, sys.stdin.encoding)
   535                         if sourceuri in used:
   536                         if sourceuri in used:
   536                             print '-> uri already used, choose another one.'
   537                             print('-> uri already used, choose another one.')
   537                         else:
   538                         else:
   538                             break
   539                             break
   539                 url = raw_input('source URL (leave empty for none): ').strip()
   540                 url = raw_input('source URL (leave empty for none): ').strip()
   540                 url = unicode(url) if url else None
   541                 url = unicode(url) if url else None
   541                 # XXX configurable inputlevel
   542                 # XXX configurable inputlevel
   581                               set_owner=set_owner), cursor)
   582                               set_owner=set_owner), cursor)
   582         except Exception as ex:
   583         except Exception as ex:
   583             cnx.rollback()
   584             cnx.rollback()
   584             import traceback
   585             import traceback
   585             traceback.print_exc()
   586             traceback.print_exc()
   586             print '-> an error occurred:', ex
   587             print('-> an error occurred:', ex)
   587         else:
   588         else:
   588             cnx.commit()
   589             cnx.commit()
   589             print '-> rights granted to %s on instance %s.' % (appid, user)
   590             print('-> rights granted to %s on instance %s.' % (appid, user))
   590 
   591 
   591 
   592 
   592 class ResetAdminPasswordCommand(Command):
   593 class ResetAdminPasswordCommand(Command):
   593     """Reset the administrator password.
   594     """Reset the administrator password.
   594 
   595 
   615         config = ServerConfiguration.config_for(appid)
   616         config = ServerConfiguration.config_for(appid)
   616         sourcescfg = config.read_sources_file()
   617         sourcescfg = config.read_sources_file()
   617         try:
   618         try:
   618             adminlogin = sourcescfg['admin']['login']
   619             adminlogin = sourcescfg['admin']['login']
   619         except KeyError:
   620         except KeyError:
   620             print '-> Error: could not get cubicweb administrator login.'
   621             print('-> Error: could not get cubicweb administrator login.')
   621             sys.exit(1)
   622             sys.exit(1)
   622         cnx = source_cnx(sourcescfg['system'])
   623         cnx = source_cnx(sourcescfg['system'])
   623         driver = sourcescfg['system']['db-driver']
   624         driver = sourcescfg['system']['db-driver']
   624         dbhelper = get_db_helper(driver)
   625         dbhelper = get_db_helper(driver)
   625         cursor = cnx.cursor()
   626         cursor = cnx.cursor()
   626         # check admin exists
   627         # check admin exists
   627         cursor.execute("SELECT * FROM cw_CWUser WHERE cw_login=%(l)s",
   628         cursor.execute("SELECT * FROM cw_CWUser WHERE cw_login=%(l)s",
   628                        {'l': adminlogin})
   629                        {'l': adminlogin})
   629         if not cursor.fetchall():
   630         if not cursor.fetchall():
   630             print ("-> error: admin user %r specified in sources doesn't exist "
   631             print("-> error: admin user %r specified in sources doesn't exist "
   631                    "in the database" % adminlogin)
   632                   "in the database" % adminlogin)
   632             print "   fix your sources file before running this command"
   633             print("   fix your sources file before running this command")
   633             cnx.close()
   634             cnx.close()
   634             sys.exit(1)
   635             sys.exit(1)
   635         if self.config.password is None:
   636         if self.config.password is None:
   636             # ask for a new password
   637             # ask for a new password
   637             msg = 'new password for %s' % adminlogin
   638             msg = 'new password for %s' % adminlogin
   648             config.write_sources_file(sourcescfg)
   649             config.write_sources_file(sourcescfg)
   649         except Exception as ex:
   650         except Exception as ex:
   650             cnx.rollback()
   651             cnx.rollback()
   651             import traceback
   652             import traceback
   652             traceback.print_exc()
   653             traceback.print_exc()
   653             print '-> an error occurred:', ex
   654             print('-> an error occurred:', ex)
   654         else:
   655         else:
   655             cnx.commit()
   656             cnx.commit()
   656             print '-> password reset, sources file regenerated.'
   657             print('-> password reset, sources file regenerated.')
   657         cnx.close()
   658         cnx.close()
   658 
   659 
   659 
   660 
   660 
   661 
   661 def _remote_dump(host, appid, output, sudo=False):
   662 def _remote_dump(host, appid, output, sudo=False):
   664     filename = '%s-%s.tgz' % (appid, date.today().strftime('%Y-%m-%d'))
   665     filename = '%s-%s.tgz' % (appid, date.today().strftime('%Y-%m-%d'))
   665     dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s %s' % (filename, appid)
   666     dmpcmd = 'cubicweb-ctl db-dump -o /tmp/%s %s' % (filename, appid)
   666     if sudo:
   667     if sudo:
   667         dmpcmd = 'sudo %s' % (dmpcmd)
   668         dmpcmd = 'sudo %s' % (dmpcmd)
   668     dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd)
   669     dmpcmd = 'ssh -t %s "%s"' % (host, dmpcmd)
   669     print dmpcmd
   670     print(dmpcmd)
   670     if os.system(dmpcmd):
   671     if os.system(dmpcmd):
   671         raise ExecutionError('Error while dumping the database')
   672         raise ExecutionError('Error while dumping the database')
   672     if output is None:
   673     if output is None:
   673         output = filename
   674         output = filename
   674     cmd = 'scp %s:/tmp/%s %s' % (host, filename, output)
   675     cmd = 'scp %s:/tmp/%s %s' % (host, filename, output)
   675     print cmd
   676     print(cmd)
   676     if os.system(cmd):
   677     if os.system(cmd):
   677         raise ExecutionError('Error while retrieving the dump at /tmp/%s' % filename)
   678         raise ExecutionError('Error while retrieving the dump at /tmp/%s' % filename)
   678     rmcmd = 'ssh -t %s "rm -f /tmp/%s"' % (host, filename)
   679     rmcmd = 'ssh -t %s "rm -f /tmp/%s"' % (host, filename)
   679     print rmcmd
   680     print(rmcmd)
   680     if os.system(rmcmd) and not ASK.confirm(
   681     if os.system(rmcmd) and not ASK.confirm(
   681         'An error occurred while deleting remote dump at /tmp/%s. '
   682         'An error occurred while deleting remote dump at /tmp/%s. '
   682         'Continue anyway?' % filename):
   683         'Continue anyway?' % filename):
   683         raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename)
   684         raise ExecutionError('Error while deleting remote dump at /tmp/%s' % filename)
   684 
   685 
   699     repo = mih.repo
   700     repo = mih.repo
   700     # version of the database
   701     # version of the database
   701     dbversions = repo.get_versions()
   702     dbversions = repo.get_versions()
   702     mih.shutdown()
   703     mih.shutdown()
   703     if not dbversions:
   704     if not dbversions:
   704         print "bad or missing version information in the database, don't upgrade file system"
   705         print("bad or missing version information in the database, don't upgrade file system")
   705         return
   706         return
   706     # version of installed software
   707     # version of installed software
   707     eversion = dbversions['cubicweb']
   708     eversion = dbversions['cubicweb']
   708     status = instance_status(config, eversion, dbversions)
   709     status = instance_status(config, eversion, dbversions)
   709     # * database version > installed software
   710     # * database version > installed software
   710     if status == 'needsoftupgrade':
   711     if status == 'needsoftupgrade':
   711         print "** The database of %s is more recent than the installed software!" % config.appid
   712         print("** The database of %s is more recent than the installed software!" % config.appid)
   712         print "** Upgrade your software, then migrate the database by running the command"
   713         print("** Upgrade your software, then migrate the database by running the command")
   713         print "** 'cubicweb-ctl upgrade %s'" % config.appid
   714         print("** 'cubicweb-ctl upgrade %s'" % config.appid)
   714         return
   715         return
   715     # * database version < installed software, an upgrade will be necessary
   716     # * database version < installed software, an upgrade will be necessary
   716     #   anyway, just rewrite vc.conf and warn user he has to upgrade
   717     #   anyway, just rewrite vc.conf and warn user he has to upgrade
   717     elif status == 'needapplupgrade':
   718     elif status == 'needapplupgrade':
   718         print "** The database of %s is older than the installed software." % config.appid
   719         print("** The database of %s is older than the installed software." % config.appid)
   719         print "** Migrate the database by running the command"
   720         print("** Migrate the database by running the command")
   720         print "** 'cubicweb-ctl upgrade %s'" % config.appid
   721         print("** 'cubicweb-ctl upgrade %s'" % config.appid)
   721         return
   722         return
   722     # * database version = installed software, database version = instance fs version
   723     # * database version = installed software, database version = instance fs version
   723     #   ok!
   724     #   ok!
   724 
   725 
   725 def instance_status(config, cubicwebapplversion, vcconf):
   726 def instance_status(config, cubicwebapplversion, vcconf):
   730         return 'needapplupgrade'
   731         return 'needapplupgrade'
   731     for cube in config.cubes():
   732     for cube in config.cubes():
   732         try:
   733         try:
   733             softversion = config.cube_version(cube)
   734             softversion = config.cube_version(cube)
   734         except ConfigurationError:
   735         except ConfigurationError:
   735             print '-> Error: no cube version information for %s, please check that the cube is installed.' % cube
   736             print('-> Error: no cube version information for %s, please check that the cube is installed.' % cube)
   736             continue
   737             continue
   737         try:
   738         try:
   738             applversion = vcconf[cube]
   739             applversion = vcconf[cube]
   739         except KeyError:
   740         except KeyError:
   740             print '-> Error: no cube version information for %s in version configuration.' % cube
   741             print('-> Error: no cube version information for %s in version configuration.' % cube)
   741             continue
   742             continue
   742         if softversion == applversion:
   743         if softversion == applversion:
   743             continue
   744             continue
   744         if softversion > applversion:
   745         if softversion > applversion:
   745             return 'needsoftupgrade'
   746             return 'needsoftupgrade'
   881         else:
   882         else:
   882             _local_dump(srcappid, output, format=self.config.format)
   883             _local_dump(srcappid, output, format=self.config.format)
   883         _local_restore(destappid, output, not self.config.no_drop,
   884         _local_restore(destappid, output, not self.config.no_drop,
   884                        self.config.format)
   885                        self.config.format)
   885         if self.config.keep_dump:
   886         if self.config.keep_dump:
   886             print '-> you can get the dump file at', output
   887             print('-> you can get the dump file at', output)
   887         else:
   888         else:
   888             os.remove(output)
   889             os.remove(output)
   889 
   890 
   890 
   891 
   891 class CheckRepositoryCommand(Command):
   892 class CheckRepositoryCommand(Command):
  1001                 stats = source.pull_data(cnx, force=True, raise_on_error=True)
  1002                 stats = source.pull_data(cnx, force=True, raise_on_error=True)
  1002         finally:
  1003         finally:
  1003             repo.shutdown()
  1004             repo.shutdown()
  1004         for key, val in stats.iteritems():
  1005         for key, val in stats.iteritems():
  1005             if val:
  1006             if val:
  1006                 print key, ':', val
  1007                 print(key, ':', val)
  1007 
  1008 
  1008 
  1009 
  1009 
  1010 
  1010 def permissionshandler(relation, perms):
  1011 def permissionshandler(relation, perms):
  1011     from yams.schema import RelationDefinitionSchema
  1012     from yams.schema import RelationDefinitionSchema