Keep session around instead of calling the API for a new one every time a call is...
[bootmanager.git] / source / BootAPI.py
1 #!/usr/bin/python2
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     auth['AuthMethod']= 'hmac'
33
34     try:
35         auth['node_id'] = vars['NODE_ID']
36         auth['node_ip'] = vars['NETWORK_SETTINGS']['ip']
37     except KeyError, e:
38         return None
39
40     #params= serialize_params(call_params)
41     #params.sort()
42     #msg= "[" + "".join(params) + "]"
43     #node_hmac= hmac.new(vars['NODE_KEY'], msg.encode('utf-8'), sha).hexdigest()
44     node_hmac= hmac.new(vars['NODE_KEY'], "[]".encode('utf-8'), sha).hexdigest()
45     auth['value']= node_hmac
46     try:
47         auth_session = {}
48         if not vars.has_key('SESSION'):
49             session = vars['API_SERVER_INST'].GetSession(auth)
50             auth_session['session'] = session
51             vars['SESSION'] = session
52         else:
53             auth_session['session'] = vars['SESSION']
54         auth_session['AuthMethod'] = 'session'
55         auth = auth_session
56     except Exception, e:
57         print e
58         pass
59     print auth
60     return auth
61
62
63 def serialize_params( call_params ):
64     """
65     convert a list of parameters into a format that will be used in the
66     hmac generation. both the boot manager and plc must have a common
67     format. full documentation is in the boot manager technical document,
68     but essentially we are going to take all the values (and keys for
69     dictionary objects), and put them into a list. sort them, and combine
70     them into one long string encased in a set of braces.
71     """
72
73     values= []
74     
75     for param in call_params:
76         if isinstance(param,list) or isinstance(param,tuple):
77             values += serialize_params(param)
78         elif isinstance(param,dict):
79             values += serialize_params(param.values())
80         elif isinstance(param,xmlrpclib.Boolean):
81             # bool was not a real type in Python <2.3 and had to be
82             # marshalled as a custom type in xmlrpclib. Make sure that
83             # bools serialize consistently.
84             if param:
85                 values.append("True")
86             else:
87                 values.append("False")
88         else:
89             values.append(unicode(param))
90                 
91     return values
92
93     
94 def call_api_function( vars, function, user_params ):
95     """
96     call the named api function with params, and return the
97     value to the caller. the authentication structure is handled
98     automatically, and doesn't need to be passed in with params.
99
100     If the call fails, a BootManagerException is raised.
101     """
102     global stash
103
104     try:
105         api_server= vars['API_SERVER_INST']
106     except KeyError, e:
107         raise BootManagerException, "No connection to the API server exists."
108
109     if api_server is None:
110         if not stash:
111             load(vars)
112         for i in stash:
113             if i[0] == function and i[1] == user_params:
114                return i[2]
115         raise BootManagerException, \
116               "Disconnected operation failed, insufficient stash."
117
118     auth= create_auth_structure(vars,user_params)
119     if auth is None:
120         raise BootManagerException, \
121               "Could not create auth structure, missing values."
122     
123     params= (auth,)
124     params= params + user_params
125
126     try:
127         exec( "rc= api_server.%s(*params)" % function )
128         if stash is None:
129             stash = []
130         stash += [ [ function, user_params, rc ] ]
131         return rc
132     except xmlrpclib.Fault, fault:
133         raise BootManagerException, "API Fault: %s" % fault
134     except xmlrpclib.ProtocolError, err:
135         raise BootManagerException,"XML RPC protocol error: %s" % err
136     except xml.parsers.expat.ExpatError, err:
137         raise BootManagerException,"XML parsing error: %s" % err
138
139
140 class Stash(file):
141     mntpnt = '/tmp/stash'
142     def __init__(self, vars, mode):
143         utils.makedirs(self.mntpnt)
144         try:
145             utils.sysexec('mount -t auto -U %s %s' % (vars['DISCONNECTED_OPERATION'], self.mntpnt))
146             # make sure it's not read-only
147             f = file('%s/api.cache' % self.mntpnt, 'a')
148             f.close()
149             file.__init__(self, '%s/api.cache' % self.mntpnt, mode)
150         except:
151             utils.sysexec_noerr('umount %s' % self.mntpnt)
152             raise BootManagerException, "Couldn't find API-cache for disconnected operation"
153
154     def close(self):
155         file.close(self)
156         utils.sysexec_noerr('umount %s' % self.mntpnt)
157
158 def load(vars):
159     global stash
160     s = Stash(vars, 'r')
161     stash = cPickle.load(s)
162     s.close()
163
164 def save(vars):
165     global stash
166     if vars['DISCONNECTED_OPERATION']:
167         s = Stash(vars, 'w')
168         cPickle.dump(stash, s)
169         s.close()