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