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