prettifying source/
[bootmanager.git] / source / BootAPI.py
index f3092fb..c24afbc 100644 (file)
@@ -1,5 +1,5 @@
-#!/usr/bin/python2
-
+#!/usr/bin/python
+#
 # Copyright (c) 2003 Intel Corporation
 # All rights reserved.
 #
@@ -12,11 +12,15 @@ import xml.parsers.expat
 import hmac
 import string
 import sha
+import cPickle
+import utils
+import os
 
 from Exceptions import *
 
+stash = None
 
-def create_auth_structure( vars, call_params ):
+def create_auth_structure(vars, call_params):
     """
     create and return an authentication structure for a Boot API
     call. Vars contains the boot manager runtime variables, and
@@ -24,30 +28,67 @@ def create_auth_structure( vars, call_params ):
     API call. Return None if unable to (typically due to missing
     keys in vars, such as node_id or node_key)
     """
-    
-    auth= {}
-    auth['AuthMethod']= 'hmac'
+
+    auth = {}
 
     try:
-        network= vars['NETWORK_SETTINGS']
-        
-        auth['node_id']= vars['NODE_ID']
-        auth['node_ip']= network['ip']
-        node_key= vars['NODE_KEY']
-    except KeyError, e:
-        return None
-
-    params= serialize_params(call_params)
-    params.sort()
-    msg= "[" + "".join(params) + "]"
-    node_hmac= hmac.new(node_key,msg.encode('utf-8'),sha).hexdigest()
-    auth['value']= node_hmac
+        auth_session = {}
+        auth_session['AuthMethod'] = 'session'
 
-    return auth
+        if not vars.has_key('NODE_SESSION'):
+            # Try to load /etc/planetlab/session if it exists.
+            sessionfile = open('/etc/planetlab/session', 'r')
+            session = sessionfile.read().strip()
+
+            auth_session['session'] = session
+            # Test session.  Faults if it's no good.
+            vars['API_SERVER_INST'].AuthCheck(auth_session)
+            vars['NODE_SESSION'] = session
 
+            sessionfile.close()
+        else:
+            auth_session['session'] = vars['NODE_SESSION']
+
+        auth = auth_session
+
+    except:
+        auth['AuthMethod'] = 'hmac'
+
+        try:
+            auth['node_id'] = vars['NODE_ID']
+            auth['node_ip'] = vars['INTERFACE_SETTINGS']['ip']
+        except KeyError as e:
+            return None
+
+        node_hmac = hmac.new(vars['NODE_KEY'], "[]".encode('utf-8'), sha).hexdigest()
+        auth['value'] = node_hmac
+        try:
+            auth_session = {}
+            if not vars.has_key('NODE_SESSION'):
+                session = vars['API_SERVER_INST'].GetSession(auth)
+                auth_session['session'] = session
+                vars['NODE_SESSION'] = session
+                # NOTE: save session value to /etc/planetlab/session for 
+                # RunlevelAgent and future BootManager runs
+                if not os.path.exists("/etc/planetlab"):
+                    os.makedirs("/etc/planetlab")
+                sessionfile = open('/etc/planetlab/session', 'w')
+                sessionfile.write(vars['NODE_SESSION'])
+                sessionfile.close()
+            else:
+                auth_session['session'] = vars['NODE_SESSION']
+
+            auth_session['AuthMethod'] = 'session'
+            auth = auth_session
+
+        except Exception as e:
+            # NOTE: BM has failed to authenticate utterly.
+            raise BootManagerAuthenticationException("{}".format(e))
+
+    return auth
 
 
-def serialize_params( call_params ):
+def serialize_params(call_params):
     """
     convert a list of parameters into a format that will be used in the
     hmac generation. both the boot manager and plc must have a common
@@ -57,20 +98,28 @@ def serialize_params( call_params ):
     them into one long string encased in a set of braces.
     """
 
