From 3377f174bdd0bcf3f6212038dbc51cdb922e5481 Mon Sep 17 00:00:00 2001 From: Narayan Desai Date: Fri, 13 Jul 2007 18:16:04 +0000 Subject: Add checker support to Component git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@3441 ce84e21b-d406-0410-9b95-82705330c041 --- src/lib/Client/Frame.py | 6 +- src/lib/Component.py | 6 +- src/lib/Server/Component.py | 280 --------------------------------- src/lib/Server/Reports/importscript.py | 15 +- 4 files changed, 21 insertions(+), 286 deletions(-) delete mode 100644 src/lib/Server/Component.py (limited to 'src') diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py index f685e7491..a2682d023 100644 --- a/src/lib/Client/Frame.py +++ b/src/lib/Client/Frame.py @@ -1,7 +1,7 @@ '''Frame is the Client Framework that verifies and installs entries, and generates statistics''' __revision__ = '$Revision$' -import logging, time +import binascii, logging, time import Bcfg2.Client.Tools def cmpent(ent1, ent2): @@ -326,6 +326,10 @@ class Frame: for item in data: item.set('qtext', '') container.append(item) + if item.get('current_diff', False): + item.set('current_bdiff', + binascii.a2b_base64(item.get('current_diff'))) + item.set('current_diff', '') timeinfo = Bcfg2.Client.XML.Element("OpStamps") feedback.append(stats) diff --git a/src/lib/Component.py b/src/lib/Component.py index 34dfbddc2..71df6d68b 100644 --- a/src/lib/Component.py +++ b/src/lib/Component.py @@ -60,11 +60,12 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): class TLSServer(Bcfg2.tlslite.api.TLSSocketServerMixIn, BaseHTTPServer.HTTPServer): '''This class is an tlslite-using SSLServer''' - def __init__(self, address, keyfile, handler): + def __init__(self, address, keyfile, handler, checker=None): self.sc = Bcfg2.tlslite.api.SessionCache() x509 = Bcfg2.tlslite.api.X509() s = open(keyfile).read() x509.parse(s) + self.checker = checker try: self.key = Bcfg2.tlslite.api.parsePEMKey(s, private=True) except: @@ -83,7 +84,8 @@ class TLSServer(Bcfg2.tlslite.api.TLSSocketServerMixIn, try: tlsConnection.handshakeServer(certChain=self.chain, privateKey=self.key, - sessionCache=self.sc) + sessionCache=self.sc, + checher=self.checker) tlsConnection.ignoreAbruptClose = True return True except Bcfg2.tlslite.errors.TLSError, error: diff --git a/src/lib/Server/Component.py b/src/lib/Server/Component.py deleted file mode 100644 index 34dfbddc2..000000000 --- a/src/lib/Server/Component.py +++ /dev/null @@ -1,280 +0,0 @@ -'''Cobalt component base classes''' -__revision__ = '$Revision$' - -import atexit, logging, select, signal, socket, sys, time, urlparse, xmlrpclib, cPickle, ConfigParser -import BaseHTTPServer, SimpleXMLRPCServer, SocketServer -import Bcfg2.tlslite.errors -import Bcfg2.tlslite.api - -import Bcfg2.Client.Proxy as Proxy -from Bcfg2.tlslite.TLSConnection import TLSConnection - -log = logging.getLogger('Component') - -class ComponentInitError(Exception): - '''Raised in case of component initialization failure''' - pass - -class ComponentKeyError(Exception): - '''raised in case of key parse fails''' - pass - -class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): - '''CobaltXMLRPCRequestHandler takes care of ssl xmlrpc requests''' - def finish(self): - '''Finish HTTPS connections properly''' - 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 - log.error("Unexcepted handler failure in do_POST", exc_info=1) - self.send_response(500) - self.end_headers() - else: - # got a valid XML RPC response - try: - 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() - except socket.error: - pass - - def setup(self): - '''Setup a working connection''' - self.connection = self.request - self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) - self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) - -class TLSServer(Bcfg2.tlslite.api.TLSSocketServerMixIn, - BaseHTTPServer.HTTPServer): - '''This class is an tlslite-using SSLServer''' - def __init__(self, address, keyfile, handler): - self.sc = Bcfg2.tlslite.api.SessionCache() - x509 = Bcfg2.tlslite.api.X509() - s = open(keyfile).read() - x509.parse(s) - try: - self.key = Bcfg2.tlslite.api.parsePEMKey(s, private=True) - except: - raise ComponentKeyError - self.chain = Bcfg2.tlslite.api.X509CertChain([x509]) - BaseHTTPServer.HTTPServer.__init__(self, address, handler) - - def finish_request(self, sock, client_address): - sock.settimeout(90) - tlsConnection = TLSConnection(sock) - if self.handshake(tlsConnection) == True: - self.RequestHandlerClass(tlsConnection, client_address, self) - tlsConnection.close() - - def handshake(self, tlsConnection): - try: - tlsConnection.handshakeServer(certChain=self.chain, - privateKey=self.key, - sessionCache=self.sc) - tlsConnection.ignoreAbruptClose = True - return True - except Bcfg2.tlslite.errors.TLSError, error: - return False - except socket.error: - return False - -class Component(TLSServer, - SimpleXMLRPCServer.SimpleXMLRPCDispatcher): - """Cobalt component providing XML-RPC access""" - __name__ = 'Component' - __implementation__ = 'Generic' - __statefields__ = [] - async_funcs = ['assert_location'] - - def __init__(self, setup): - # need to get addr - self.setup = setup - self.shut = False - signal.signal(signal.SIGINT, self.start_shutdown) - signal.signal(signal.SIGTERM, self.start_shutdown) - self.logger = logging.getLogger('Component') - self.cfile = ConfigParser.ConfigParser() - if setup['configfile']: - cfilename = setup['configfile'] - else: - cfilename = '/etc/bcfg2.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.urlparse(self.cfile.get('components', self.__name__))[1].split(':') - location = (location[0], int(location[1])) - else: - location = (socket.gethostname(), 0) - try: - keyfile = self.cfile.get('communication', 'key') - #keyfile = '/tmp/keys/server.pkey' - except ConfigParser.NoOptionError: - print "No key specified in cobalt.conf" - raise SystemExit, 1 - - self.password = self.cfile.get('communication', 'password') - - try: - TLSServer.__init__(self, location, keyfile, CobaltXMLRPCRequestHandler) - except socket.error: - self.logger.error("Failed to bind to socket") - raise ComponentInitError - except ComponentKeyError: - self.logger.error("Failed to parse key" % (keyfile)) - raise ComponentInitError - except: - self.logger.error("Failed to load ssl key %s" % (keyfile), exc_info=1) - raise ComponentInitError - try: - SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self) - except TypeError: - SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, False, None) - self.logRequests = 0 - self.port = self.socket.getsockname()[1] - self.url = "https://%s:%s" % (socket.gethostname(), self.port) - self.logger.info("Bound to port %s" % self.port) - self.funcs.update({'system.listMethods':self.addr_system_listMethods}) - self.atime = 0 - self.assert_location() - atexit.register(self.deassert_location) - - def _cobalt_marshalled_dispatch(self, data, address): - """Decode and dispatch XMLRPC requests. Overloaded to pass through - client address information - """ - try: - rawparams, method = xmlrpclib.loads(data) - except: - self.logger.error("Failed to parse request from %s" \ - % (address[0])) - #open('/tmp/badreq', 'w').write(data) - return xmlrpclib.dumps(xmlrpclib.Fault(4, "Bad Request")) - if len(rawparams) < 2: - self.logger.error("No authentication included with request from %s" % address[0]) - return xmlrpclib.dumps(xmlrpclib.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): - return xmlrpclib.dumps(xmlrpclib.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 = xmlrpclib.dumps(response, methodresponse=1) - except xmlrpclib.Fault, fault: - response = xmlrpclib.dumps(fault) - except TypeError, terror: - self.logger.error("Client %s called function %s with wrong argument count" % - (address[0], method), exc_info=1) - response = xmlrpclib.dumps(xmlrpclib.Fault(4, terror.args[0])) - except: - self.logger.error("Unexpected handler failure", exc_info=1) - # report exception back to server - response = xmlrpclib.dumps(xmlrpclib.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(cPickle.dumps(savedata)) - except: - self.logger.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 = cPickle.loads(open("/var/spool/cobalt/%s" % self.__implementation__).read()) - except: - self.logger.info("Statefile load failed") - return - for field in self.__statefields__: - setattr(self, field, loaddata[self.__statefields__.index(field)]) - - def addr_system_listMethods(self, address): - """get rid of the address argument and call the underlying dispatcher method""" - return SimpleXMLRPCServer.SimpleXMLRPCDispatcher.system_listMethods(self) - - def get_request(self): - '''We need to do work between requests, so select with timeout instead of blocking in accept''' - rsockinfo = [] - while self.socket not in rsockinfo: - if self.shut: - raise socket.error - for funcname in self.async_funcs: - func = getattr(self, funcname, False) - if callable(func): - func() - else: - self.logger.error("Cannot call uncallable method %s" % (funcname)) - try: - rsockinfo = select.select([self.socket], [], [], 10)[0] - except select.error: - continue - if self.socket in rsockinfo: - return self.socket.accept() - - def assert_location(self): - '''Assert component location with slp''' - if self.__name__ == 'service-location' or self.static: - return - if (time.time() - self.atime) > 240: - slp = Proxy.service_location() - slp.AssertService({'tag':'location', 'name':self.__name__, 'url':self.url}) - self.atime = time.time() - - def deassert_location(self): - '''remove registration from slp''' - if self.__name__ == 'service-location' or self.static: - return - slp = Proxy.service_location() - try: - slp.DeassertService([{'tag':'location', 'name':self.__name__, 'url':self.url}]) - except xmlrpclib.Fault, fault: - if fault.faultCode == 11: - self.logger.error("Failed to deregister self; no matching component") - - def serve_forever(self): - """Handle one request at a time until doomsday.""" - while not self.shut: - self.handle_request() - - def start_shutdown(self, signum, frame): - '''Shutdown on unexpected signals''' - self.shut = True - diff --git a/src/lib/Server/Reports/importscript.py b/src/lib/Server/Reports/importscript.py index 39e4ea77b..be7f59da6 100755 --- a/src/lib/Server/Reports/importscript.py +++ b/src/lib/Server/Reports/importscript.py @@ -2,7 +2,7 @@ '''Imports statistics.xml and clients.xml files in to database backend for new statistics engine''' __revision__ = '$Revision$' -import os, sys +import os, sys, binascii try: import settings except ImportError: @@ -146,6 +146,10 @@ if __name__ == '__main__': for r in statsdata.findall('.//Bad/*')+statsdata.findall('.//Extra/*')+statsdata.findall('.//Modified/*'): + if r.get('current_bdiff', False): + rc_diff = binascii.b2a_base64(r.get('current_bdiff')) + else: + rc_diff = r.get('current_diff', '') arguments = [r.get('owner', default=""), r.get('current_owner', default=""), r.get('group', default=""), r.get('current_group', default=""), r.get('perms', default=""), r.get('current_perms', default=""), @@ -153,7 +157,7 @@ if __name__ == '__main__': r.get('to', default=""), r.get('current_to', default=""), r.get('version', default=""), r.get('current_version', default=""), (r.get('current_exists', default="True").capitalize()=="True"), - r.get('current_diff', default="")] + rc_diff] if reasons_hash.has_key(tuple(arguments)): current_reason_id = reasons_hash[tuple(arguments)] if verbose: @@ -199,6 +203,11 @@ if __name__ == '__main__': ('Extra/*', extra_hash, 'reports_extra'), ('Modified/*', modified_hash, 'reports_modified')]: for x in statistics.findall(xpath): + if x.get('current_bdiff', False): + xc_diff = binascii.b2a_base64(x.get('current_bdiff')) + else: + xc_diff = x.get('current_diff', '') + arguments = [x.get('owner', default=""), x.get('current_owner', default=""), x.get('group', default=""), x.get('current_group', default=""), x.get('perms', default=""), x.get('current_perms', default=""), @@ -206,7 +215,7 @@ if __name__ == '__main__': x.get('to', default=""), x.get('current_to', default=""), x.get('version', default=""), x.get('current_version', default=""), (x.get('current_exists', default="True").capitalize()=="True"), - x.get('current_diff', default="")] + xc_diff] if not hashname.has_key((x.get('name'), x.tag, reasons_hash[tuple(arguments)])): cursor.execute("INSERT INTO "+tablename+" VALUES (NULL, %s, %s, %s, %s);", -- cgit v1.2.3-1-g7c22