3 # Copyright (c) 2003 Intel Corporation
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
11 import xml.parsers.expat
19 from Exceptions import *
23 def create_auth_structure(vars, call_params):
25 create and return an authentication structure for a Boot API
26 call. Vars contains the boot manager runtime variables, and
27 call_params is a tuple of the parameters that will be passed to the
28 API call. Return None if unable to (typically due to missing
29 keys in vars, such as node_id or node_key)
36 auth_session['AuthMethod'] = 'session'
38 if not vars.has_key('NODE_SESSION'):
39 # Try to load /etc/planetlab/session if it exists.
40 with open('/etc/planetlab/session', 'r') as sessionfile:
41 session = sessionfile.read().strip()
43 auth_session['session'] = session
44 # Test session. Faults if it's no good.
45 vars['API_SERVER_INST'].AuthCheck(auth_session)
46 vars['NODE_SESSION'] = session
49 auth_session['session'] = vars['NODE_SESSION']
54 auth['AuthMethod'] = 'hmac'
57 auth['node_id'] = vars['NODE_ID']
58 auth['node_ip'] = vars['INTERFACE_SETTINGS']['ip']
62 node_hmac = hmac.new(vars['NODE_KEY'], "[]".encode('utf-8'), sha).hexdigest()
63 auth['value'] = node_hmac
66 if not vars.has_key('NODE_SESSION'):
67 session = vars['API_SERVER_INST'].GetSession(auth)
68 auth_session['session'] = session
69 vars['NODE_SESSION'] = session
70 # NOTE: save session value to /etc/planetlab/session for
71 # RunlevelAgent and future BootManager runs
72 if not os.path.exists("/etc/planetlab"):
73 os.makedirs("/etc/planetlab")
74 with open('/etc/planetlab/session', 'w') as sessionfile:
75 sessionfile.write(vars['NODE_SESSION'])
78 auth_session['session'] = vars['NODE_SESSION']
80 auth_session['AuthMethod'] = 'session'
83 except Exception as e:
84 # NOTE: BM has failed to authenticate utterly.
87 raise BootManagerAuthenticationException("{}".format(e))
92 def serialize_params(call_params):
94 convert a list of parameters into a format that will be used in the
95 hmac generation. both the boot manager and plc must have a common
96 format. full documentation is in the boot manager technical document,
97 but essentially we are going to take all the values (and keys for
98 dictionary objects), and put them into a list. sort them, and combine
99 them into one long string encased in a set of braces.
104 for param in call_params:
105 if isinstance(param,list) or isinstance(param,tuple):
106 values += serialize_params(param)
107 elif isinstance(param,dict):
108 values += serialize_params(param.values())
109 elif isinstance(param,xmlrpclib.Boolean):
110 # bool was not a real type in Python <2.3 and had to be
111 # marshalled as a custom type in xmlrpclib. Make sure that
112 # bools serialize consistently.
114 values.append("True")
116 values.append("False")
118 values.append(unicode(param))
123 def call_api_function(vars, function, user_params):
125 call the named api function with params, and return the
126 value to the caller. the authentication structure is handled
127 automatically, and doesn't need to be passed in with params.
129 If the call fails, a BootManagerException is raised.
134 api_server = vars['API_SERVER_INST']
135 except KeyError as e:
136 raise BootManagerException("No connection to the API server exists.")
138 if api_server is None:
142 if i[0] == function and i[1] == user_params:
144 raise BootManagerException(
145 "Disconnected operation failed, insufficient stash.")
147 auth = create_auth_structure(vars,user_params)
149 raise BootManagerException(
150 "Could not create auth structure, missing values.")
153 params = params + user_params
156 exec("rc= api_server.{}(*params)".format(function))
159 stash += [ [ function, user_params, rc ] ]
161 except xmlrpclib.Fault as fault:
162 raise BootManagerException("API Fault: {}".format(fault))
163 except xmlrpclib.ProtocolError as err:
164 raise BootManagerException("XML RPC protocol error: {}".format(err))
165 except xml.parsers.expat.ExpatError as err:
166 raise BootManagerException("XML parsing error: {}".format(err))
170 mntpnt = '/tmp/stash'
171 def __init__(self, vars, mode):
172 utils.makedirs(self.mntpnt)
174 utils.sysexec('mount -t auto -U {} {}'.format(vars['DISCONNECTED_OPERATION'], self.mntpnt))
175 # make sure it's not read-only
176 f = file('{}/api.cache'.format(self.mntpnt), 'a')
178 file.__init__(self, '{}/api.cache'.format(self.mntpnt), mode)
180 utils.sysexec_noerr('umount {}'.format(self.mntpnt))
181 raise BootManagerException("Couldn't find API-cache for disconnected operation")
185 utils.sysexec_noerr('umount {}'.format(self.mntpnt))
190 stash = cPickle.load(s)
195 if vars['DISCONNECTED_OPERATION']:
197 cPickle.dump(stash, s)