summaryrefslogtreecommitdiff
path: root/pyro_ext.py
blob: 0f4d2790b51e44023c79735d6c036a92806a7e10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-common.
#
# logilab-common 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.
#
# logilab-common 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 logilab-common.  If not, see <http://www.gnu.org/licenses/>.
"""Python Remote Object utilities

Main functions available:

* `register_object` to expose arbitrary object through pyro using delegation
  approach and register it in the nameserver.
* `ns_unregister` unregister an object identifier from the nameserver.
* `ns_get_proxy` get a pyro proxy from a nameserver object identifier.
"""

__docformat__ = "restructuredtext en"

import logging
import tempfile

from Pyro import core, naming, errors, util, config

_LOGGER = logging.getLogger('pyro')
_MARKER = object()

config.PYRO_STORAGE = tempfile.gettempdir()

def ns_group_and_id(idstr, defaultnsgroup=_MARKER):
    try:
        nsgroup, nsid = idstr.rsplit('.', 1)
    except ValueError:
        if defaultnsgroup is _MARKER:
            nsgroup = config.PYRO_NS_DEFAULTGROUP
        else:
            nsgroup = defaultnsgroup
        nsid = idstr
    if nsgroup is not None and not nsgroup.startswith(':'):
        nsgroup = ':' + nsgroup
    return nsgroup, nsid

def host_and_port(hoststr):
    if not hoststr:
        return None, None
    try:
        hoststr, port = hoststr.split(':')
    except ValueError:
        port = None
    else:
        port = int(port)
    return hoststr, port

_DAEMONS = {}
_PYRO_OBJS = {}
def _get_daemon(daemonhost, start=True):
    if not daemonhost in _DAEMONS:
        if not start:
            raise Exception('no daemon for %s' % daemonhost)
        if not _DAEMONS:
            core.initServer(banner=0)
        host, port = host_and_port(daemonhost)
        daemon = core.Daemon(host=host, port=port)
        _DAEMONS[daemonhost] = daemon
    return _DAEMONS[daemonhost]


def locate_ns(nshost):
    """locate and return the pyro name server to the daemon"""
    core.initClient(banner=False)
    return naming.NameServerLocator().getNS(*host_and_port(nshost))


def register_object(object, nsid, defaultnsgroup=_MARKER,
                    daemonhost=None, nshost=None, use_pyrons=True):
    """expose the object as a pyro object and register it in the name-server

    if use_pyrons is False, then the object is exposed, but no
    attempt to register it to a pyro nameserver is made.

    return the pyro daemon object
    """
    nsgroup, nsid = ns_group_and_id(nsid, defaultnsgroup)
    daemon = _get_daemon(daemonhost)
    if use_pyrons:
        nsd = locate_ns(nshost)
        # make sure our namespace group exists
        try:
            nsd.createGroup(nsgroup)
        except errors.NamingError:
            pass
        daemon.useNameServer(nsd)
    # use Delegation approach
    impl = core.ObjBase()
    impl.delegateTo(object)
    qnsid = '%s.%s' % (nsgroup, nsid)
    uri = daemon.connect(impl, qnsid)
    _PYRO_OBJS[qnsid] = str(uri)
    _LOGGER.info('registered %s a pyro object using group %s and id %s',
                 object, nsgroup, nsid)
    return daemon

def get_object_uri(qnsid):
    return _PYRO_OBJS[qnsid]

def ns_unregister(nsid, defaultnsgroup=_MARKER, nshost=None):
    """unregister the object with the given nsid from the pyro name server"""
    nsgroup, nsid = ns_group_and_id(nsid, defaultnsgroup)
    try:
        nsd = locate_ns(nshost)
    except errors.PyroError, ex:
        # name server not responding
        _LOGGER.error('can\'t locate pyro name server: %s', ex)
    else:
        try:
            nsd.unregister('%s.%s' % (nsgroup, nsid))
            _LOGGER.info('%s unregistered from pyro name server', nsid)
        except errors.NamingError:
            _LOGGER.warning('%s not registered in pyro name server', nsid)


def ns_reregister(nsid, defaultnsgroup=_MARKER, nshost=None):
    """reregister a pyro object into the name server. You only have to specify
    the name-server id of the object (though you MUST have gone through
    `register_object` for the given object previously).

    This is especially useful for long running server while the name server may
    have been restarted, and its records lost.
    """
    nsgroup, nsid = ns_group_and_id(nsid, defaultnsgroup)
    qnsid = '%s.%s' % (nsgroup, nsid)
    nsd = locate_ns(nshost)
    try:
        nsd.unregister(qnsid)
    except errors.NamingError:
        # make sure our namespace group exists
        try:
            nsd.createGroup(nsgroup)
        except errors.NamingError:
            pass
    nsd.register(qnsid, _PYRO_OBJS[qnsid])

def ns_get_proxy(nsid, defaultnsgroup=_MARKER, nshost=None):
    """
    if nshost is None, the nameserver is found by a broadcast.
    """
    # resolve the Pyro object
    nsgroup, nsid = ns_group_and_id(nsid, defaultnsgroup)
    try:
        nsd = locate_ns(nshost)
        pyrouri = nsd.resolve('%s.%s' % (nsgroup, nsid))
    except errors.ProtocolError, ex:
        raise errors.PyroError(
            'Could not connect to the Pyro name server (host: %s)' % nshost)
    except errors.NamingError:
        raise errors.PyroError(
            'Could not get proxy for %s (not registered in Pyro), '
            'you may have to restart your server-side application' % nsid)
    return core.getProxyForURI(pyrouri)

def get_proxy(pyro_uri):
    """get a proxy for the passed pyro uri without using a nameserver
    """
    return core.getProxyForURI(pyro_uri)

def set_pyro_log_threshold(level):
    pyrologger = logging.getLogger('Pyro.%s' % str(id(util.Log)))
    # remove handlers so only the root handler is used
    pyrologger.handlers = []
    pyrologger.setLevel(level)