Moved some files around and merged from 1.0 branch:
[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'])
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'])
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, allow_none=True)
178         site_id = api.GetNodes (auth.auth, {"hostname": nodename}, ['site_id'])
179         if len(site_id) == 1:
180                 loginbase = api.GetSites (auth.auth, site_id[0], ["login_base"])
181                 return loginbase[0]['login_base']
182         else:
183                 print "Not nodes returned!!!!"
184
185 '''
186 Returns list of slices for a site.
187 '''
188 def slices(loginbase):
189         siteslices = []
190         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
191         sliceids = api.GetSites (auth.auth, {"login_base" : loginbase}, ["slice_ids"])[0]['slice_ids']
192         for slice in api.GetSlices(auth.auth, {"slice_id" : sliceids}, ["name"]):
193                 siteslices.append(slice['name'])
194         return siteslices
195
196 '''
197 Returns dict of PCU info of a given node.
198 '''
199 def getpcu(nodename):
200         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
201         anon = {'AuthMethod': "anonymous"}
202         nodeinfo = api.GetNodes(auth.auth, {"hostname": nodename}, ["pcu_ids", "ports"])[0]
203         if nodeinfo['pcu_ids']:
204                 sitepcu = api.GetPCUs(auth.auth, nodeinfo['pcu_ids'])[0]
205                 sitepcu[nodename] = nodeinfo["ports"][0]
206                 return sitepcu
207         else:
208                 logger.info("%s doesn't have PCU" % nodename)
209                 return False
210
211 def GetPCUs(filter=None, fields=None):
212         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
213         pcu_list = api.GetPCUs(auth.auth, filter, fields)
214         return pcu_list 
215
216 '''
217 Returns all site nodes for site id (loginbase).
218 '''
219 def getSiteNodes(loginbase, fields=None):
220         api = xmlrpclib.Server(auth.server, verbose=False)
221         nodelist = []
222         anon = {'AuthMethod': "anonymous"}
223         try:
224                 nodeids = api.GetSites(anon, {"login_base": loginbase}, fields)[0]['node_ids']
225                 for node in api.GetNodes(anon, {"node_id": nodeids}, ['hostname']):
226                         nodelist.append(node['hostname'])
227         except Exception, exc:
228                 logger.info("getSiteNodes:  %s" % exc)
229                 print "getSiteNodes:  %s" % exc
230         return nodelist
231
232
233 def getPersons(filter=None, fields=None):
234         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
235         persons = []
236         try:
237                 persons = api.GetPersons(auth.auth, filter, fields)
238         except Exception, exc:
239                 print "getPersons:  %s" % exc
240                 logger.info("getPersons:  %s" % exc)
241         return persons
242
243 def getSites(filter=None, fields=None):
244         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
245         sites = []
246         anon = {'AuthMethod': "anonymous"}
247         try:
248                 #sites = api.GetSites(anon, filter, fields)
249                 sites = api.GetSites(auth.auth, filter, fields)
250         except Exception, exc:
251                 traceback.print_exc()
252                 print "getSites:  %s" % exc
253                 logger.info("getSites:  %s" % exc)
254         return sites
255
256 def getSiteNodes2(loginbase):
257         api = xmlrpclib.Server(auth.server, verbose=False)
258         nodelist = []
259         anon = {'AuthMethod': "anonymous"}
260         try:
261                 nodeids = api.GetSites(anon, {"login_base": loginbase})[0]['node_ids']
262                 nodelist += getNodes({'node_id':nodeids})
263         except Exception, exc:
264                 logger.info("getSiteNodes2:  %s" % exc)
265         return nodelist
266
267 def getNodeNetworks(filter=None):
268         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
269         nodenetworks = api.GetNodeNetworks(auth.auth, filter, None)
270         return nodenetworks
271
272 def getNodes(filter=None, fields=None):
273         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
274         nodes = api.GetNodes(auth.auth, filter, fields) 
275                         #['boot_state', 'hostname', 
276                         #'site_id', 'date_created', 'node_id', 'version', 'nodenetwork_ids',
277                         #'last_updated', 'peer_node_id', 'ssh_rsa_key' ])
278         return nodes
279
280 '''
281 Sets boot state of a node.
282 '''
283 def nodeBootState(nodename, state):
284         api = xmlrpclib.Server(auth.server, verbose=False)
285         try:
286                 return api.UpdateNode(auth.auth, nodename, {'boot_state': state})
287         except Exception, exc:
288                 logger.info("nodeBootState:  %s" % exc)
289
290 def updateNodeKey(nodename, key):
291         api = xmlrpclib.Server(auth.server, verbose=False)
292         try:
293                 return api.UpdateNode(auth.auth, nodename, {'key': key})
294         except Exception, exc:
295                 logger.info("updateNodeKey:  %s" % exc)
296
297 '''
298 Sends Ping Of Death to node.
299 '''
300 def nodePOD(nodename):
301         api = xmlrpclib.Server(auth.server, verbose=False)
302         logger.info("Sending POD to %s" % nodename)
303         try:
304                 if not debug:
305                         return api.RebootNode(auth.auth, nodename)
306         except Exception, exc:
307                         logger.info("nodePOD:  %s" % exc)
308
309 '''
310 Freeze all site slices.
311 '''
312 def suspendSlices(nodename):
313         api = xmlrpclib.Server(auth.server, verbose=False)
314         for slice in slices(siteId(nodename)):
315                 logger.info("Suspending slice %s" % slice)
316                 try:
317                         if not debug:
318                                 api.AddSliceAttribute(auth.auth, slice, "enabled", "0")
319                 except Exception, exc:
320                         logger.info("suspendSlices:  %s" % exc)
321
322 def enableSlices(nodename):
323         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
324         for slice in slices(siteId(nodename)):
325                 logger.info("Enabling slices %s" % slice)
326                 try:
327                         if not debug:
328                                 slice_list = api.GetSlices(auth.auth, {'name': slice}, None)
329                                 if len(slice_list) == 0:
330                                         return
331                                 slice_id = slice_list[0]['slice_id']
332                                 l_attr = api.GetSliceAttributes(auth.auth, {'slice_id': slice_id}, None)
333                                 for attr in l_attr:
334                                         if "enabled" == attr['name'] and attr['value'] == "0":
335                                                 logger.info("Deleted enable=0 attribute from slice %s" % slice)
336                                                 api.DeleteSliceAttribute(auth.auth, attr['slice_attribute_id'])
337                 except Exception, exc:
338                         logger.info("enableSlices: %s" % exc)
339                         print "exception: %s" % exc
340
341 #I'm commenting this because this really should be a manual process.  
342 #'''
343 #Enable suspended site slices.
344 #'''
345 #def enableSlices(nodename, slicelist):
346 #       api = xmlrpclib.Server(auth.server, verbose=False)
347 #       for slice in  slices(siteId(nodename)):
348 #               logger.info("Suspending slice %s" % slice)
349 #               api.SliceAttributeAdd(auth.auth, slice, "plc_slice_state", {"state" : "suspended"})
350 #
351 def enableSliceCreation(nodename):
352         api = xmlrpclib.Server(auth.server, verbose=False, allow_none=True)
353         try:
354                 loginbase = siteId(nodename)
355                 logger.info("Enabling slice creation for site %s" % loginbase)
356                 if not debug:
357                         logger.info("\tcalling UpdateSite(%s, enabled=True)" % loginbase)
358                         api.UpdateSite(auth.auth, loginbase, {'enabled': True})
359         except Exception, exc:
360                 print "ERROR: enableSliceCreation:  %s" % exc
361                 logger.info("ERROR: enableSliceCreation:  %s" % exc)
362
363 '''
364 Removes ability to create slices. Returns previous max_slices
365 '''
366 def removeSliceCreation(nodename):
367         print "removeSliceCreation(%s)" % nodename
368         api = xmlrpclib.Server(auth.server, verbose=False)
369         try:
370                 loginbase = siteId(nodename)
371                 #numslices = api.GetSites(auth.auth, {"login_base": loginbase}, 
372                 #               ["max_slices"])[0]['max_slices']
373                 logger.info("Removing slice creation for site %s" % loginbase)
374                 if not debug:
375                         #api.UpdateSite(auth.auth, loginbase, {'max_slices': 0})
376                         api.UpdateSite(auth.auth, loginbase, {'enabled': False})
377         except Exception, exc:
378                 logger.info("removeSliceCreation:  %s" % exc)
379
380 '''
381 QED
382 '''
383 #def enableSliceCreation(nodename, maxslices):
384 #       api = xmlrpclib.Server(auth.server, verbose=False)
385 #       anon = {'AuthMethod': "anonymous"}
386 #       siteid = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
387 #       if len(siteid) == 1:
388 #               logger.info("Enabling slice creation for site %s" % siteId(nodename))
389 #               try:
390 #                       if not debug:
391 #                               api.AdmUpdateSite(auth.auth, siteid[0], {"max_slices" : maxslices})
392 #               except Exception, exc:
393 #                       logger.info("API:  %s" % exc)
394 #       else:
395 #               logger.debug("Cant find site for %s.  Cannot enable creation." % nodename)
396
397 def main():
398         logger.setLevel(logging.DEBUG)
399         ch = logging.StreamHandler()
400         ch.setLevel(logging.DEBUG)
401         formatter = logging.Formatter('logger - %(message)s')
402         ch.setFormatter(formatter)
403         logger.addHandler(ch)
404         #print getpcu("kupl2.ittc.ku.edu")
405         #print getpcu("planetlab1.cse.msu.edu")
406         #print getpcu("alice.cs.princeton.edu")
407         #print nodesDbg()
408         #nodeBootState("alice.cs.princeton.edu", "boot")
409         #freezeSite("alice.cs.princeton.edu")
410         print removeSliceCreation("alice.cs.princeton.edu")
411         #enableSliceCreation("alice.cs.princeton.edu", 1024)
412         #print getSiteNodes("princeton")
413         #print siteId("alice.cs.princeton.edu")
414         #print nodePOD("alice.cs.princeton.edu")
415         #print slices("princeton")
416
417 if __name__=="__main__":
418         main()