# HG changeset patch # User Vincent Michel # Date 1334070544 -7200 # Node ID 0e3b41118631d9788c4725595b80b5cd19572a81 # Parent 02f4f01375e894a8f9f7df0cb44a9fea4fca091b [dbapi] add possibility to connect to a remote ZMQRepository (closes #2290126) diff -r 02f4f01375e8 -r 0e3b41118631 cwctl.py --- a/cwctl.py Tue Apr 10 17:07:03 2012 +0200 +++ b/cwctl.py Tue Apr 10 17:09:04 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -38,6 +38,8 @@ from os.path import exists, join, isfile, isdir, dirname, abspath +from urlparse import urlparse + from logilab.common.clcommands import CommandLine from logilab.common.shellutils import ASK @@ -867,31 +869,45 @@ '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': '', - 'help': 'Pyro name server host. If not set, will be detected by ' - 'using a broadcast query.', + ('repo-uri', + {'short': 'H', 'type' : 'string', 'metavar': '://<[host][:port]>', + 'help': 'URI of the CubicWeb repository to connect to. URI can be \ +pyro://[host:port] the Pyro name server host; if the pyro nameserver is not set, \ +it will be detected by using a broadcast query, a ZMQ URL or \ +inmemory:// (default) use an in-memory repository.', 'group': 'remote' }), ) def run(self, args): appid = args.pop(0) - if self.config.pyro: + if self.config.repo_uri: + uri = urlparse(self.config.repo_uri) + if uri.scheme == 'pyro': + cnxtype = uri.scheme + hostport = uri.netloc + elif uri.scheme == 'inmemory': + cnxtype = '' + hostport = '' + else: + cnxtype = 'zmq' + hostport = self.config.repo_uri + else: + cnxtype = '' + + if cnxtype: from cubicweb import AuthenticationError - from cubicweb.dbapi import connect + from cubicweb.dbapi import connect, ConnectionProperties from cubicweb.server.utils import manager_userpasswd from cubicweb.server.migractions import ServerMigrationHelper + cnxprops = ConnectionProperties(cnxtype=cnxtype) + while True: try: login, pwd = manager_userpasswd(msg=None) cnx = connect(appid, login=login, password=pwd, - host=self.config.pyro_ns_host, mulcnx=False) + host=hostport, mulcnx=False, + cnxprops=cnxprops) except AuthenticationError, ex: print ex except (KeyboardInterrupt, EOFError): @@ -927,7 +943,7 @@ else: mih.interactive_shell() finally: - if not self.config.pyro: + if not cnxtype: # shutdown in-memory repo mih.shutdown() else: cnx.close() diff -r 02f4f01375e8 -r 0e3b41118631 dbapi.py --- a/dbapi.py Tue Apr 10 17:07:03 2012 +0200 +++ b/dbapi.py Tue Apr 10 17:09:04 2012 +0200 @@ -93,7 +93,7 @@ Only 'in-memory' and 'pyro' are supported for now. Either vreg or config argument should be given """ - assert method in ('pyro', 'inmemory') + assert method in ('pyro', 'inmemory', 'zmq') assert vreg or config if vreg and not config: config = vreg.config @@ -102,7 +102,9 @@ from cubicweb.server.repository import Repository from cubicweb.server.utils import TasksManager return Repository(config, TasksManager(), vreg=vreg) - + elif method == 'zmq': + from cubicweb.zmqclient import ZMQRepositoryClient + return ZMQRepositoryClient(config, vreg=vreg) else: # method == 'pyro' # resolve the Pyro object from logilab.common.pyro_ext import ns_get_proxy, get_proxy @@ -147,8 +149,8 @@ the user login to use to authenticate. :host: - the pyro nameserver host. Will be detected using broadcast query if - unspecified. + - pyro: nameserver host. Will be detected using broadcast query if unspecified + - zmq: repository host socket address :group: the instance's pyro nameserver group. You don't have to specify it unless @@ -185,6 +187,8 @@ config.global_set_option('pyro-ns-host', host) if group: config.global_set_option('pyro-ns-group', group) + elif method == 'zmq': + config = cwconfig.CubicWebNoAppConfiguration() else: assert database config = cwconfig.instance_configuration(database) diff -r 02f4f01375e8 -r 0e3b41118631 misc/migration/3.15.0_common.py --- a/misc/migration/3.15.0_common.py Tue Apr 10 17:07:03 2012 +0200 +++ b/misc/migration/3.15.0_common.py Tue Apr 10 17:09:04 2012 +0200 @@ -1,2 +1,5 @@ undo_actions = config.cfgfile_parser.get('MAIN', 'undo-support', False) config.global_set_option('undo-enabled', bool(undo_actions)) +pyro_actions = config.cfgfile_parser.get('REMOTE', 'pyro', False) +if pyro_actions: + config.global_set_option('repo-uri', 'pyro://') diff -r 02f4f01375e8 -r 0e3b41118631 zmqclient.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/zmqclient.py Tue Apr 10 17:09:04 2012 +0200 @@ -0,0 +1,61 @@ +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of CubicWeb. +# +# CubicWeb is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# CubicWeb is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with CubicWeb. If not, see . +"""Source to query another RQL repository using pyro""" + +__docformat__ = "restructuredtext en" +_ = unicode + +from functools import partial +import zmq + + +# XXX hack to overpass old zmq limitation that force to have +# only one context per python process +try: + from cubicweb.server.cwzmq import ctx +except ImportError: + ctx = zmq.Context() + +class ZMQRepositoryClient(object): + """ + This class delegate the overall repository stuff to a remote source. + + So calling a method of this repository will results on calling the + corresponding method of the remote source repository. + + Any raised exception on the remote source is propagated locally. + + ZMQ is used as the transport layer and cPickle is used to serialize data. + """ + + def __init__(self, config, vreg=None): + self.config = config + self.vreg = vreg + self.socket = ctx.socket(zmq.REQ) + self.host = config.get('base-url') + self.socket.connect(self.host) + + def __zmqcall__(self, name, *args, **kwargs): + self.socket.send_pyobj([name, args, kwargs]) + result = self.socket.recv_pyobj() + if isinstance(result, BaseException): + raise result + return result + + def __getattr__(self, name): + return partial(self.__zmqcall__, name)