Change plugin API (GetSlivers()) argument order to avoid unnecessary PLCAPI dependenc...
[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         # just a simple call to check authentication
54         return self.AuthCheck(self)
55
56
57     def add_auth(self, function):
58         """
59         Returns a wrapper which adds an Auth struct as the first
60         argument when the function is called.
61         """
62
63         def canonicalize(args):
64             """
65             BootAuth canonicalization method. Parameter values are
66             collected, sorted, converted to strings, then hashed with
67             the node key.
68             """
69
70             values = []
71
72             for arg in args:
73                 if isinstance(arg, list) or isinstance(arg, tuple):
74                     # The old implementation did not recursively handle
75                     # lists of lists. But neither did the old API itself.
76                     values += canonicalize(arg)
77                 elif isinstance(arg, dict):
78                     # Yes, the comments in the old implementation are
79                     # misleading. Keys of dicts are not included in the
80                     # hash.
81                     values += canonicalize(arg.values())
82                 else:
83                     # We use unicode() instead of str().
84                     values.append(unicode(arg))
85
86             return values
87
88         def wrapper(*params):
89             """
90             Adds an Auth struct as the first argument when the
91             function is called.
92             """
93
94             if self.session is not None:
95                 # Use session authentication
96                 auth = {'AuthMethod': "session",
97                         'session': self.session}
98             else:
99                 # Yes, this is the "canonicalization" method used.
100                 args = canonicalize(params)
101                 args.sort()
102                 msg = "[" + "".join(args) + "]"
103
104                 # We encode in UTF-8 before calculating the HMAC, which is
105                 # an 8-bit algorithm.
106                 digest = hmac.new(self.key, msg.encode('utf-8'), sha).hexdigest()
107
108                 auth = {'AuthMethod': "hmac",
109                         'node_id': self.node_id,
110                         'value': digest}
111
112             # Automagically add auth struct to every call
113             params = (auth,) + params
114
115             return function(*params)
116
117         return wrapper
118
119     def __getattr__(self, methodname):
120         function = getattr(self.server, methodname)
121         return self.add_auth(function)