From 237c16f0fd3998f4a2e69d9096997d10af2cf8c9 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 24 Nov 2008 12:51:03 -0700 Subject: Started moving xmlrpc-functions from ipalib.util to ipalib.rpc --- ipalib/rpc.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 ipalib/rpc.py (limited to 'ipalib/rpc.py') diff --git a/ipalib/rpc.py b/ipalib/rpc.py new file mode 100644 index 00000000..c4662f84 --- /dev/null +++ b/ipalib/rpc.py @@ -0,0 +1,86 @@ +# Authors: +# Jason Gerard DeRose +# +# 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 + +""" +Core RPC functionality. +""" + +from types import NoneType +from xmlrpclib import Binary + + +def xmlrpc_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 request, 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 `xmlrpc_unwrap`. + """ + if type(value) in (list, tuple): + return tuple(xmlrpc_wrap(v) for v in value) + if type(value) is dict: + return dict( + (k, xmlrpc_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 xmlrpc_unwrap(value, encoding='UTF-8'): + """ + Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. + + When decoding data from an XML-RPC request, 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 character data. + They are decoded and the resulting ``unicode`` instance is returned. + + Also see `xmlrpc_wrap`. + """ + if type(value) in (list, tuple): + return tuple(xmlrpc_unwrap(v, encoding) for v in value) + if type(value) is dict: + return dict( + (k, xmlrpc_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 -- cgit From 6be5e4a0a55c1ba048444430afc0e01b3048d8b9 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 15 Jan 2009 23:52:50 -0700 Subject: ipalib.rpc: now using allow_none=True after conversation with Rob; added xml_dumps() and xml_loads() functions; some name cleanup --- ipalib/rpc.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'ipalib/rpc.py') diff --git a/ipalib/rpc.py b/ipalib/rpc.py index c4662f84..df31669d 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -22,10 +22,10 @@ Core RPC functionality. """ from types import NoneType -from xmlrpclib import Binary +from xmlrpclib import Binary, Fault, dumps, loads -def xmlrpc_wrap(value): +def xml_wrap(value): """ Wrap all ``str`` in ``xmlrpclib.Binary``. @@ -42,13 +42,13 @@ def xmlrpc_wrap(value): converted to UTF-8 encoded ``str`` instances (although as mentioned, not by this function). - Also see `xmlrpc_unwrap`. + Also see `xml_unwrap`. """ if type(value) in (list, tuple): - return tuple(xmlrpc_wrap(v) for v in value) + return tuple(xml_wrap(v) for v in value) if type(value) is dict: return dict( - (k, xmlrpc_wrap(v)) for (k, v) in value.iteritems() + (k, xml_wrap(v)) for (k, v) in value.iteritems() ) if type(value) is str: return Binary(value) @@ -56,7 +56,7 @@ def xmlrpc_wrap(value): return value -def xmlrpc_unwrap(value, encoding='UTF-8'): +def xml_unwrap(value, encoding='UTF-8'): """ Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. @@ -69,13 +69,13 @@ def xmlrpc_unwrap(value, encoding='UTF-8'): * All ``str`` instances are treated as UTF-8 encoded character data. They are decoded and the resulting ``unicode`` instance is returned. - Also see `xmlrpc_wrap`. + Also see `xml_wrap`. """ if type(value) in (list, tuple): - return tuple(xmlrpc_unwrap(v, encoding) for v in value) + return tuple(xml_unwrap(v, encoding) for v in value) if type(value) is dict: return dict( - (k, xmlrpc_unwrap(v, encoding)) for (k, v) in value.iteritems() + (k, xml_unwrap(v, encoding)) for (k, v) in value.iteritems() ) if type(value) is str: return value.decode(encoding) @@ -84,3 +84,21 @@ def xmlrpc_unwrap(value, encoding='UTF-8'): 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'): + 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): + (params, method) = loads(data) + return (xml_unwrap(params), method) -- cgit From 0227a129496acf4be48f0d5821fb9f602643feac Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 16 Jan 2009 01:23:55 -0700 Subject: Added docstrings to the new rpc.xml_dumps() and rcp.xml_loads() functions --- ipalib/rpc.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'ipalib/rpc.py') diff --git a/ipalib/rpc.py b/ipalib/rpc.py index df31669d..486aff8a 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -18,7 +18,13 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -Core RPC functionality. +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 """ from types import NoneType @@ -32,7 +38,7 @@ def xml_wrap(value): 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 request, the following + 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 @@ -42,7 +48,9 @@ def xml_wrap(value): converted to UTF-8 encoded ``str`` instances (although as mentioned, not by this function). - Also see `xml_unwrap`. + 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) @@ -60,16 +68,19 @@ def xml_unwrap(value, encoding='UTF-8'): """ Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. - When decoding data from an XML-RPC request, the following transformations + 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 character data. + * 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`. + 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) @@ -87,6 +98,24 @@ def xml_unwrap(value, encoding='UTF-8'): 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: @@ -100,5 +129,27 @@ def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'): def xml_loads(data): + """ + Decode the XML-RPC packet in ``data``, transparently unwrapped 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) -- cgit From 462bac3c13de92511085b080ee5b9999f275a1e3 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 16 Jan 2009 01:56:39 -0700 Subject: Added docstring cross-references between rpc and rpcserver modules --- ipalib/rpc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'ipalib/rpc.py') diff --git a/ipalib/rpc.py b/ipalib/rpc.py index 486aff8a..acfdae95 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -18,13 +18,15 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -Shared RPC client/server functionality. +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 @@ -130,7 +132,7 @@ def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'): def xml_loads(data): """ - Decode the XML-RPC packet in ``data``, transparently unwrapped its params. + 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`` -- cgit From 55fba5420d8ea57931937728102094492ca73d86 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 19 Jan 2009 21:10:42 -0700 Subject: Added rpc.xmlclient backend plugin for forwarding; added corresponding unit tests --- ipalib/rpc.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'ipalib/rpc.py') diff --git a/ipalib/rpc.py b/ipalib/rpc.py index acfdae95..e7823ef9 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -30,7 +30,11 @@ 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): @@ -155,3 +159,49 @@ def xml_loads(data): """ (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, + ) -- cgit