beginning/proof-of-concept of a generic/ module together with SFA_GENERIC_FLAVOUR to
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Thu, 27 Oct 2011 20:13:44 +0000 (22:13 +0200)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
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
 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
 
        $(SSHCOMMAND) exec service sfa restart
 endif
 
index 212dee4..670d6f2 100644 (file)
@@ -18,6 +18,15 @@ Thierry Parmentelat
       <description>Basic system variables.</description>
 
       <variablelist>
       <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>
         <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.
             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>
             </description> 
         </variable>
+
       </variablelist>
     </category>
 
       </variablelist>
     </category>
 
index 019f535..61c087e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -36,14 +36,15 @@ bins = [
 
 package_dirs = [
     'sfa', 
 
 package_dirs = [
     'sfa', 
-    'sfa/client',
-    'sfa/methods',
-    'sfa/plc',
-    'sfa/server',
     'sfa/trust',
     'sfa/util', 
     'sfa/trust',
     'sfa/util', 
+    'sfa/client',
+    'sfa/server',
+    'sfa/methods',
+    'sfa/generic',
     'sfa/managers',
     'sfa/managers/vini',
     'sfa/managers',
     'sfa/managers/vini',
+    'sfa/plc',
     'sfa/rspecs',
     'sfa/rspecs/elements',
     'sfa/rspecs/elements/versions',
     '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
 
 import time
 import sys
 
-from sfa.server.componentserver import ComponentServer
+from sfa.server.sfaserver import SfaServer
  
 # GeniLight client support is optional
 try:
  
 # GeniLight client support is optional
 try:
@@ -17,7 +17,8 @@ except ImportError:
 ##
 # Component is a SfaServer that serves component operations.
 
 ##
 # 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.
     #
     ##
     # 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):
     # @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'
         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.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
 
 # 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
     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
     """
 
     It has no a priori knowledge of the underlying testbed
     """
 
@@ -33,6 +38,7 @@ class SfaApi (XmlrpcApi):
             return
         # Load configuration
         self.config = Config(config)
             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
         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.cache = cache
         if self.cache is None:
             self.cache = Cache()
-        self.credential = None
 
         # load registries
         from sfa.server.registry import Registries
 
         # 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"])
         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)
         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
 
         # 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:
         try:
-            self.credential = Credential(filename = ma_cred_filename)
+            self.credential = Credential(filename = ma_cred_path)
         except IOError:
             self.credential = self.getCredentialFromRegistry()
 
         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
 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
 
 ##
 # 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())
         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()
             # 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):
 ##
 # 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.
 
     def __init__(self, server_address, HandlerClass, key_file, cert_file, logRequests=True):
         """Secure XML-RPC server.