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