2ddb06120ba02e2e5c8ba9325795dc875d6b6a8f
[monitor.git] / monitor / wrapper / plc.py
1 #
2 # plc.py
3 #
4 # Helper functions that minipulate the PLC api.
5
6 # Faiyaz Ahmed <faiyaza@cs.princeton.edu
7 #
8 # $Id: plc.py,v 1.18 2007/08/29 17:26:50 soltesz Exp $
9 #
10
11 import xml, xmlrpclib
12 import logging
13 import time
14 import traceback
15 from monitor import database
16
17 try:
18         import config
19         debug = config.debug
20 except:
21         debug = False
22 logger = logging.getLogger("monitor")
23         
24 class Auth:
25         def __init__(self, username=None, password=None):
26                 if username==None and password==None:
27                         self.auth = {'AuthMethod': "anonymous"}
28                 else:
29                         self.auth = {'Username' : username,
30                                 'AuthMethod' : 'password',
31                                                 'AuthString' : password}
32                         
33
34 # NOTE: this host is used by default when there are no auth files.
35 XMLRPC_SERVER="https://boot.planet-lab.org/PLCAPI/"
36
37 # NOTE: by default, use anonymous access, but if auth files are 
38 #       configured, use them, with their auth definitions.
39 auth = Auth()
40 try:
41         from monitor import config
42         auth.auth = {'Username' : config.API_AUTH_USER,
43                      'AuthMethod' : 'password',
44                                  'AuthString' : config.API_AUTH_PASSWORD}
45         auth.server = config.API_SERVER
46 except:
47         try:
48                 import auth
49                 auth.server = auth.plc
50         except:
51                 auth = Auth()
52                 auth.server = XMLRPC_SERVER
53
54 api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
55
56 class PLC:
57         def __init__(self, auth, url):
58                 self.auth = auth
59                 self.url = url
60                 self.api = xmlrpclib.Server(self.url, verbose=False, allow_none=True)
61
62         def __getattr__(self, name):
63                 method = getattr(self.api, name)
64                 if method is None:
65                         raise AssertionError("method does not exist")
66
67                 return lambda *params : method(self.auth, *params)
68
69         def __repr__(self):
70                 return self.api.__repr__()
71
72 class CachedPLC(PLC):
73
74         def _param_to_str(self, name, *params):
75                 fields = len(params)
76                 retstr = ""
77                 retstr += "%s-" % name
78                 for x in params:
79                         retstr += "%s-" % x
80                 return retstr[:-1]
81
82         def __getattr__(self, name):
83                 method = getattr(self.api, name)
84                 if method is None:
85                         raise AssertionError("method does not exist")
86
87                 def run_or_returncached(*params):
88                         cachename = self._param_to_str(name, *params)
89                         #print "cachename is %s" % cachename
90                         if 'Get' in name:
91                                 if not database.cachedRecently(cachename):
92                                         load_old_cache = False
93                                         try:
94                                                 values = method(self.auth, *params)
95                                         except:
96                                                 print "Call %s FAILED: Using old cached data" % cachename
97                                                 load_old_cache = True
98                                                 
99                                         if load_old_cache:
100                                                 values = database.dbLoad(cachename)
101                                         else:
102                                                 database.dbDump(cachename, values)
103                                                 
104                                         return values
105                                 else:
106                                         values = database.dbLoad(cachename)
107                                         return values
108                         else:
109                                 return method(self.auth, *params)
110
111                 return run_or_returncached
112
113
114 def getAPI(url):
115         return xmlrpclib.Server(url, verbose=False, allow_none=True)
116
117 def getAuthAPI():
118         return PLC(auth.auth, auth.server)
119
120 def getCachedAuthAPI():
121         return CachedPLC(auth.auth, auth.server)
122
123 def getTechEmails(loginbase):
124         """
125                 For the given site, return all user email addresses that have the 'tech' role.
126         """
127         api = getAuthAPI()
128         # get site details.
129         s = api.GetSites(loginbase)[0]
130         # get people at site
131         p = api.GetPersons(s['person_ids'])
132         # pull out those with the right role.
133         emails = [ person['email'] for person in filter(lambda x: 'tech' in x['roles'], p) ]
134         return emails
135
136 def getPIEmails(loginbase):
137         """
138                 For the given site, return all user email addresses that have the 'tech' role.
139         """
140         api = getAuthAPI()
141         # get site details.
142         s = api.GetSites(loginbase)[0]
143         # get people at site
144         p = api.GetPersons(s['person_ids'])
145         # pull out those with the right role.
146         emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], p) ]
147         return emails
148
149 def getSliceUserEmails(loginbase):
150         """
151                 For the given site, return all user email addresses that have the 'tech' role.
152         """
153         #api = getAuthAPI()
154         # get site details.
155         s = api.GetSites(loginbase)[0]
156         # get people at site
157         slices = api.GetSlices(s['slice_ids'])
158         people = []
159         for slice in slices:
160                 people += api.GetPersons(slice['person_ids'])
161         # pull out those with the right role.
162         emails = [ person['email'] for person in filter(lambda x: 'pi' in x['roles'], people) ]
163         unique_emails = [ x for x in set(emails) ]
164         return unique_emails
165
166 '''
167 Returns list of nodes in dbg as reported by PLC
168 '''
169 def nodesDbg():
170         dbgNodes = []
171         api = xmlrpclib.Server(auth.server, verbose=False)
172         anon = {'AuthMethod': "anonymous"}
173         for node in api.GetNodes(anon, {"boot_state":"dbg"},["hostname"]):
174                 dbgNodes.append(node['hostname'])
175         logger.info("%s nodes in debug according to PLC." %len(dbgNodes))
176         return dbgNodes
177
178
179 '''
180 Returns loginbase for given nodename
181 '''
182 def siteId(nodename):
183         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
184         site_id = api.GetNodes (auth.auth, {"hostname": nodename}, ['site_id'])
185         if len(site_id) == 1:
186                 loginbase = api.GetSites (auth.auth, site_id[0], ["login_base"])
187                 return loginbase[0]['login_base']
188         else:
189                 print "Not nodes returned!!!!"
190
191 '''
192 Returns list of slices for a site.
193 '''
194 def slices(loginbase):
195         siteslices = []
196         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
197         sliceids = api.GetSites (auth.auth, {"login_base" : loginbase}, ["slice_ids"])[0]['slice_ids']
198         for slice in api.GetSlices(auth.auth, {"slice_id" : sliceids}, ["name"]):
199                 siteslices.append(slice['name'])
200         return siteslices
201
202 '''
203 Returns dict of PCU info of a given node.
204 '''
205 def getpcu(nodename):
206         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
207         anon = {'AuthMethod': "anonymous"}
208         nodeinfo = api.GetNodes(auth.auth, {"hostname": nodename}, ["pcu_ids", "ports"])[0]
209         if nodeinfo['pcu_ids']:
210                 sitepcu = api.GetPCUs(auth.auth, nodeinfo['pcu_ids'])[0]
211                 sitepcu[nodename] = nodeinfo["ports"][0]
212                 return sitepcu
213         else:
214                 logger.info("%s doesn't have PCU" % nodename)
215                 return False
216
217 def GetPCUs(filter=None, fields=None):
218         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
219         pcu_list = api.GetPCUs(auth.auth, filter, fields)
220         return pcu_list 
221
222 '''
223 Returns all site nodes for site id (loginbase).
224 '''
225 def getSiteNodes(loginbase, fields=None):
226         api = xmlrpclib.Server(auth.server, verbose=False)
227         nodelist = []
228         anon = {'AuthMethod': "anonymous"}
229         try:
230                 nodeids = api.GetSites(anon, {"login_base": loginbase}, fields)[0]['node_ids']
231                 for node in api.GetNodes(anon, {"node_id": nodeids}, ['hostname']):
232                         nodelist.append(node['hostname'])
233         except Exception, exc:
234                 logger.info("getSiteNodes:  %s" % exc)
235                 print "getSiteNodes:  %s" % exc
236         return nodelist
237
238
239 def getPersons(filter=None, fields=None):
240         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
241         persons = []
242         try:
243                 persons = api.GetPersons(auth.auth, filter, fields)
244         except Exception, exc:
245                 print "getPersons:  %s" % exc
246                 logger.info("getPersons:  %s" % exc)
247         return persons
248
249 def getSites(filter=None, fields=None):
250         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
251         sites = []
252         anon = {'AuthMethod': "anonymous"}
253         try:
254                 #sites = api.GetSites(anon, filter, fields)
255                 sites = api.GetSites(auth.auth, filter, fields)
256         except Exception, exc:
257                 traceback.print_exc()
258                 print "getSites:  %s" % exc
259                 logger.info("getSites:  %s" % exc)
260         return sites
261
262 def getSiteNodes2(loginbase):
263         api = xmlrpclib.Server(auth.server, verbose=False)
264         nodelist = []
265         anon = {'AuthMethod': "anonymous"}
266         try:
267                 nodeids = api.GetSites(anon, {"login_base": loginbase})[0]['node_ids']
268                 nodelist += getNodes({'node_id':nodeids})
269         except Exception, exc:
270                 logger.info("getSiteNodes2:  %s" % exc)
271         return nodelist
272
273 def getNodeNetworks(filter=None):
274         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
275         nodenetworks = api.GetNodeNetworks(auth.auth, filter, None)
276         return nodenetworks
277
278 def getNodes(filter=None, fields=None):
279         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
280         nodes = api.GetNodes(auth.auth, filter, fields) 
281                         #['boot_state', 'hostname', 
282                         #'site_id', 'date_created', 'node_id', 'version', 'nodenetwork_ids',
283                         #'last_updated', 'peer_node_id', 'ssh_rsa_key' ])
284         return nodes
285
286 '''
287 Sets boot state of a node.
288 '''
289 def nodeBootState(nodename, state):
290         api = xmlrpclib.Server(auth.server, verbose=False)
291         try:
292                 return api.UpdateNode(auth.auth, nodename, {'boot_state': state})
293         except Exception, exc:
294                 logger.info("nodeBootState:  %s" % exc)
295
296 def updateNodeKey(nodename, key):
297         api = xmlrpclib.Server(auth.server, verbose=False)
298         try:
299                 return api.UpdateNode(auth.auth, nodename, {'key': key})
300         except Exception, exc:
301                 logger.info("updateNodeKey:  %s" % exc)
302
303 '''
304 Sends Ping Of Death to node.
305 '''
306 def nodePOD(nodename):
307         api = xmlrpclib.Server(auth.server, verbose=False)
308         logger.info("Sending POD to %s" % nodename)
309         try:
310                 if not debug:
311                         return api.RebootNode(auth.auth, nodename)
312         except Exception, exc:
313                         logger.info("nodePOD:  %s" % exc)
314
315 '''
316 Freeze all site slices.
317 '''
318 def suspendSlices(nodename):
319         api = xmlrpclib.Server(auth.server, verbose=False)
320         for slice in slices(siteId(nodename)):
321                 logger.info("Suspending slice %s" % slice)
322                 try:
323                         if not debug:
324                                 api.AddSliceAttribute(auth.auth, slice, "enabled", "0")
325                 except Exception, exc:
326                         logger.info("suspendSlices:  %s" % exc)
327
328 def enableSlices(nodename):
329         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
330         for slice in slices(siteId(nodename)):
331                 logger.info("Enabling slices %s" % slice)
332                 try:
333                         if not debug:
334                                 slice_list = api.GetSlices(auth.auth, {'name': slice}, None)
335                                 if len(slice_list) == 0:
336                                         return
337                                 slice_id = slice_list[0]['slice_id']
338                                 l_attr = api.GetSliceAttributes(auth.auth, {'slice_id': slice_id}, None)
339                                 for attr in l_attr:
340                                         if "enabled" == attr['name'] and attr['value'] == "0":
341                                                 logger.info("Deleted enable=0 attribute from slice %s" % slice)
342                                                 api.DeleteSliceAttribute(auth.auth, attr['slice_attribute_id'])
343                 except Exception, exc:
344                         logger.info("enableSlices: %s" % exc)
345                         print "exception: %s" % exc
346
347 #I'm commenting this because this really should be a manual process.  
348 #'''
349 #Enable suspended site slices.
350 #'''
351 #def enableSlices(nodename, slicelist):
352 #       api = xmlrpclib.Server(auth.server, verbose=False)
353 #       for slice in  slices(siteId(nodename)):
354 #               logger.info("Suspending slice %s" % slice)
355 #               api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
356 #
357 def enableSliceCreation(nodename):
358         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
359         try:
360                 loginbase = siteId(nodename)
361                 logger.info("Enabling slice creation for site %s" % loginbase)
362                 if not debug:
363                         logger.info("\tcalling UpdateSite(%s, enabled=True)" % loginbase)
364                         api.UpdateSite(auth.auth, loginbase, {'enabled': True})
365         except Exception, exc:
366                 print "ERROR: enableSliceCreation:  %s" % exc
367                 logger.info("ERROR: enableSliceCreation:  %s" % exc)
368
369 '''
370 Removes ability to create slices. Returns previous max_slices
371 '''
372 def removeSliceCreation(nodename):
373         print "removeSliceCreation(%s)" % nodename
374         api = xmlrpclib.Server(auth.server, verbose=False)
375         try:
376                 loginbase = siteId(nodename)
377                 #numslices = api.GetSites(auth.auth, {"login_base": loginbase}, 
378                 #               ["max_slices"])[0]['max_slices']
379                 logger.info("Removing slice creation for site %s" % loginbase)
380                 if not debug:
381                         #api.UpdateSite(auth.auth, loginbase, {'max_slices': 0})
382                         api.UpdateSite(auth.auth, loginbase, {'enabled': False})
383         except Exception, exc:
384                 logger.info("removeSliceCreation:  %s" % exc)
385
386 '''
387 QED
388 '''
389 #def enableSliceCreation(nodename, maxslices):
390 #       api = xmlrpclib.Server(auth.server, verbose=False)
391 #       anon = {'AuthMethod': "anonymous"}
392 #       siteid = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
393 #       if len(siteid) == 1:
394 #               logger.info("Enabling slice creation for site %s" % siteId(nodename))
395 #               try:
396 #                       if not debug:
397 #                               api.AdmUpdateSite(auth.auth, siteid[0], {"max_slices" : maxslices})
398 #               except Exception, exc:
399 #                       logger.info("API:  %s" % exc)
400 #       else:
401 #               logger.debug("Cant find site for %s.  Cannot enable creation." % nodename)
402
403 def main():
404         logger.setLevel(logging.DEBUG)
405         ch = logging.StreamHandler()
406         ch.setLevel(logging.DEBUG)
407         formatter = logging.Formatter('logger - %(message)s')
408         ch.setFormatter(formatter)
409         logger.addHandler(ch)
410         #print getpcu("kupl2.ittc.ku.edu")
411         #print getpcu("planetlab1.cse.msu.edu")
412         #print getpcu("alice.cs.princeton.edu")
413         #print nodesDbg()
414         #nodeBootState("alice.cs.princeton.edu", "boot")
415         #freezeSite("alice.cs.princeton.edu")
416         print removeSliceCreation("alice.cs.princeton.edu")
417         #enableSliceCreation("alice.cs.princeton.edu", 1024)
418         #print getSiteNodes("princeton")
419         #print siteId("alice.cs.princeton.edu")
420         #print nodePOD("alice.cs.princeton.edu")
421         #print slices("princeton")
422
423 if __name__=="__main__":
424         main()