changes for 3.0
[monitor.git] / plctool.py
1 #!/bin/env python
2 #
3 # Helper functions that minipulate the PLC api.
4
5 # Faiyaz Ahmed <faiyaza@cs.princeton.edu>
6 # Copyright (C) 2006, 2007 The Trustees of Princeton University
7 #
8 # $Id: plctool.py,v 1.2 2007/04/19 20:43:00 mef Exp $
9 #
10
11 from emailTxt import *
12 import xml, xmlrpclib
13 import logging
14 import time
15 import config
16 import getpass, getopt
17 import sys
18
19 logger = logging.getLogger("monitor")
20 XMLRPC_SERVER = 'https://www.planet-lab.org/PLCAPI/'
21 api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False, allow_none = True)
22 auth = None
23
24 def nodesDbg(argv):
25         """Returns list of nodes in dbg as reported by PLC"""
26
27         global api, auth
28         dbgNodes = []
29         allnodes = api.GetNodes(auth, None, ['hostname','boot_state'])
30         for node in allnodes:
31                 if node['boot_state'] == 'dbg': dbgNodes.append(node['hostname'])
32         logger.info("%d nodes in debug according to PLC." %len(dbgNodes))
33         return dbgNodes
34
35
36 def siteId(argv):
37         """Returns loginbase for given nodename"""
38
39         global api, auth
40         nodename = argv[0]
41         site_ids = api.GetNodes(auth, [nodename], ['site_id'])
42         if len(site_ids) == 1:
43                 site_id = [site_ids[0]['site_id']]
44                 loginbase = api.GetSites (auth, site_id, ["login_base"])
45                 return loginbase[0]['login_base']
46
47 def slices(argv):
48         """Returns list of slices for a site."""
49
50         global api, auth
51         if len(argv) < 1:
52                 printUsage("not enough arguments; please provide loginbase")
53                 sys.exit(1)
54
55         loginbase = argv[0]
56         if auth is None:
57                 printUsage("requires admin privs")
58                 sys.exit(1)
59
60         slices = api.GetSlices (auth, {'name':"%s_*"%loginbase},['name'])
61         slices = map(lambda x: x['name'],slices)
62         return slices
63
64 def getpcu(argv):
65         """Returns dict of PCU info of a given node."""
66
67         global api, auth
68         nodename = argv[0].lower()
69         if auth is None:
70                 printUsage("requires admin privs")
71                 sys.exit(1)
72
73         pcus = api.GetNodes(auth, [nodename], ['pcu_ids'])
74         if len(pcus):
75                 pcus = map(lambda x: x['pcu_ids'],pcus)[0]
76                 nodepcus = api.GetPCUs(auth,pcus)
77         else:
78                 nodepcus = []
79         return nodepcus
80
81
82 def getSiteNodes(argv):
83         """Returns all site nodes for site id (loginbase)."""
84         global api, auth
85         if len(argv) < 1:
86                 printUsage("not enough arguments; please provide loginbase")
87                 sys.exit(1)
88
89         loginbase = argv[0]
90         nodelist = []
91         site_ids = api.GetSites(auth, {'login_base': "%s" % loginbase}, ['node_ids'])
92         if len(site_ids) == 1:
93                 node_ids = site_ids[0]['node_ids']
94                 nodes = api.GetNodes(auth,node_ids,['hostname'])
95                 nodelist = map(lambda x: x['hostname'], nodes)
96         elif len(site_ids) == 0:
97                 logger.info("getSiteNodes: can't find site %s" %loginbase)            
98         nodelist.sort()
99         return nodelist
100
101 def renewAllSlices (argv):
102         """Sets the expiration date of all slices to given date"""
103         global api, auth
104
105         newexp = argv[0]
106         # convert time string using fmt "%B %d %Y" to epoch integer
107         try:
108                 newexp = int(time.mktime(time.strptime(newexp,"%B %d %Y")))
109         except ValueError, e:
110                 errormsg = """Expecting date to be in Month Day Year
111   e.g., April 7 2007
112   new expiration date provided %s""" % newexp
113                 printUsage(errormsg)
114                 sys.exit(1)
115                 
116         slices = api.GetSlices(auth)
117         for slice in slices:
118                 name = slice['name']
119                 exp = int(slice['expires'])
120                 olddate = time.asctime(time.localtime(exp))
121                 slice_attributes = api.GetSliceAttributes(auth,slice['slice_attribute_ids'])
122                 for slice_attribute in slice_attributes:
123                         if slice_attribute['name'] == "enabled":
124                                 print "%s is suspended" % name
125                 if exp < newexp:
126                         newdate = time.asctime(time.localtime(newexp))
127                         ret = api.SliceRenew(auth,name,newexp)
128                         if ret == 0:
129                                 print "failed to renew %s" %name
130
131 def nodeBootState(argv):
132         """Sets boot state of a node."""
133
134         global api, auth
135         if len(argv) < 1:
136                 printUsage("not enough arguments")
137                 sys.exit(1)
138                 
139         if len(argv) >=1:
140                 nodename = argv[0]
141         if len(argv) >=2:
142                 state = argv[1]
143
144         if auth is None:
145                 printUsage("requires admin privs")
146                 sys.exit(1)
147
148         node = api.GetNodes(auth, [nodename], ['node_id','boot_state'])
149         if len(node) == 1:
150                 node = node[0]
151                 try:
152                         logger.info("%s boot_state=%s" %(nodename, node['boot_state']))
153                         if len(argv) >=2 and not config.debug:
154                                 logger.info("Setting node %s boot_state=%s" %(nodename, state))
155                                 node_id = node['node_id']
156                                 api.UpdateNode(auth, node_id, {'boot_state': state})
157                 except Exception, exc:
158                         logger.info("nodeBootState:  %s" % exc)
159         else:
160                 logger.info("Cant find node %s to toggle boot state" % nodename)
161
162
163 def nodePOD(argv):
164         """Sends Ping Of Death to node."""
165
166         global api, auth
167         if len(argv) < 1:
168                 printUsage("not enough arguments")
169                 sys.exit(1)
170                 
171         nodename = argv[0]
172         if auth is None:
173                 printUsage("requires admin privs")
174                 sys.exit(1)
175
176         node = api.GetNodes(auth, [nodename], ['node_id'])
177         if len(node) == 1:
178                 node = node[0]
179                 logger.info("Sending POD to %s" % nodename)
180                 try:
181                         if not config.debug:
182                                 api.RebootNode(auth, node['node_id'])
183                 except Exception, exc:
184                         logger.info("nodePOD:  %s" % exc)
185         else:
186                 logger.info("Cant find node %s to send POD." % nodename)
187
188 def suspendSlice(argv):
189         """Freeze specific slice."""
190         global api, auth
191         if auth is None:
192                 printUsage("requires admin privs")
193                 sys.exit(1)
194
195         slice = argv[0]
196         logger.info("Suspending slice %s" % slice)
197         try:
198                 if not config.debug:
199                         api.AddSliceAttribute(auth, slice, "enabled", "0")
200         except Exception, exc:
201                 logger.info("suspendSlices:  %s" % exc)
202
203 def suspendSlices(argv):
204         """Freeze all site slices."""
205         global api, auth
206         if auth is None:
207                 printUsage("requires admin privs")
208                 sys.exit(1)
209
210         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
211         else: siteslices = slices(argv)
212
213         for slice in siteslices:
214                 suspendSlice([slice])
215
216 def __enableSlice(slice):
217         logger.info("unfreezing slice %s" % slice['name'])
218         slice_attributes = api.GetSliceAttributes(auth,slice['slice_attribute_ids'])
219         for slice_attribute in slice_attributes:
220                 if slice_attribute['name'] == "enabled":
221                         api.DeleteSliceAttribute(auth, slice_attribute['slice_attribute_id'])
222         
223 def enableSlice(arg):
224         """Enable suspended slice."""
225         global api, auth
226         if auth is None:
227                 printUsage("requires admin privs")
228                 sys.exit(1)
229
230         slicename = arg[0]
231         gSlices = {'name':slicename}
232         slice = api.GetSlices(auth,gSlices)
233         if len(slice) == 1:
234                 __enableSlice(slice[0])
235         else:
236                 logger.info("slice %s not found" % slicename)
237
238 def enableSlices(argv):
239         """Enable suspended site slices."""
240
241         global api, auth
242         if auth is None:
243                 printUsage("requires admin privs")
244                 sys.exit(1)
245
246         if argv[0].find(".") <> -1:
247                 slices = api.GetSlices(auth,[siteId(argv)])
248         else:
249                 gSlices = {'name':"%s_*"%argv[0]}
250                 slices = api.GetSlices(auth,gSlices)
251
252         for slice in slices:
253                 __enableSlice(slice)
254
255 def setSliceMax(argv):
256         """Set max_slices for Slice. Returns previous max_slices"""
257         global api, auth
258         if auth is None:
259                 printUsage("requires admin privs")
260                 sys.exit(1)
261
262         name = argv[0]
263         val = int(argv[1])
264         if name.find(".") <> -1:
265                 site_ids = api.GetNodes(auth, [name], ['site_id'])
266                 if len(site_ids) == 1:
267                         site_id = [site_ids[0]['site_id']]
268                         loginbase = api.GetSites (auth, site_id, ["login_base"])
269                 else:
270                         printUsage("invalid hostname %s" % name)
271                         sys.exit(1)
272         else:
273                 site_ids = api.GetSites(auth, {'login_base': "%s" % name}, ['site_id'])
274                 if len(site_ids) == 1:
275                         siteid = site_ids[0]['site_id']
276                 loginbase = name
277
278         numslices = api.GetSites(auth, [siteid], ["max_slices"])[0]['max_slices']
279         try:
280                 api.UpdateSite(auth, siteid, {'max_slices': val})
281                 logger.info("_SetSliceMax:  %s max_slices was %d set to %d" % (loginbase,numslices,val))
282                 return numslices
283         except Exception, exc:
284                 logger.info("_SetSliceMax:  %s" % exc)
285
286
287 def authCheck(arg):
288         """Enable suspended slice."""
289         global api, auth
290         if auth is None:
291                 printUsage("requires admin privs")
292                 sys.exit(1)
293
294         if len(arg) != 2:
295                 printUsage("incorrect arguments")
296                 sys.exit(1)
297         user= arg[0]
298         pwd = arg[1]
299         
300         check = {}
301         check['Username'] = user
302         check['AuthMethod'] = "password"
303         check['AuthString'] = pwd
304         for role in ['user','tech','pi','admin']:
305                 check['Role'] = role
306                 res = api.AdmAuthCheck(check)
307                 print "%s -> %s %d" % (user,role,res)
308
309
310 def cleanSlices(arg):
311         """Remove all disabled/deleted users from all slices."""
312         disabledUsers = {'enabled':False}
313         persons = api.GetPersons(auth,disabledUsers,['enabled','slice_ids','email','person_id'])
314         for person in persons:
315                 assert (person['enabled']==False)
316                 person_id = person['person_id']
317                 if len(person['slice_ids'])>0:
318                         for slice_id in person['slice_ids']:
319                                 print "deleting slice %d from %s" % (slice_id,person['email'])
320                                 api.DeletePersonFromSlice(auth,person_id,slice_id)
321
322
323
324 USAGE = """
325 Usage: %s [-u user] [-p password] [-r role] CMD
326
327 Options:
328 -u      PLC account username
329 -p      PLC account password
330 -r      PLC account role
331 -h      This message
332 """ % sys.argv[0]
333
334 def printUsage(error = None):
335         global funclist
336         if error <> None:
337                 print "%s %s" %(sys.argv[0],error)
338         print USAGE
339         print "CMD:"
340         for name,function in funclist:
341                 print "%20s\t%20s" % (name, function.__doc__)
342         
343 def main():
344         global api, auth
345
346         auth = None
347         user = None
348         password = None
349         role = 'admin'
350
351         (opts, argv) = getopt.getopt(sys.argv[1:], "u:p:r:h")
352         if len(argv)==0:
353                 printUsage()
354                 sys.exit(1)
355
356         for (opt, optval) in opts:
357                 if opt == '-u':
358                         user = optval
359                 elif opt == '-p':
360                         password = optval
361                 elif opt == '-r':
362                         role = optval
363                 elif opt == '-h':
364                         print USAGE
365                         sys.exit(0)
366
367         if user <> None:
368                 if password is None:
369                         try:
370                                 password = getpass.getpass()
371                         except (EOFError, KeyboardInterrupt):
372                                 print( "" )
373                                 sys.exit(1)
374                 auth = {}
375                 auth['Username'] = user
376                 auth['AuthMethod'] = "password"
377                 auth['AuthString'] = password
378                 auth['Role'] = role
379
380         cmd = functbl.get(argv[0], None)
381         if cmd is None:
382                 printUsage()
383                 sys.exit(1)
384
385         logger.setLevel(logging.DEBUG)
386         ch = logging.StreamHandler()
387         ch.setLevel(logging.DEBUG)
388         formatter = logging.Formatter('logger - %(message)s')
389         ch.setFormatter(formatter)
390         logger.addHandler(ch)
391         result = cmd(argv[1:])
392         if result <> None:
393                 if argv[0] == "nodesDbg":
394                         for n in result:
395                                 print n
396                 else:
397                         print result
398
399 funclist = (("nodesDbg",nodesDbg),
400             ("siteId", siteId),
401             ("slices", slices),
402             ("pcu", getpcu),
403             ("siteNodes", getSiteNodes),
404             ("nodeBootState", nodeBootState),
405             ("nodePOD", nodePOD),
406             ("freezeSlice", suspendSlice),
407             ("unfreezeSlice", enableSlice),
408             ("freezeSlices", suspendSlices),
409             ("unfreezeSlices", enableSlices),
410             ("setSliceMax", setSliceMax),
411             ("authCheck", authCheck),
412             ("cleanSlices", cleanSlices),
413             ("renewAllSlices", renewAllSlices))
414
415 functbl = {}
416 for f in funclist:
417         functbl[f[0]]=f[1]
418
419 if __name__=="__main__":
420         import reboot
421         main()