summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/base.py2
-rw-r--r--ipalib/cli.py9
-rw-r--r--ipalib/frontend.py20
-rw-r--r--ipalib/plugins/misc.py2
-rw-r--r--ipalib/rpc.py19
-rw-r--r--ipaserver/rpcserver.py12
-rwxr-xr-xlite-xmlrpc2.py54
-rw-r--r--tests/test_ipalib/test_frontend.py2
-rw-r--r--tests/test_ipalib/test_parameters.py2
-rw-r--r--tests/test_ipalib/test_rpc.py18
10 files changed, 108 insertions, 32 deletions
diff --git a/ipalib/base.py b/ipalib/base.py
index bff8f1951..e0951e41e 100644
--- a/ipalib/base.py
+++ b/ipalib/base.py
@@ -455,7 +455,7 @@ class NameSpace(ReadOnly):
:param key: The name or index of a member, or a slice object.
"""
- if type(key) is str:
+ if isinstance(key, basestring):
return self.__map[key]
if type(key) in (int, slice):
return self.__members[key]
diff --git a/ipalib/cli.py b/ipalib/cli.py
index 809c0e551..62b8b9304 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -35,6 +35,7 @@ import struct
import frontend
import backend
import errors
+import errors2
import plugable
import util
from constants import CLI_TAB
@@ -534,9 +535,9 @@ class CLI(object):
print ''
self.api.log.info('operation aborted')
sys.exit()
- except errors.IPAError, e:
- self.api.log.error(unicode(e))
- sys.exit(e.faultCode)
+ except errors2.PublicError, e:
+ self.api.log.error(e.strerror)
+ sys.exit(e.errno)
def run_real(self):
"""
@@ -620,6 +621,8 @@ class CLI(object):
(c.name.replace('_', '-'), c) for c in self.api.Command()
)
self.textui = self.api.Backend.textui
+ if self.api.env.in_server is False and 'xmlclient' in self.api.Backend:
+ self.api.Backend.xmlclient.connect()
def load_plugins(self):
"""
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 2277c7a09..6efccbb4d 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -96,14 +96,14 @@ class Command(plugable.Plugin):
If not in a server context, the call will be forwarded over
XML-RPC and the executed an the nearest IPA server.
"""
- self.debug(make_repr(self.name, *args, **options))
params = self.args_options_2_params(*args, **options)
params = self.normalize(**params)
params = self.convert(**params)
params.update(self.get_default(**params))
self.validate(**params)
(args, options) = self.params_2_args_options(**params)
- self.debug(make_repr(self.name, *args, **options))
+ # FIXME: don't log passords!
+ self.info(make_repr(self.name, *args, **options))
result = self.run(*args, **options)
self.debug('%s result: %r', self.name, result)
return result
@@ -200,6 +200,11 @@ class Command(plugable.Plugin):
(k, self.params[k].convert(v)) for (k, v) in kw.iteritems()
)
+ def __convert_iter(self, kw):
+ for param in self.params():
+ if kw.get(param.name, None) is None:
+ continue
+
def get_default(self, **kw):
"""
Return a dictionary of defaults for all missing required values.
@@ -245,7 +250,7 @@ class Command(plugable.Plugin):
elif param.required:
raise errors.RequirementError(param.name)
- def run(self, *args, **kw):
+ def run(self, *args, **options):
"""
Dispatch to `Command.execute` or `Command.forward`.
@@ -258,11 +263,8 @@ class Command(plugable.Plugin):
performs is executed remotely.
"""
if self.api.env.in_server:
- target = self.execute
- else:
- target = self.forward
- object.__setattr__(self, 'run', target)
- return target(*args, **kw)
+ return self.execute(*args, **options)
+ return self.forward(*args, **options)
def execute(self, *args, **kw):
"""
@@ -283,7 +285,7 @@ class Command(plugable.Plugin):
"""
Forward call over XML-RPC to this same command on server.
"""
- return self.Backend.xmlrpc.forward_call(self.name, *args, **kw)
+ return self.Backend.xmlclient.forward(self.name, *args, **kw)
def finalize(self):
"""
diff --git a/ipalib/plugins/misc.py b/ipalib/plugins/misc.py
index a2f0fa4e4..327e52b56 100644
--- a/ipalib/plugins/misc.py
+++ b/ipalib/plugins/misc.py
@@ -22,7 +22,7 @@ Misc frontend plugins.
"""
import re
-from ipalib import api, LocalOrRemote
+from ipalib import api, LocalOrRemote, Bytes
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index e845b8939..aa8002094 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -135,8 +135,13 @@ def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'):
allow_none=True,
)
+def decode_fault(e, encoding='UTF-8'):
+ assert isinstance(e, Fault)
+ if type(e.faultString) is str:
+ return Fault(e.faultCode, e.faultString.decode(encoding))
+ return e
-def xml_loads(data):
+def xml_loads(data, encoding='UTF-8'):
"""
Decode the XML-RPC packet in ``data``, transparently unwrapping its params.
@@ -159,8 +164,11 @@ def xml_loads(data):
:param data: The XML-RPC packet to decode.
"""
- (params, method) = loads(data)
- return (xml_unwrap(params), method)
+ try:
+ (params, method) = loads(data)
+ return (xml_unwrap(params), method)
+ except Fault, e:
+ raise decode_fault(e)
class KerbTransport(SafeTransport):
@@ -211,8 +219,8 @@ class xmlclient(Backend):
)
)
conn = ServerProxy(self.env.xmlrpc_uri,
- transport=KerbTransport(),
allow_none=True,
+ encoding='UTF-8',
)
setattr(context, self.connection_name, conn)
@@ -243,9 +251,10 @@ class xmlclient(Backend):
command = getattr(context.xmlconn, name)
params = args + (kw,)
try:
- response = command(xml_wrap(params))
+ response = command(*xml_wrap(params))
return xml_unwrap(response)
except Fault, e:
+ e = decode_fault(e)
self.debug('Caught fault %d from server %s: %s', e.faultCode,
self.env.xmlrpc_uri, e.faultString)
if e.faultCode in self.__errors:
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 225173675..f5fb3c623 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -27,6 +27,7 @@ from xmlrpclib import Fault
from ipalib import Backend
from ipalib.errors2 import PublicError, InternalError, CommandError
from ipalib.rpc import xml_dumps, xml_loads
+from ipalib.util import make_repr
def params_2_args_options(params):
@@ -47,21 +48,26 @@ class xmlserver(Backend):
self.debug('Received RPC call to %r', method)
if method not in self.Command:
raise CommandError(name=method)
+ self.info('params = %r', params)
(args, options) = params_2_args_options(params)
+ self.info('args = %r', args)
+ self.info('options = %r', options)
+ self.debug(make_repr(method, *args, **options))
result = self.Command[method](*args, **options)
return (result,) # Must wrap XML-RPC response in a tuple singleton
- def execute(self, data, ccache=None, client_version=None,
- client_ip=None, languages=None):
+ def execute(self, data):
"""
Execute the XML-RPC request in contained in ``data``.
"""
try:
(params, method) = xml_loads(data)
response = self.dispatch(method, params)
+ print 'okay'
except Exception, e:
if not isinstance(e, PublicError):
e = InternalError()
assert isinstance(e, PublicError)
+ self.debug('Returning %r exception', e.__class__.__name__)
response = Fault(e.errno, e.strerror)
- return dumps(response)
+ return xml_dumps(response, methodresponse=True)
diff --git a/lite-xmlrpc2.py b/lite-xmlrpc2.py
new file mode 100755
index 000000000..cb216456c
--- /dev/null
+++ b/lite-xmlrpc2.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+# 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
+
+"""
+In-tree XML-RPC server using SimpleXMLRPCServer.
+"""
+
+from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
+from ipalib import api
+
+api.bootstrap_with_global_options(context='server')
+api.finalize()
+
+
+class Instance(object):
+ def _listMethods(self):
+ return list(api.Command)
+
+
+class Server(SimpleXMLRPCServer):
+ def _marshaled_dispatch(self, data, dispatch_method=None):
+ return api.Backend.xmlserver.execute(data)
+
+
+address = ('', api.env.lite_xmlrpc_port)
+server = Server(address,
+ logRequests=False,
+ allow_none=True,
+ encoding='UTF-8',
+)
+server.register_introspection_functions()
+server.register_instance(Instance())
+try:
+ server.serve_forever()
+except KeyboardInterrupt:
+ api.log.info('Server stopped')
diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py
index 1e90dbb34..72fc889e6 100644
--- a/tests/test_ipalib/test_frontend.py
+++ b/tests/test_ipalib/test_frontend.py
@@ -378,7 +378,6 @@ class test_Command(ClassChecker):
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
assert ('execute', args, kw) == o.run(*args, **kw)
- assert o.run.im_func is my_cmd.execute.im_func
# Test in non-server context
(api, home) = create_test_api(in_server=False)
@@ -387,7 +386,6 @@ class test_Command(ClassChecker):
o.set_api(api)
assert o.run.im_func is self.cls.run.im_func
assert ('forward', args, kw) == o.run(*args, **kw)
- assert o.run.im_func is my_cmd.forward.im_func
class test_LocalOrRemote(ClassChecker):
diff --git a/tests/test_ipalib/test_parameters.py b/tests/test_ipalib/test_parameters.py
index e9e514eb1..b3ab996df 100644
--- a/tests/test_ipalib/test_parameters.py
+++ b/tests/test_ipalib/test_parameters.py
@@ -335,6 +335,7 @@ class test_Param(ClassChecker):
o = Subclass('my_param')
for value in NULLS:
assert o.convert(value) is None
+ assert o.convert(None) is None
for value in okay:
assert o.convert(value) is value
@@ -821,6 +822,7 @@ class test_Str(ClassChecker):
assert e.name == 'my_str'
assert e.index == 18
assert_equal(e.error, u'must be Unicode text')
+ assert o.convert(None) is None
def test_rule_minlength(self):
"""
diff --git a/tests/test_ipalib/test_rpc.py b/tests/test_ipalib/test_rpc.py
index 30175e3bf..351f483be 100644
--- a/tests/test_ipalib/test_rpc.py
+++ b/tests/test_ipalib/test_rpc.py
@@ -171,11 +171,13 @@ def test_xml_loads():
assert_equal(tup[0], params)
# Test un-serializing an RPC response containing a Fault:
- fault = Fault(69, unicode_str)
- data = dumps(fault, methodresponse=True, allow_none=True)
- e = raises(Fault, f, data)
- assert e.faultCode == 69
- assert_equal(e.faultString, unicode_str)
+ for error in (unicode_str, u'hello'):
+ fault = Fault(69, error)
+ data = dumps(fault, methodresponse=True, allow_none=True, encoding='UTF-8')
+ e = raises(Fault, f, data)
+ assert e.faultCode == 69
+ assert_equal(e.faultString, error)
+ assert type(e.faultString) is unicode
class test_xmlclient(PluginTester):
@@ -227,19 +229,19 @@ class test_xmlclient(PluginTester):
context.xmlconn = DummyClass(
(
'user_add',
- (rpc.xml_wrap(params),),
+ rpc.xml_wrap(params),
{},
rpc.xml_wrap(result),
),
(
'user_add',
- (rpc.xml_wrap(params),),
+ rpc.xml_wrap(params),
{},
Fault(3007, u"'four' is required"), # RequirementError
),
(
'user_add',
- (rpc.xml_wrap(params),),
+ rpc.xml_wrap(params),
{},
Fault(700, u'no such error'), # There is no error 700
),