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 /lite-xmlrpc.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 'lite-xmlrpc.py')
-rwxr-xr-x | lite-xmlrpc.py | 175 |
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.') |