beginning/proof-of-concept of a generic/ module together with SFA_GENERIC_FLAVOUR to
Thierry Parmentelat [Thu, 27 Oct 2011 20:13:44 +0000 (22:13 +0200)]
configure which implementations must be put together
allows to trash componentserver

Makefile
config/default_config.xml
setup.py
sfa/generic/__init__.py [new file with mode: 0644]
sfa/generic/pl.py [new file with mode: 0644]
sfa/generic/plcm.py [new file with mode: 0644]
sfa/server/component.py
sfa/server/componentserver.py [deleted file]
sfa/server/sfaapi.py
sfa/server/threadedserver.py

index e3cd9a4..04ed4ba 100644 (file)
--- 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
 
index 212dee4..670d6f2 100644 (file)
@@ -18,6 +18,15 @@ Thierry Parmentelat
       <description>Basic system variables.</description>
 
       <variablelist>
+       <variable id="generic_flavour" type="string">
+         <name>Generic Flavour</name>
+         <value>pl</value>
+         <description>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.
+         </description>
+       </variable>
+
         <variable id="interface_hrn" type="string">
           <name>Human readable name</name>
           <value>plc</value>
@@ -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. 
             </description> 
         </variable>
+
       </variablelist>
     </category>
 
index 019f535..61c087e 100755 (executable)
--- 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 (file)
index 0000000..362a459
--- /dev/null
@@ -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 (file)
index 0000000..34753d6
--- /dev/null
@@ -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 (file)
index 0000000..2abdd12
--- /dev/null
@@ -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
+
index 953b5e7..3958c5f 100644 (file)
@@ -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 (file)
index 419ebf2..0000000
+++ /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()
-
-
index 97741bf..87f7935 100644 (file)
@@ -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()
 
index 6dafa0b..b9b3ba5 100644 (file)
@@ -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.