From 25ba0fdec627f472689c9f93a254e666b5d4317d Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Thu, 27 Oct 2011 22:13:44 +0200 Subject: [PATCH] beginning/proof-of-concept of a generic/ module together with SFA_GENERIC_FLAVOUR to configure which implementations must be put together allows to trash componentserver --- Makefile | 5 +- config/default_config.xml | 12 +++- setup.py | 9 +-- sfa/generic/__init__.py | 47 +++++++++++++ sfa/generic/pl.py | 9 +++ sfa/generic/plcm.py | 8 +++ sfa/server/component.py | 7 +- sfa/server/componentserver.py | 126 ---------------------------------- sfa/server/sfaapi.py | 16 +++-- sfa/server/threadedserver.py | 22 ++++-- 10 files changed, 113 insertions(+), 148 deletions(-) create mode 100644 sfa/generic/__init__.py create mode 100644 sfa/generic/pl.py create mode 100644 sfa/generic/plcm.py delete mode 100644 sfa/server/componentserver.py diff --git a/Makefile b/Makefile index e3cd9a4c..04ed4bae 100644 --- a/Makefile +++ b/Makefile @@ -137,8 +137,9 @@ ifeq (,$(SSHURL)) else +$(RSYNC) ./sfa/ $(SSHURL)/usr/lib\*/python2.\*/site-packages/sfa/ +$(RSYNC) ./tests/ $(SSHURL)/root/tests-sfa - +$(RSYNC) $(BINS) $(SSHURL)/usr/bin - +$(RSYNC) ./sfa/init.d/sfa $(SSHURL)/etc/init.d + +$(RSYNC) $(BINS) $(SSHURL)/usr/bin/ + +$(RSYNC) ./sfa/init.d/sfa $(SSHURL)/etc/init.d/ + +$(RSYNC) ./config/default_config.xml $(SSHURL)/etc/sfa/ $(SSHCOMMAND) exec service sfa restart endif diff --git a/config/default_config.xml b/config/default_config.xml index 212dee42..670d6f26 100644 --- a/config/default_config.xml +++ b/config/default_config.xml @@ -18,6 +18,15 @@ Thierry Parmentelat Basic system variables. + + Generic Flavour + pl + This string refers to a class located in sfa.generic that describes + which specific implementation needs to be used for api, manager and driver objects. + PlanetLab users do not need to change this setting. + + + Human readable name plc @@ -49,9 +58,10 @@ Thierry Parmentelat it look like the user is the one performing the operation. Doing this requires a valid key pair and credential for the user. This option defines the path where key pairs and credentials are generated and stored. - This functionality is used by the SFA web gui + This functionality is used by the SFA web GUI. + diff --git a/setup.py b/setup.py index 019f5359..61c087ef 100755 --- a/setup.py +++ b/setup.py @@ -36,14 +36,15 @@ bins = [ package_dirs = [ 'sfa', - 'sfa/client', - 'sfa/methods', - 'sfa/plc', - 'sfa/server', 'sfa/trust', 'sfa/util', + 'sfa/client', + 'sfa/server', + 'sfa/methods', + 'sfa/generic', 'sfa/managers', 'sfa/managers/vini', + 'sfa/plc', 'sfa/rspecs', 'sfa/rspecs/elements', 'sfa/rspecs/elements/versions', diff --git a/sfa/generic/__init__.py b/sfa/generic/__init__.py new file mode 100644 index 00000000..362a4591 --- /dev/null +++ b/sfa/generic/__init__.py @@ -0,0 +1,47 @@ +from sfa.util.sfalogging import logger +from sfa.util.config import Config +import traceback + +# a bundle is the combination of +# (*) an api that reacts on the incoming requests to trigger the API methods +# (*) a manager that implements the function of the service, +# either aggregate, registry, or slicemgr +# (*) a driver that controls the underlying testbed +# +# +# The Generic class is a utility that uses the configuration to figure out +# which combination of these pieces need to be put together +# from config. +# this extra indirection is needed to adapt to the current naming scheme +# where we have 'pl' and 'plc' and components and the like, that does not +# yet follow a sensible scheme + +# needs refinements to cache more efficiently, esp. wrt the config + +class Generic: + + def __init__ (self, config): + self.config=config + + # proof of concept + # example flavour='pl' -> sfa.generic.pl.pl() + @staticmethod + def the_flavour (flavour=None, config=None): + if config is None: config=Config() + if flavour is None: flavour=config.SFA_GENERIC_FLAVOUR + flavour = flavour.lower() + #mixed = flavour.capitalize() + module_path="sfa.generic.%s"%flavour + classname="%s"%flavour + logger.info("Generic.the_flavour with flavour=%s"%flavour) + try: + module = __import__ (module_path, globals(), locals(), [classname]) + return getattr(module, classname)(config) + except: + logger.log_exc("Cannot locate generic instance with flavour=%s"%flavour) + + + # how to build an API object + # default is to use api_class but can be redefined + def make_api (self, *args, **kwds): + return self.api_class()(*args, **kwds) diff --git a/sfa/generic/pl.py b/sfa/generic/pl.py new file mode 100644 index 00000000..34753d67 --- /dev/null +++ b/sfa/generic/pl.py @@ -0,0 +1,9 @@ +from sfa.generic import Generic +import sfa.plc.plcsfaapi + +class pl (Generic): + + def api_class (self): + return sfa.plc.plcsfaapi.PlcSfaApi + + diff --git a/sfa/generic/plcm.py b/sfa/generic/plcm.py new file mode 100644 index 00000000..2abdd127 --- /dev/null +++ b/sfa/generic/plcm.py @@ -0,0 +1,8 @@ +from sfa.generic.pl import pl +import sfa.plc.plccomponentapi + +class plcm (pl): + + def api_class (self): + return sfa.plc.plccomponentapi.PlcComponentApi + diff --git a/sfa/server/component.py b/sfa/server/component.py index 953b5e74..3958c5fb 100644 --- a/sfa/server/component.py +++ b/sfa/server/component.py @@ -6,7 +6,7 @@ import os import time import sys -from sfa.server.componentserver import ComponentServer +from sfa.server.sfaserver import SfaServer # GeniLight client support is optional try: @@ -17,7 +17,8 @@ except ImportError: ## # Component is a SfaServer that serves component operations. -class Component(ComponentServer): +# set SFA_GENERIC_FLAVOUR=plcm to get a PlcComponentApi instance in the request handler +class Component(SfaServer): ## # Create a new registry object. # @@ -27,5 +28,5 @@ class Component(ComponentServer): # @param cert_file certificate filename containing public key (could be a GID file) def __init__(self, ip, port, key_file, cert_file): - ComponentServer.__init__(self, ip, port, key_file, cert_file) + SfaServer.__init__(self, ip, port, key_file, cert_file) self.server.interface = 'component' diff --git a/sfa/server/componentserver.py b/sfa/server/componentserver.py deleted file mode 100644 index 419ebf2d..00000000 --- a/sfa/server/componentserver.py +++ /dev/null @@ -1,126 +0,0 @@ -## -# This module implements a general-purpose server layer for sfa. -# The same basic server should be usable on the registry, component, or -# other interfaces. -# -# TODO: investigate ways to combine this with existing PLC server? -## - -import threading -import socket -import SimpleXMLRPCServer - -from sfa.util.sfalogging import logger -from sfa.trust.certificate import Keypair, Certificate -from sfa.plc.api import PlcComponentApi -from sfa.server.threadedserver import ThreadedServer - - -## -# taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler - -class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): - """Secure XML-RPC request handler class. - - It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data. - """ - def setup(self): - self.connection = self.request - self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) - self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) - - def do_POST(self): - """Handles the HTTPS POST request. - - It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly. - """ - try: - peer_cert = Certificate() - peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate()) - self.api = PlcComponentApi(peer_cert = peer_cert, - interface = self.server.interface, - key_file = self.server.key_file, - cert_file = self.server.cert_file) - # get arguments - request = self.rfile.read(int(self.headers["content-length"])) - # In previous versions of SimpleXMLRPCServer, _dispatch - # could be overridden in this class, instead of in - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, - # check to see if a subclass implements _dispatch and dispatch - # using that method if present. - #response = self.server._marshaled_dispatch(request, getattr(self, '_dispatch', None)) - # XX TODO: Need to get the real remote address - remote_addr = (remote_ip, remote_port) = self.connection.getpeername() - self.api.remote_addr = remote_addr - #remote_addr = (self.rfile.connection.remote_ip, remote_port) - #self.api.remote_addr = remote_addr - response = self.api.handle(remote_addr, request) - - - except Exception, fault: - raise - # This should only happen if the module is buggy - # internal error, report as HTTP server error - self.send_response(500) - self.end_headers() - logger.log_exc("componentserver.SecureXMLRpcRequestHandler.do_POST") - 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() # Modified here! - -## -# Implements an HTTPS XML-RPC server. Generally it is expected that SFA -# functions will take a credential string, which is passed to -# decode_authentication. Decode_authentication() will verify the validity of -# the credential, and verify that the user is using the key that matches the -# GID supplied in the credential. - -class ComponentServer(threading.Thread): - - ## - # Create a new SfaServer object. - # - # @param ip the ip address to listen on - # @param port the port to listen on - # @param key_file private key filename of registry - # @param cert_file certificate filename containing public key - # (could be a GID file) - - def __init__(self, ip, port, key_file, cert_file, api=None): - threading.Thread.__init__(self) - self.key = Keypair(filename = key_file) - self.cert = Certificate(filename = cert_file) - self.server = ThreadedServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file) - self.trusted_cert_list = None - self.register_functions() - - - ## - # Register functions that will be served by the XMLRPC server. This - # function should be overrided by each descendant class. - - def register_functions(self): - self.server.register_function(self.noop) - - ## - # Sample no-op server function. The no-op function decodes the credential - # that was passed to it. - - def noop(self, cred, anything): - return anything - - ## - # Execute the server, serving requests forever. - - def run(self): - self.server.serve_forever() - - diff --git a/sfa/server/sfaapi.py b/sfa/server/sfaapi.py index 97741bf6..87f7935e 100644 --- a/sfa/server/sfaapi.py +++ b/sfa/server/sfaapi.py @@ -1,9 +1,13 @@ +import os.path +import datetime + from sfa.util.faults import SfaAPIError from sfa.util.config import Config from sfa.util.cache import Cache from sfa.trust.auth import Auth from sfa.trust.certificate import Keypair, Certificate +from sfa.trust.credential import Credential # this is wrong all right, but temporary from sfa.managers.managerwrapper import ManagerWrapper, import_manager @@ -18,6 +22,7 @@ class SfaApi (XmlrpcApi): augmented with the local cryptographic material and hrn It also has the notion of neighbour sfa services as defined in /etc/sfa/{aggregates,registries}.xml + Finally it contains a cache instance It has no a priori knowledge of the underlying testbed """ @@ -33,6 +38,7 @@ class SfaApi (XmlrpcApi): return # Load configuration self.config = Config(config) + self.credential = None self.auth = Auth(peer_cert) self.interface = interface self.hrn = self.config.SFA_INTERFACE_HRN @@ -43,7 +49,6 @@ class SfaApi (XmlrpcApi): self.cache = cache if self.cache is None: self.cache = Cache() - self.credential = None # load registries from sfa.server.registry import Registries @@ -104,7 +109,7 @@ class SfaApi (XmlrpcApi): type = 'authority' path = self.config.SFA_DATA_DIR filename = ".".join([self.interface, self.hrn, type, "cred"]) - cred_filename = path + os.sep + filename + cred_filename = os.path.join(path,filename) cred = None if os.path.isfile(cred_filename): cred = Credential(filename = cred_filename) @@ -193,10 +198,11 @@ class SfaApi (XmlrpcApi): # see if this file exists # XX This is really the aggregate's credential. Using this is easier than getting - # the registry's credential from iteslf (ssl errors). - ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred" + # the registry's credential from iteslf (ssl errors). + filename = self.interface + self.hrn + ".ma.cred" + ma_cred_path = os.path.join(self.config.SFA_DATA_DIR,filename) try: - self.credential = Credential(filename = ma_cred_filename) + self.credential = Credential(filename = ma_cred_path) except IOError: self.credential = self.getCredentialFromRegistry() diff --git a/sfa/server/threadedserver.py b/sfa/server/threadedserver.py index 6dafa0bb..b9b3ba54 100644 --- a/sfa/server/threadedserver.py +++ b/sfa/server/threadedserver.py @@ -21,8 +21,8 @@ from sfa.util.config import Config from sfa.util.cache import Cache from sfa.trust.certificate import Certificate from sfa.trust.trustedroots import TrustedRoots -#can we get rid of that ? -from sfa.plc.api import PlcSfaApi +# don't hard code an api class anymore here +from sfa.generic import Generic ## # Verification callback for pyOpenSSL. We do our own checking of keys because @@ -95,11 +95,18 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): try: peer_cert = Certificate() peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate()) - self.api = PlcSfaApi(peer_cert = peer_cert, - interface = self.server.interface, - key_file = self.server.key_file, - cert_file = self.server.cert_file, - cache = self.cache) + generic=Generic.the_flavour() + self.api = generic.make_api (peer_cert = peer_cert, + interface = self.server.interface, + key_file = self.server.key_file, + cert_file = self.server.cert_file, + cache = self.cache) + #logger.info("SecureXMLRpcRequestHandler.do_POST:") + #logger.info("interface=%s"%self.server.interface) + #logger.info("key_file=%s"%self.server.key_file) + #logger.info("api=%s"%self.api) + #logger.info("server=%s"%self.server) + #logger.info("handler=%s"%self) # get arguments request = self.rfile.read(int(self.headers["content-length"])) remote_addr = (remote_ip, remote_port) = self.connection.getpeername() @@ -127,6 +134,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): ## # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher): + def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True): """Secure XML-RPC server. -- 2.43.0