#481017: cubicweb-ctl shell on remote instance stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 16 Oct 2009 14:37:11 +0200
branchstable
changeset 3700 fd550e4dc515
parent 3699 20ba545e00e1
child 3701 104b7c326172
#481017: cubicweb-ctl shell on remote instance
common/migration.py
cwctl.py
server/migractions.py
--- a/common/migration.py	Fri Oct 16 14:15:44 2009 +0200
+++ b/common/migration.py	Fri Oct 16 14:37:11 2009 +0200
@@ -92,7 +92,9 @@
 
     def __init__(self, config, interactive=True, verbosity=1):
         self.config = config
-        self.config.init_log(logthreshold=logging.ERROR, debug=True)
+        if config:
+            # no config on shell to a remote instance
+            self.config.init_log(logthreshold=logging.ERROR, debug=True)
         # 0: no confirmation, 1: only main commands confirmed, 2 ask for everything
         self.verbosity = verbosity
         self.need_wrap = True
--- a/cwctl.py	Fri Oct 16 14:15:44 2009 +0200
+++ b/cwctl.py	Fri Oct 16 14:37:11 2009 +0200
@@ -685,10 +685,16 @@
 
 
 class ShellCommand(Command):
-    """Run an interactive migration shell. This is a python shell with
-    enhanced migration commands predefined in the namespace. An additional
-    argument may be given corresponding to a file containing commands to
-    execute in batch mode.
+    """Run an interactive migration shell on an instance. This is a python shell
+    with enhanced migration commands predefined in the namespace. An additional
+    argument may be given corresponding to a file containing commands to execute
+    in batch mode.
+
+    By default it will connect to a local instance using an in memory
+    connection, unless -P option is specified, in which case you will be
+    connected through pyro. In the later case, you won't have access to
+    repository internals (session, etc...) so most migration commands won't be
+    available.
 
     <instance>
       the identifier of the instance to connect.
@@ -698,46 +704,72 @@
     options = (
         ('system-only',
          {'short': 'S', 'action' : 'store_true',
-          'default': False,
           'help': 'only connect to the system source when the instance is '
           'using multiple sources. You can\'t use this option and the '
-          '--ext-sources option at the same time.'}),
+          '--ext-sources option at the same time.',
+          'group': 'local'
+         }),
 
         ('ext-sources',
          {'short': 'E', 'type' : 'csv', 'metavar': '<sources>',
-          'default': None,
           'help': "For multisources instances, specify to which sources the \
 repository should connect to for upgrading. When unspecified or 'all' given, \
 will connect to all defined sources. If 'migration' is given, appropriate \
 sources for migration will be automatically selected.",
+          'group': 'local'
           }),
 
         ('force',
          {'short': 'f', 'action' : 'store_true',
-          'default' : False,
-          'help': 'don\'t check instance is up to date.'}
-         ),
+          'help': 'don\'t check instance is up to date.',
+          'group': 'local'
+          }),
+
+        ('pyro',
+         {'short': 'P', 'action' : 'store_true',
+          'help': 'connect to a running instance through Pyro.',
+          'group': 'remote',
+          }),
+        ('pyro-ns-host',
+         {'short': 'H', 'type' : 'string', 'metavar': '<host[:port]>',
+          'help': 'Pyro name server host. If not set, will be detected by '
+          'using a broadcast query.',
+          'group': 'remote'
+          }),
         )
 
     def run(self, args):
         appid = pop_arg(args, 99, msg="No instance specified !")
-        config = cwcfg.config_for(appid)
-        if self.config.ext_sources:
-            assert not self.config.system_only
-            sources = self.config.ext_sources
-        elif self.config.system_only:
-            sources = ('system',)
+        if self.config.pyro:
+            from cubicweb.dbapi import connect
+            from cubicweb.server.utils import manager_userpasswd
+            from cubicweb.server.migractions import ServerMigrationHelper
+            login, pwd = manager_userpasswd(msg=None)
+            cnx = connect(appid, login=login, password=pwd,
+                          host=self.config.pyro_ns_host, mulcnx=False)
+            repo = cnx._repo
+            mih = ServerMigrationHelper(None, repo=repo, cnx=cnx,
+                                         # hack so it don't try to load fs schema
+                                        schema=1)
         else:
-            sources = ('all',)
-        config.set_sources_mode(sources)
-        config.repairing = self.config.force
-        mih = config.migration_handler()
+            config = cwcfg.config_for(appid)
+            if self.config.ext_sources:
+                assert not self.config.system_only
+                sources = self.config.ext_sources
+            elif self.config.system_only:
+                sources = ('system',)
+            else:
+                sources = ('all',)
+            config.set_sources_mode(sources)
+            config.repairing = self.config.force
+            mih = config.migration_handler()
         if args:
             for arg in args:
                 mih.process_script(arg)
         else:
             mih.interactive_shell()
