loop on authcheck code in case session is wrong AND api is unavailable.
[nodemanager.git] / plcapi.py
1 # $Id$
2
3 import safexmlrpc
4 import hmac, sha
5 import logger
6
7 class PLCAPI:
8     """
9     Wrapper around safexmlrpc.ServerProxy to automagically add an Auth
10     struct as the first argument to every XML-RPC call. Initialize
11     auth with either:
12
13     (node_id, key) => BootAuth
14     or
15     session => SessionAuth
16
17     To authenticate using the Boot Manager authentication method, or
18     the new session-based method.
19     """
20
21     def __init__(self, uri, cacert, auth, timeout = 90, **kwds):
22         self.uri = uri
23         self.cacert = cacert
24         self.timeout = timeout
25
26         if isinstance(auth, (tuple, list)):
27             (self.node_id, self.key) = auth
28             self.session = None
29         elif isinstance(auth, (str, unicode)):
30             self.node_id = self.key = None
31             self.session = auth
32         else:
33             self.node_id = self.key = self.session = None
34
35         self.server = safexmlrpc.ServerProxy(self.uri, self.cacert, self.timeout, allow_none = 1, **kwds)
36
37
38     def update_session(self, f="/usr/boot/plnode.txt"):
39         # try authenticatipopulate /etc.planetlab/session 
40         def plnode(key):
41             try:
42                 return [i[:-1].split('=') for i in open(f).readlines() if i.startswith(key)][0][1].strip('"')
43             except:
44                 return None
45
46         auth = (int(plnode("NODE_ID")), plnode("NODE_KEY"))
47         plc = PLCAPI(self.uri, self.cacert, auth, self.timeout)
48         open("/etc/planetlab/session", 'w').write(plc.GetSession().strip())
49         self.session = open("/etc/planetlab/session").read().strip()
50
51         
52     def check_authentication(self):
53         authstatus = False
54         if self.key or self.session:
55             try: 
56                 authstatus = self.AuthCheck()
57             except: 
58                 logger.log_exc()
59         return authstatus
60
61
62     def add_auth(self, function):
63         """
64         Returns a wrapper which adds an Auth struct as the first
65         argument when the function is called.
66         """
67
68         def canonicalize(args):
69             """
70             BootAuth canonicalization method. Parameter values are
71             collected, sorted, converted to strings, then hashed with
72             the node key.
73             """
74
75             values = []
76
77             for arg in args:
78                 if isinstance(arg, list) or isinstance(arg, tuple):
79                     # The old implementation did not recursively handle
80                     # lists of lists. But neither did the old API itself.
81                     values += canonicalize(arg)
82                 elif isinstance(arg, dict):
83                     # Yes, the comments in the old implementation are
84                     # misleading. Keys of dicts are not included in the
85                     # hash.
86                     values += canonicalize(arg.values())
87                 else:
88                     # We use unicode() instead of str().
89                     values.append(unicode(arg))
90
91             return values
92
93         def wrapper(*params):
94             """
95             Adds an Auth struct as the first argument when the
96             function is called.
97             """
98
99             if self.session is not None:
100                 # Use session authentication
101                 auth = {'AuthMethod': "session",
102                         'session': self.session}
103             else:
104                 # Yes, this is the "canonicalization" method used.
105                 args = canonicalize(params)
106                 args.sort()
107                 msg = "[" + "".join(args) + "]"
108
109                 # We encode in UTF-8 before calculating the HMAC, which is
110                 # an 8-bit algorithm.
111                 digest = hmac.new(self.key, msg.encode('utf-8'), sha).hexdigest()
112
113                 auth = {'AuthMethod': "hmac",
114                         'node_id': self.node_id,
115                         'value': digest}
116
117             # Automagically add auth struct to every call
118             params = (auth,) + params
119
120             return function(*params)
121
122         return wrapper
123
124     def __getattr__(self, methodname):
125         function = getattr(self.server, methodname)
126         return self.add_auth(function)