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