summaryrefslogtreecommitdiffstats
path: root/lite-xmlrpc.py
diff options
context:
space:
mode:
Diffstat (limited to 'lite-xmlrpc.py')
-rwxr-xr-xlite-xmlrpc.py175
1 files changed, 175 insertions, 0 deletions
diff --git a/lite-xmlrpc.py b/lite-xmlrpc.py
new file mode 100755
index 00000000..ee03adae
--- /dev/null
+++ b/lite-xmlrpc.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+
+# Authors:
+# Rob Crittenden <rcritten@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.
+"""
+
+import sys
+import SimpleXMLRPCServer
+import logging
+import xmlrpclib
+import re
+import threading
+import commands
+from ipalib import api
+from ipalib import config
+from ipaserver import conn
+from ipaserver.servercore import context
+from ipalib.util import xmlrpc_unmarshal
+import traceback
+import krbV
+
+class StoppableXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
+ """Override of TIME_WAIT"""
+ allow_reuse_address = True
+
+ def serve_forever(self):
+ self.stop = False
+ while not self.stop:
+ self.handle_request()
+
+class LoggingSimpleXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
+ """Overides the default SimpleXMLRPCRequestHander to support logging.
+ Logs client IP and the XML request and response.
+ """
+
+ def parse(self, given):
+ """Convert the incoming arguments into the format IPA expects"""
+ args = []
+ kw = {}
+ for g in given:
+ kw[g] = unicode(given[g])
+ return (args, kw)
+
+ def _dispatch(self, method, params):
+ """
+ Dispatches the XML-RPC method.
+
+ Methods beginning with an '_' are considered private and will
+ not be called.
+ """
+ if method not in funcs:
+ logger.error('no such method %r', method)
+ raise Exception('method "%s" is not supported' % method)
+ func = funcs[method]
+ krbccache = krbV.default_context().default_ccache().name
+ context.conn = conn.IPAConn(
+ api.env.ldap_host,
+ api.env.ldap_port,
+ krbccache,
+ )
+ logger.info('calling %s', method)
+ (args, kw) = xmlrpc_unmarshal(*params)
+ return func(*args, **kw)
+
+ def _marshaled_dispatch(self, data, dispatch_method = None):
+ try:
+ params, method = xmlrpclib.loads(data)
+
+ # generate response
+ if dispatch_method is not None:
+ response = dispatch_method(method, params)
+ else:
+ response = self._dispatch(method, params)
+ # wrap response in a singleton tuple
+ response = (response,)
+ response = xmlrpclib.dumps(response, methodresponse=1)
+ except:
+ # report exception back to client. This is needed to report
+ # tracebacks found in server code.
+ e_class, e = sys.exc_info()[:2]
+ # FIXME, need to get this number from somewhere...
+ faultCode = getattr(e_class,'faultCode',1)
+ tb_str = ''.join(traceback.format_exception(*sys.exc_info()))
+ faultString = tb_str
+ response = xmlrpclib.dumps(xmlrpclib.Fault(faultCode, faultString))
+
+ return response
+
+ def do_POST(self):
+ clientIP, port = self.client_address
+ # Log client IP and Port
+ logger.info('Client IP: %s - Port: %s' % (clientIP, port))
+ try:
+ # get arguments
+ data = self.rfile.read(int(self.headers["content-length"]))
+
+ # unmarshal the XML data
+ params, method = xmlrpclib.loads(data)
+ logger.info('Call to %s(%s) from %s:%s', method,
+ ', '.join(repr(p) for p in params),
+ clientIP, port
+ )
+
+ # Log client request
+ logger.debug('Client request: \n%s\n' % data)
+
+ response = self._marshaled_dispatch(
+ data, getattr(self, '_dispatch', None))
+
+ # Log server response
+ logger.debug('Server response: \n%s\n' % response)
+ except Exception, e:
+ # This should only happen if the module is buggy
+ # internal error, report as HTTP server error
+ print e
+ self.send_response(500)
+ self.end_headers()
+ else:
+ # got a valid XML-RPC response
+ self.send_response(200)
+ self.send_header("Content-type", "text/xml")
+ self.send_header("Content-length", str(len(response)))
+ self.end_headers()
+ self.wfile.write(response)
+
+ # shut down the connection
+ self.wfile.flush()
+ self.connection.shutdown(1)
+
+
+if __name__ == '__main__':
+ api.bootstrap_with_global_options(context='server')
+ api.finalize()
+ logger = api.log
+
+ # Set up the server
+ XMLRPCServer = StoppableXMLRPCServer(
+ ('', api.env.lite_xmlrpc_port),
+ LoggingSimpleXMLRPCRequestHandler
+ )
+ XMLRPCServer.register_introspection_functions()
+
+ # Get and register all the methods
+
+ for cmd in api.Command:
+ logger.debug('registering %s', cmd)
+ XMLRPCServer.register_function(api.Command[cmd], cmd)
+ funcs = XMLRPCServer.funcs
+
+ logger.info('Logging to file %r', api.env.log)
+ logger.info('Listening on port %d', api.env.lite_xmlrpc_port)
+ try:
+ XMLRPCServer.serve_forever()
+ except KeyboardInterrupt:
+ XMLRPCServer.server_close()
+ logger.info('Server shutdown.')