-        mih.shutdown()
+        if not self.config.pyro:
+            mih.shutdown()
 
 
 class RecompileInstanceCatalogsCommand(InstanceCommand):
--- a/server/migractions.py	Fri Oct 16 14:15:44 2009 +0200
+++ b/server/migractions.py	Fri Oct 16 14:37:11 2009 +0200
@@ -54,6 +54,7 @@
     def __init__(self, config, schema, interactive=True,
                  repo=None, cnx=None, verbosity=1, connect=True):
         MigrationHelper.__init__(self, config, interactive, verbosity)
+        # no config on shell to a remote instance
         if not interactive:
             assert cnx
             assert repo
@@ -61,7 +62,8 @@
             assert repo
             self._cnx = cnx
             self.repo = repo
-            self.session.data['rebuild-infered'] = False
+            if config is not None:
+                self.session.data['rebuild-infered'] = False
         elif connect:
             self.repo_connect()
         if not schema:
@@ -233,7 +235,10 @@
 
     @property
     def session(self):
-        return self.repo._get_session(self.cnx.sessionid)
+        if self.config is not None:
+            return self.repo._get_session(self.cnx.sessionid)
+        # no access to session on remote instance
+        return None
 
     def commit(self):
         if hasattr(self, '_cnx'):
@@ -268,8 +273,7 @@
     @cached
     def group_mapping(self):
         """cached group mapping"""
-        self.session.set_pool()
-        return ss.group_mapping(self.session)
+        return ss.group_mapping(self._cw)
 
     def exec_event_script(self, event, cubepath=None, funcname=None,
                           *args, **kwargs):
@@ -981,7 +985,6 @@
 
          :rtype: `Workflow`
         """
-        self.session.set_pool() # ensure pool is set
         wf = self.cmd_create_entity('Workflow', name=unicode(name),
                                     **kwargs)
         if not isinstance(wfof, (list, tuple)):
@@ -1001,7 +1004,6 @@
 
     # XXX remove once cmd_add_[state|transition] are removed
     def _get_or_create_wf(self, etypes):
-        self.session.set_pool() # ensure pool is set
         if not isinstance(etypes, (list, tuple)):
             etypes = (etypes,)
         rset = self.rqlexec('Workflow X WHERE X workflow_of ET, ET name %(et)s',
@@ -1041,16 +1043,14 @@
         """set or add (if `reset` is False) groups and conditions for a
         transition
         """
-        self.session.set_pool() # ensure pool is set
-        tr = self.session.entity_from_eid(treid)
+        tr = self._cw.entity_from_eid(treid)
         tr.set_transition_permissions(requiredgroups, conditions, reset)
         if commit:
             self.commit()
 
     @deprecated('[3.5] use entity.fire_transition("transition") or entity.change_state("state")')
     def cmd_set_state(self, eid, statename, commit=False):
-        self.session.set_pool() # ensure pool is set
-        self.session.entity_from_eid(eid).change_state(statename)
+        self._cw.entity_from_eid(eid).change_state(statename)
         if commit:
             self.commit()
 
@@ -1074,11 +1074,18 @@
 
     # other data migration commands ###########################################
 
+    @property
+    def _cw(self):
+        session = self.session
+        if session is not None:
+            session.set_pool()
+            return session
+        return self.cnx.request()
+
     def cmd_create_entity(self, etype, *args, **kwargs):
         """add a new entity of the given type"""
         commit = kwargs.pop('commit', False)
-        self.session.set_pool()
-        entity = self.session.create_entity(etype, *args, **kwargs)
+        entity = self._cw.create_entity(etype, *args, **kwargs)
         if commit:
             self.commit()
         return entity
@@ -1114,7 +1121,6 @@
         if not isinstance(rql, (tuple, list)):
             rql = ( (rql, kwargs), )
         res = None
-        self.session.set_pool()
         for rql, kwargs in rql:
             if kwargs:
                 msg = '%s (%s)' % (rql, kwargs)
@@ -1122,7 +1128,7 @@
                 msg = rql
             if not ask_confirm or self.confirm('execute rql: %s ?' % msg):
                 try:
-                    res = self.session.execute(rql, kwargs, cachekey)
+                    res = self._cw.execute(rql, kwargs, cachekey)
                 except Exception, ex:
                     if self.confirm('error: %s\nabort?' % ex):
                         raise
@@ -1211,9 +1217,8 @@
         if self.ask_confirm:
             if not self._h.confirm('execute rql: %s ?' % msg):
                 raise StopIteration
-        self._h.session.set_pool()
         try:
-            rset = self._h.session.execute(rql, kwargs)
+            rset = self._h._cw.execute(rql, kwargs)
         except Exception, ex:
             if self._h.confirm('error: %s\nabort?' % ex):
                 raise