X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=source%2FBootAPI.py;h=db173565895d51ef835e2603cf03c911aaffb792;hb=3d1aa2ec9d43ee03243c0808a85bdcc578a7ace8;hp=a27593bd31c5109bc6c7fda2e8799c8a3061e0f3;hpb=bd85ea4bb68783a0a58a6ddc6c3cc1dd22715eb1;p=bootmanager.git diff --git a/source/BootAPI.py b/source/BootAPI.py index a27593b..db17356 100644 --- a/source/BootAPI.py +++ b/source/BootAPI.py @@ -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,28 +28,68 @@ 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 - - msg= serialize_params(call_params) - node_hmac= hmac.new(node_key,msg,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. + with open('/etc/planetlab/session', 'r') as sessionfile: + 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 + + 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") + with open('/etc/planetlab/session', 'w') as sessionfile: + sessionfile.write(vars['NODE_SESSION']) + + 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. + import traceback + traceback.print_exc() + 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 @@ -55,50 +99,28 @@ def serialize_params( call_params ): them into one long string encased in a set of braces. """ - # if there are no parameters, just return empty paren set - if len(call_params) == 0: - return "[]" + values = [] - values= [] - for param in call_params: if isinstance(param,list) or isinstance(param,tuple): - values= values + map(str,param) + values += serialize_params(param) elif isinstance(param,dict): - values= values + collapse_dict(param) + 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( str(param) ) - - values.sort() - values= "[" + string.join(values,"") + "]" - return values + values.append(unicode(param)) - -def collapse_dict( value ): - """ - given a dictionary, return a list of all the keys and values as strings, - in no particular order - """ + return values - item_list= [] - - if not isinstance(value,dict): - return item_list - - for key in value.keys(): - key_value= value[key] - if isinstance(key_value,list) or isinstance(key_value,tuple): - item_list= item_list + map(str,key_value) - elif isinstance(key_value,dict): - item_list= item_list + collapse_dict(key_value) - else: - item_list.append( str(key_value) ) - return item_list - - - -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 @@ -106,26 +128,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." - - params= (auth,) - params= params + user_params + raise BootManagerException( + "Could not create auth structure, missing values.") + + 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()