summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNarayan Desai <desai@mcs.anl.gov>2005-10-24 08:47:26 +0000
committerNarayan Desai <desai@mcs.anl.gov>2005-10-24 08:47:26 +0000
commit959d380863f429cdc322d66f8a3341af5d4d8058 (patch)
tree19f7e3c56b5f37b19f7e4ca39f067fdac2cdcb26 /src
parenta37b3ac21f6e5dc6fed4f4c7046ccd689ee38a69 (diff)
downloadbcfg2-959d380863f429cdc322d66f8a3341af5d4d8058.tar.gz
bcfg2-959d380863f429cdc322d66f8a3341af5d4d8058.tar.bz2
bcfg2-959d380863f429cdc322d66f8a3341af5d4d8058.zip
sync from cobalt tree
2005/10/24 03:06:46-05:00 anl.gov!desai (Logical change 1.341) git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@1404 ce84e21b-d406-0410-9b95-82705330c041
Diffstat (limited to 'src')
-rw-r--r--src/lib/Server/Component.py177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/lib/Server/Component.py b/src/lib/Server/Component.py
index e69de29bb..54af40db8 100644
--- a/src/lib/Server/Component.py
+++ b/src/lib/Server/Component.py
@@ -0,0 +1,177 @@
+'''Cobalt component base classes'''
+__revision__ = '$Revision: 1.4 $'
+
+from ConfigParser import ConfigParser, NoOptionError
+from cPickle import loads, dumps
+from M2Crypto import SSL
+from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCDispatcher
+from select import select
+from socket import gethostname
+from sys import exc_info
+import sys
+from syslog import openlog, syslog, LOG_INFO, LOG_ERR, LOG_LOCAL0
+from traceback import extract_tb
+from xmlrpclib import dumps, loads, Fault
+from urlparse import urlparse
+
+class CobaltXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
+ '''CobaltXMLRPCRequestHandler takes care of ssl xmlrpc requests'''
+ def finish(self):
+ '''Finish HTTPS connections properly'''
+ self.request.set_shutdown(SSL.SSL_RECEIVED_SHUTDOWN | SSL.SSL_SENT_SHUTDOWN)
+ self.request.close()
+
+ def do_POST(self):
+ '''Overload do_POST to pass through client address information'''
+ try:
+ # get arguments
+ data = self.rfile.read(int(self.headers["content-length"]))
+ response = self.server._cobalt_marshalled_dispatch(data, self.client_address)
+ except: # This should only happen if the module is buggy
+ # internal error, report as HTTP server error
+ (trace, val, trb) = exc_info()
+ syslog(LOG_ERR, "Unexpected failure in handler")
+ for line in extract_tb(trb):
+ syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n' % line)
+ syslog(LOG_ERR, "%s: %s\n"%(trace, val))
+ del trace, val, trb
+ 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)
+
+class Component(SSL.SSLServer,
+ SimpleXMLRPCDispatcher):
+ """Cobalt component providing XML-RPC access"""
+ __name__ = 'Component'
+ __implementation__ = 'Generic'
+ __statefields__ = []
+
+ def __init__(self, setup):
+ # need to get addr
+ self.setup = setup
+ self.cfile = ConfigParser()
+ openlog(self.__implementation__, 0, LOG_LOCAL0)
+ if setup['configfile']:
+ cfilename = setup['configfile']
+ else:
+ cfilename = '/etc/cobalt.conf'
+ self.cfile.read([cfilename])
+ if not self.cfile.has_section('communication'):
+ print "Configfile missing communication section"
+ raise SystemExit, 1
+ self.static = False
+ if not self.cfile.has_section('components'):
+ print "Configfile missing components section"
+ raise SystemExit, 1
+
+ if self.cfile._sections['components'].has_key(self.__name__):
+ self.static = True
+ location = urlparse(self.cfile.get('components', self.__name__))[1].split(':')
+ location = (location[0], int(location[1]))
+ else:
+ location = (gethostname(), 0)
+
+ self.password = self.cfile.get('communication', 'password')
+ sslctx = SSL.Context('sslv23')
+ try:
+ keyfile = self.cfile.get('communication', 'key')
+ except NoOptionError:
+ print "No key specified in cobalt.conf"
+ raise SystemExit, 1
+ sslctx.load_cert_chain(keyfile)
+ #sslctx.load_verify_locations('ca.pem')
+ #sslctx.set_client_CA_list_from_file('ca.pem')
+ sslctx.set_verify(SSL.verify_none, 15)
+ #sslctx.set_allow_unknown_ca(1)
+ sslctx.set_session_id_ctx(self.__name__)
+ sslctx.set_info_callback(self.handle_sslinfo)
+ #sslctx.set_tmp_dh('dh1024.pem')
+ self.logRequests = 0
+ # setup unhandled request syslog handling
+ SimpleXMLRPCDispatcher.__init__(self)
+ SSL.SSLServer.__init__(self, location, CobaltXMLRPCRequestHandler, sslctx)
+ self.port = self.socket.socket.getsockname()[1]
+ syslog(LOG_INFO, "Bound to port %s" % self.port)
+ self.funcs.update({'HandleEvents':self.HandleEvents,
+ 'system.listMethods':self.system_listMethods})
+
+ def HandleEvents(self, address, event_list):
+ '''Default event handler'''
+ return True
+
+ def handle_sslinfo(self, where, ret, ssl_ptr):
+ '''This is where we need to handle all ssl negotiation issues'''
+ pass
+
+ def _cobalt_marshalled_dispatch(self, data, address):
+ """Decode and dispatch XMLRPC requests. Overloaded to pass through
+ client address information
+ """
+ rawparams, method = loads(data)
+ if len(rawparams) < 2:
+ syslog(LOG_ERR, "No authentication included with request from %s" % address[0])
+ return dumps(Fault(2, "No Authentication Info"))
+ user = rawparams[0]
+ password = rawparams[1]
+ params = rawparams[2:]
+ # check authentication
+ if not self._authenticate_connection(method, user, password, address):
+ syslog(LOG_ERR, "Authentication failure from %s" % address[0])
+ return dumps(Fault(3, "Authentication Failure"))
+ # generate response
+ try:
+ # all handlers must take address as the first argument
+ response = self._dispatch(method, (address, ) + params)
+ # wrap response in a singleton tuple
+ response = (response,)
+ response = dumps(response, methodresponse=1)
+ except Fault, fault:
+ response = dumps(fault)
+ except:
+ # report exception back to server
+ response = dumps(Fault(1,
+ "%s:%s" % (sys.exc_type, sys.exc_value)))
+
+ return response
+
+ def _authenticate_connection(self, method, user, password, address):
+ '''Authenticate new connection'''
+ (user, address, method)
+ return password == self.password
+
+ def save_state(self):
+ '''Save fields defined in __statefields__ in /var/spool/cobalt/__implementation__'''
+ if self.__statefields__:
+ savedata = tuple([getattr(self, field) for field in self.__statefields__])
+ try:
+ statefile = open("/var/spool/cobalt/%s" % self.__implementation__, 'w')
+ # need to flock here
+ statefile.write(dumps(savedata))
+ except:
+ syslog(LOG_INFO, "Statefile save failed; data persistence disabled")
+ self.__statefields__ = []
+
+ def load_state(self):
+ '''Load fields defined in __statefields__ from /var/spool/cobalt/__implementation__'''
+ if self.__statefields__:
+ try:
+ loaddata = loads(open("/var/spool/cobalt/%s" % self.__implementation__).read())
+ except:
+ syslog(LOG_INFO, "Statefile load failed")
+ return
+ for field in self.__statefields__:
+ setattr(self, field, loaddata[self.__statefields__.index(field)])
+
+ def system_listMethods(self, address):
+ """get rid of the address argument and call the underlying dispatcher method"""
+ return SimpleXMLRPCDispatcher.system_listMethods(self)