-    values= []
+    values = []
     
     for param in call_params:
         if isinstance(param,list) or isinstance(param,tuple):
             values += serialize_params(param)
         elif isinstance(param,dict):
             values += serialize_params(param.values())
+        elif isinstance(param,xmlrpclib.Boolean):
+            # bool was not a real type in Python <2.3 and had to be
+            # marshalled as a custom type in xmlrpclib. Make sure that
+            # bools serialize consistently.
+            if param:
+                values.append("True")
+            else:
+                values.append("False")
         else:
             values.append(unicode(param))
                 
     return values
 
     
-def call_api_function( vars, function, user_params ):
+def call_api_function(vars, function, user_params):
     """
     call the named api function with params, and return the
     value to the caller. the authentication structure is handled
@@ -78,26 +127,71 @@ def call_api_function( vars, function, user_params ):
 
     If the call fails, a BootManagerException is raised.
     """
-    
-    try:
-        api_server= vars['API_SERVER_INST']
-    except KeyError, e:
-        raise BootManagerException, "No connection to the API server exists."
+    global stash
 
-    auth= create_auth_structure(vars,user_params)
+    try:
+        api_server = vars['API_SERVER_INST']
+    except KeyError as e:
+        raise BootManagerException("No connection to the API server exists.")
+
+    if api_server is None:
+        if not stash:
+            load(vars)
+        for i in stash:
+            if i[0] == function and i[1] == user_params:
+               return i[2]
+        raise BootManagerException(
+              "Disconnected operation failed, insufficient stash.")
+
+    auth = create_auth_structure(vars,user_params)
     if auth is None:
-        raise BootManagerException, \
-              "Could not create auth structure, missing values."
+        raise BootManagerException(
+              "Could not create auth structure, missing values.")
     
-    params= (auth,)
-    params= params + user_params
+    params = (auth,)
+    params = params + user_params
 
     try:
-        exec( "rc= api_server.%s(*params)" % function )
+        exec("rc= api_server.{}(*params)".format(function))
+        if stash is None:
+            stash = []
+        stash += [ [ function, user_params, rc ] ]
         return rc
-    except xmlrpclib.Fault, fault:
-        raise BootManagerException, "API Fault: %s" % fault
-    except xmlrpclib.ProtocolError, err:
-        raise BootManagerException,"XML RPC protocol error: %s" % err
-    except xml.parsers.expat.ExpatError, err:
-        raise BootManagerException,"XML parsing error: %s" % err
+    except xmlrpclib.Fault as fault:
+        raise BootManagerException("API Fault: {}".format(fault))
+    except xmlrpclib.ProtocolError as err:
+        raise BootManagerException("XML RPC protocol error: {}".format(err))
+    except xml.parsers.expat.ExpatError as err:
+        raise BootManagerException("XML parsing error: {}".format(err))
+
+
+class Stash(file):
+    mntpnt = '/tmp/stash'
+    def __init__(self, vars, mode):
+        utils.makedirs(self.mntpnt)
+        try:
+            utils.sysexec('mount -t auto -U {} {}'.format(vars['DISCONNECTED_OPERATION'], self.mntpnt))
+            # make sure it's not read-only
+            f = file('{}/api.cache'.format(self.mntpnt), 'a')
+            f.close()
+            file.__init__(self, '{}/api.cache'.format(self.mntpnt), mode)
+        except:
+            utils.sysexec_noerr('umount {}'.format(self.mntpnt))
+            raise BootManagerException("Couldn't find API-cache for disconnected operation")
+
+    def close(self):
+        file.close(self)
+        utils.sysexec_noerr('umount {}'.format(self.mntpnt))
+
+def load(vars):
+    global stash
+    s = Stash(vars, 'r')
+    stash = cPickle.load(s)
+    s.close()
+
+def save(vars):
+    global stash
+    if vars['DISCONNECTED_OPERATION']:
+        s = Stash(vars, 'w')
+        cPickle.dump(stash, s)
+        s.close()