d69755b0bf7d3b649a1c9ee8b70bce3107e3fc3b
[bootmanager.git] / source / BootAPI.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2003 Intel Corporation
4 # All rights reserved.
5 #
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
7 # All rights reserved.
8
9
10 import xmlrpclib
11 import xml.parsers.expat
12 import hmac
13 import string
14 import sha
15 import cPickle
16 import utils
17
18 from Exceptions import *
19
20 stash = None
21
22 def create_auth_structure( vars, call_params ):
23     """
24     create and return an authentication structure for a Boot API
25     call. Vars contains the boot manager runtime variables, and
26     call_params is a tuple of the parameters that will be passed to the
27     API call. Return None if unable to (typically due to missing
28     keys in vars, such as node_id or node_key)
29     """
30
31     auth= {}
32
33     try:
34         auth_session = {}
35         auth_session['AuthMethod'] = 'session'
36
37         if not vars.has_key('NODE_SESSION'):
38             # Try to load /etc/planetlab/session if it exists.
39             sessionfile = open('/etc/planetlab/session', 'r')
40             session = sessionfile.read().strip()
41
42             auth_session['session'] = session
43             # Test session.  Faults if it's no good.
44             vars['API_SERVER_INST'].AuthCheck(auth_session)
45             vars['NODE_SESSION'] = session
46
47             sessionfile.close()
48         else:
49             auth_session['session'] = vars['NODE_SESSION']
50
51         auth = auth_session
52
53     except:
54         auth['AuthMethod']= 'hmac'
55
56         try:
57             auth['node_id'] = vars['NODE_ID']
58             auth['node_ip'] = vars['INTERFACE_SETTINGS']['ip']
59         except KeyError, e:
60             return None
61
62         node_hmac= hmac.new(vars['NODE_KEY'], "[]".encode('utf-8'), sha).hexdigest()
63         auth['value']= node_hmac
64         try:
65             auth_session = {}
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                 sessionfile = open('/etc/planetlab/session', 'w')
73                 sessionfile.write( vars['NODE_SESSION'] )
74                 sessionfile.close()
75             else:
76                 auth_session['session'] = vars['NODE_SESSION']
77
78             auth_session['AuthMethod'] = 'session'
79             auth = auth_session
80
81         except Exception, e:
82             print e
83             pass
84
85     return auth
86
87
88 def serialize_params( call_params ):
89     """
90     convert a list of parameters into a format that will be used in the
91     hmac generation. both the boot manager and plc must have a common
92     format. full documentation is in the boot manager technical document,
93     but essentially we are going to take all the values (and keys for
94     dictionary objects), and put them into a list. sort them, and combine
95     them into one long string encased in a set of braces.
96     """
97
98     values= []
99     
100     for param in call_params:
101         if isinstance(param,list) or isinstance(param,tuple):
102             values += serialize_params(param)
103         elif isinstance(param,dict):
104             values += serialize_params(param.values())
105         elif isinstance(param,xmlrpclib.Boolean):
106             # bool was not a real type in Python <2.3 and had to be
107             # marshalled as a custom type in xmlrpclib. Make sure that
108             # bools serialize consistently.
109             if param:
110                 values.append("True")
111             else:
112                 values.append("False")
113         else:
114             values.append(unicode(param))
115                 
116     return values
117
118     
119 def call_api_function( vars, function, user_params ):
120     """
121     call the named api function with params, and return the
122     value to the caller. the authentication structure is handled
123     automatically, and doesn't need to be passed in with params.
124
125     If the call fails, a BootManagerException is raised.
126     """
127     global stash
128
129     try:
130         api_server= vars['API_SERVER_INST']
131     except KeyError, e:
132         raise BootManagerException, "No connection to the API server exists."
133
134     if api_server is None:
135         if not stash:
136             load(vars)
137         for i in stash:
138             if i[0] == function and i[1] == user_params:
139                return i[2]
140         raise BootManagerException, \
141               "Disconnected operation failed, insufficient stash."
142
143     auth= create_auth_structure(vars,user_params)
144     if auth is None:
145         raise BootManagerException, \
146               "Could not create auth structure, missing values."
147     
148     params= (auth,)
149     params= params + user_params
150
151     try:
152         exec( "rc= api_server.%s(*params)" % function )
153         if stash is None:
154             stash = []
155         stash += [ [ function, user_params, rc ] ]
156         return rc
157     except xmlrpclib.Fault, fault:
158         raise BootManagerException, "API Fault: %s" % fault
159     except xmlrpclib.ProtocolError, err:
160         raise BootManagerException,"XML RPC protocol error: %s" % err
161     except xml.parsers.expat.ExpatError, err:
162         raise BootManagerException,"XML parsing error: %s" % err
163
164
165 class Stash(file):
166     mntpnt = '/tmp/stash'
167     def __init__(self, vars, mode):
168         utils.makedirs(self.mntpnt)
169         try:
170             utils.sysexec('mount -t auto -U %s %s' % (vars['DISCONNECTED_OPERATION'], self.mntpnt))
171             # make sure it's not read-only
172             f = file('%s/api.cache' % self.mntpnt, 'a')
173             f.close()
174             file.__init__(self, '%s/api.cache' % self.mntpnt, mode)
175         except:
176             utils.sysexec_noerr('umount %s' % self.mntpnt)
177             raise BootManagerException, "Couldn't find API-cache for disconnected operation"
178
179     def close(self):
180         file.close(self)
181         utils.sysexec_noerr('umount %s' % self.mntpnt)
182
183 def load(vars):
184     global stash
185     s = Stash(vars, 'r')
186     stash = cPickle.load(s)
187     s.close()
188
189 def save(vars):
190     global stash
191     if vars['DISCONNECTED_OPERATION']:
192         s = Stash(vars, 'w')
193         cPickle.dump(stash, s)
194         s.close()