freeipa/lite-xmlrpc.py

176 lines
5.7 KiB
Python
Executable File

#!/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 ipa_server import conn
from ipa_server.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.')