diff options
author | Rob Crittenden <rcritten@redhat.com> | 2009-01-22 10:42:41 -0500 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2009-01-22 10:42:41 -0500 |
commit | c2967a675a288e7d31374229fd974d0cb9966f2c (patch) | |
tree | 58be8ca6319f4660d9f18b97a37b9c0c56104d02 /ipalib/rpc.py | |
parent | 2b8b87b4d6c3b4389a0a7bf48c225035c53e7ad1 (diff) | |
parent | 5d82e3b35a8fb2d4c25f282cddad557a7650197c (diff) | |
download | freeipa.git-c2967a675a288e7d31374229fd974d0cb9966f2c.tar.gz freeipa.git-c2967a675a288e7d31374229fd974d0cb9966f2c.tar.xz freeipa.git-c2967a675a288e7d31374229fd974d0cb9966f2c.zip |
Merge branch 'master' of git://fedorapeople.org/~jderose/freeipa2
Diffstat (limited to 'ipalib/rpc.py')
-rw-r--r-- | ipalib/rpc.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/ipalib/rpc.py b/ipalib/rpc.py new file mode 100644 index 00000000..e7823ef9 --- /dev/null +++ b/ipalib/rpc.py @@ -0,0 +1,207 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +RPC client and shared RPC client/server functionality. + +This module adds some additional functionality on top of the ``xmlrpclib`` +module in the Python standard library. For documentation on the +``xmlrpclib`` module, see: + + http://docs.python.org/library/xmlrpclib.html + +Also see the `ipaserver.rpcserver` module. +""" + +from types import NoneType +import threading +from xmlrpclib import Binary, Fault, dumps, loads +from ipalib.backend import Backend +from ipalib.errors2 import public_errors, PublicError, UnknownError +from ipalib.request import context + + +def xml_wrap(value): + """ + Wrap all ``str`` in ``xmlrpclib.Binary``. + + Because ``xmlrpclib.dumps()`` will itself convert all ``unicode`` instances + into UTF-8 encoded ``str`` instances, we don't do it here. + + So in total, when encoding data for an XML-RPC packet, the following + transformations occur: + + * All ``str`` instances are treated as binary data and are wrapped in + an ``xmlrpclib.Binary()`` instance. + + * Only ``unicode`` instances are treated as character data. They get + converted to UTF-8 encoded ``str`` instances (although as mentioned, + not by this function). + + Also see `xml_unwrap()`. + + :param value: The simple scalar or simple compound value to wrap. + """ + if type(value) in (list, tuple): + return tuple(xml_wrap(v) for v in value) + if type(value) is dict: + return dict( + (k, xml_wrap(v)) for (k, v) in value.iteritems() + ) + if type(value) is str: + return Binary(value) + assert type(value) in (unicode, int, float, bool, NoneType) + return value + + +def xml_unwrap(value, encoding='UTF-8'): + """ + Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. + + When decoding data from an XML-RPC packet, the following transformations + occur: + + * The binary payloads of all ``xmlrpclib.Binary`` instances are + returned as ``str`` instances. + + * All ``str`` instances are treated as UTF-8 encoded Unicode strings. + They are decoded and the resulting ``unicode`` instance is returned. + + Also see `xml_wrap()`. + + :param value: The value to unwrap. + :param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``). + """ + if type(value) in (list, tuple): + return tuple(xml_unwrap(v, encoding) for v in value) + if type(value) is dict: + return dict( + (k, xml_unwrap(v, encoding)) for (k, v) in value.iteritems() + ) + if type(value) is str: + return value.decode(encoding) + if isinstance(value, Binary): + assert type(value.data) is str + return value.data + assert type(value) in (unicode, int, float, bool, NoneType) + return value + + +def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'): + """ + Encode an XML-RPC data packet, transparently wraping ``params``. + + This function will wrap ``params`` using `xml_wrap()` and will + then encode the XML-RPC data packet using ``xmlrpclib.dumps()`` (from the + Python standard library). + + For documentation on the ``xmlrpclib.dumps()`` function, see: + + http://docs.python.org/library/xmlrpclib.html#convenience-functions + + Also see `xml_loads()`. + + :param params: A ``tuple`` or an ``xmlrpclib.Fault`` instance. + :param methodname: The name of the method to call if this is a request. + :param methodresponse: Set this to ``True`` if this is a response. + :param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``). + """ + if type(params) is tuple: + params = xml_wrap(params) + else: + assert isinstance(params, Fault) + return dumps(params, + methodname=methodname, + methodresponse=methodresponse, + encoding=encoding, + allow_none=True, + ) + + +def xml_loads(data): + """ + Decode the XML-RPC packet in ``data``, transparently unwrapping its params. + + This function will decode the XML-RPC packet in ``data`` using + ``xmlrpclib.loads()`` (from the Python standard library). If ``data`` + contains a fault, ``xmlrpclib.loads()`` will itself raise an + ``xmlrpclib.Fault`` exception. + + Assuming an exception is not raised, this function will then unwrap the + params in ``data`` using `xml_unwrap()`. Finally, a + ``(params, methodname)`` tuple is returned containing the unwrapped params + and the name of the method being called. If the packet contains no method + name, ``methodname`` will be ``None``. + + For documentation on the ``xmlrpclib.loads()`` function, see: + + http://docs.python.org/library/xmlrpclib.html#convenience-functions + + Also see `xml_dumps()`. + + :param data: The XML-RPC packet to decode. + """ + (params, method) = loads(data) + return (xml_unwrap(params), method) + + +class xmlclient(Backend): + """ + Forwarding backend for XML-RPC client. + """ + + def __init__(self): + super(xmlclient, self).__init__() + self.__errors = dict((e.errno, e) for e in public_errors) + + def forward(self, name, *args, **kw): + """ + Forward call to command named ``name`` over XML-RPC. + + This method will encode and forward an XML-RPC request, and will then + decode and return the corresponding XML-RPC response. + + :param command: The name of the command being forwarded. + :param args: Positional arguments to pass to remote command. + :param kw: Keyword arguments to pass to remote command. + """ + if name not in self.Command: + raise ValueError( + '%s.forward(): %r not in api.Command' % (self.name, name) + ) + if not hasattr(context, 'xmlconn'): + raise StandardError( + '%s.forward(%r): need context.xmlconn in thread %r' % ( + self.name, name, threading.currentThread().getName() + ) + ) + command = getattr(context.xmlconn, name) + params = args + (kw,) + try: + response = command(xml_wrap(params)) + return xml_unwrap(response) + except Fault, e: + if e.faultCode in self.__errors: + error = self.__errors[e.faultCode] + raise error(message=e.faultString) + raise UnknownError( + code=e.faultCode, + error=e.faultString, + server=self.env.xmlrpc_uri, + ) |