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