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