updated a number of functions to use new API
[monitor.git] / plc.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: plc.py,v 1.6 2007/02/08 19:59:03 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://www2.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, anon, 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         def fast():
61                 slices = api.GetSlices (auth, {'name':"%s_*"%loginbase})
62                 return slices
63
64         def slow():
65                 results = []
66                 slice_ids = api.GetSites(auth,{'login_base':loginbase},['slice_ids'])
67                 if len(slice_ids)==1:
68                         slice_ids=slice_ids[0]
69                         slice_ids=slice_ids['slice_ids']
70                         slices = api.GetSlices(auth,slice_ids,['name'])
71                         results = map(lambda x: x['name'],slices)
72                 return results
73
74         return slow()
75
76 def getpcu(argv):
77         """Returns dict of PCU info of a given node."""
78
79         global api, anon, auth
80         nodename = argv[0].lower()
81         if auth is None:
82                 printUsage("requires admin privs")
83                 sys.exit(1)
84
85         pcus = api.GetNodes(auth, [nodename], ['pcu_ids'])
86         if len(pcus):
87                 pcus = map(lambda x: x['pcu_ids'],pcus)[0]
88                 nodepcus = api.GetPCUs(auth,pcus)
89         else:
90                 nodepcus = []
91         return nodepcus
92
93
94 def getSiteNodes(argv):
95         """Returns all site nodes for site id (loginbase)."""
96         global api, auth
97         if len(argv) < 1:
98                 printUsage("not enough arguments; please provide loginbase")
99                 sys.exit(1)
100
101         loginbase = argv[0]
102         nodelist = []
103         site_ids = api.GetSites(auth, {'login_base': "%s" % loginbase}, ['node_ids'])
104         if len(site_ids) == 1:
105                 node_ids = site_ids[0]['node_ids']
106                 nodes = api.GetNodes(auth,node_ids,['hostname'])
107                 nodelist = map(lambda x: x['hostname'], nodes)
108         elif len(site_ids) == 0:
109                 logger.info("getSiteNodes: can't find site %s" %loginbase)            
110         nodelist.sort()
111         return nodelist
112
113 def renewAllSlices (argv):
114         """Sets the expiration date of all slices to given date"""
115         global api, auth
116
117         newexp = argv[0]
118         # convert time string using fmt "%B %d %Y" to epoch integer
119         try:
120                 newexp = int(time.mktime(time.strptime(newexp,"%B %d %Y")))
121         except ValueError, e:
122                 errormsg = """Expecting date to be in Month Day Year
123   e.g., April 7 2007
124   new expiration date provided %s""" % newexp
125                 printUsage(errormsg)
126                 sys.exit(1)
127                 
128         slices = api.GetSlices(auth)
129         for slice in slices:
130                 name = slice['name']
131                 exp = int(slice['expires'])
132                 olddate = time.asctime(time.localtime(exp))
133                 slice_attributes = api.GetSliceAttributes(auth,slice['slice_attribute_ids'])
134                 for slice_attribute in slice_attributes:
135                         if slice_attribute['name'] == "enabled":
136                                 print "%s is suspended" % name
137                 continue
138                 if exp < newexp:
139                         newdate = time.asctime(time.localtime(newexp))
140                         ret = api.SliceRenew(auth,name,newexp)
141                         if ret == 0:
142                                 print "failed to renew %s" %name
143
144 def nodeBootState(argv):
145         """Sets boot state of a node."""
146
147         global api, anon, auth
148         if len(argv) < 1:
149                 printUsage("not enough arguments")
150                 sys.exit(1)
151                 
152         if len(argv) >=1:
153                 nodename = argv[0]
154         if len(argv) >=2:
155                 state = argv[1]
156
157         if auth is None:
158                 printUsage("requires admin privs")
159                 sys.exit(1)
160
161         node = api.GetNodes(auth, [nodename], ['node_id','boot_state'])
162         if len(node) == 1:
163                 node = node[0]
164                 try:
165                         logger.info("%s boot_state=%s" %(nodename, node['boot_state']))
166                         if len(argv) >=2 and not config.debug:
167                                 logger.info("Setting node %s boot_state=%s" %(nodename, state))
168                                 node_id = node['node_id']
169                                 api.UpdateNode(auth, node_id, {'boot_state': state})
170                 except Exception, exc:
171                         logger.info("nodeBootState:  %s" % exc)
172         else:
173                 logger.info("Cant find node %s to toggle boot state" % nodename)
174
175 def nodePOD(argv):
176         """Sends Ping Of Death to node."""
177
178         global api, auth
179         if len(argv) < 1:
180                 printUsage("not enough arguments")
181                 sys.exit(1)
182                 
183         nodename = argv[0]
184         if auth is None:
185                 printUsage("requires admin privs")
186                 sys.exit(1)
187
188         node = api.GetNodes(auth, [nodename], ['node_id'])
189         if len(node) == 1:
190                 node = node[0]
191                 logger.info("Sending POD to %s" % nodename)
192                 try:
193                         if not config.debug:
194                                 api.RebootNode(auth, node['node_id'])
195                 except Exception, exc:
196                         logger.info("nodePOD:  %s" % exc)
197         else:
198                 logger.info("Cant find node %s to send POD." % nodename)
199
200 def suspendSlices(argv):
201         """Freeze all site slices."""
202
203         global api, anon, auth
204         if auth is None:
205                 printUsage("requires admin privs")
206                 sys.exit(1)
207
208         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
209         else: siteslices = slices(argv)
210
211         for slice in siteslices:
212                 logger.info("Suspending slice %s" % slice)
213                 try:
214                         if not config.debug:
215                                 api.SliceAttributeAdd(auth, slice, "plc_slice_state", 
216                                 {"state" : "suspended"})
217                 except Exception, exc:
218                         logger.info("suspendSlices:  %s" % exc)
219
220
221 def enableSlices(argv):
222         """Enable suspended site slices."""
223
224         global api, anon, auth
225         if auth is None:
226                 printUsage("requires admin privs")
227                 sys.exit(1)
228
229         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
230         else: siteslices = slices(argv)
231
232         for slice in siteslices:
233                 logger.info("unfreezing slice %s" % slice)
234                 api.SliceAttributeDelete(auth, slice, "plc_slice_state")
235
236
237 def removeSliceCreation(argv):
238         """Removes ability to create slices. Returns previous max_slices"""
239
240         global api, anon, auth
241         if auth is None:
242                 printUsage("requires admin privs")
243                 sys.exit(1)
244
245         name = argv[0]
246         if name.find(".") <> -1:
247                 siteid = api.AnonAdmQuerySite (anon, {"node_hostname": name})
248                 loginbase = siteId(name)
249         else:
250                 siteid = api.AnonAdmQuerySite (anon, {"site_loginbase": name})          
251                 loginbase = name
252
253         numslices = api.AdmGetSites(auth, siteid, ["max_slices"])[0]['max_slices']
254         if len(siteid) == 1:
255                 logger.info("Removing slice creation for site %s" % loginbase)
256                 try:
257                         if not config.debug:
258                                 api.AdmUpdateSite(auth, siteid[0], {'max_slices': 0})
259                         return numslices
260                 except Exception, exc:
261                         logger.info("removeSliceCreation:  %s" % exc)
262         else:
263                 logger.debug("Cant find site for %s.  Cannot revoke creation." % loginbase)
264
265 def enableSliceCreation(argv):
266         """QED"""
267
268         global api, anon, auth
269         if auth is None:
270                 printUsage("requires admin privs")
271                 sys.exit(1)
272
273         if len(argv) < 2:
274                 printUsage("requires maxslice arg")
275                 sys.exit(1)
276
277         maxslices = int(argv[1])
278         name = argv[0]
279         if name.find(".") <> -1:
280                 siteid = api.AnonAdmQuerySite (anon, {"node_hostname": name})
281                 loginbase = siteId(name)
282         else:
283                 siteid = api.AnonAdmQuerySite (anon, {"site_loginbase": name})          
284                 loginbase = name
285
286         if len(siteid) == 1:
287                 logger.info("Enabling slice creation for site %s" % loginbase)
288                 try:
289                         if not config.debug:
290                                 api.AdmUpdateSite(auth, siteid[0], {"max_slices" : maxslices})
291                 except Exception, exc:
292                         logger.info("API:  %s" % exc)
293         else:
294                 logger.debug("Cant find site for %s.  Cannot enable creation." % loginbase)
295
296
297
298 USAGE = """
299 Usage: %s [-u user] [-p password] [-r role] CMD
300
301 Options:
302 -u      PLC account username
303 -p      PLC account password
304 -r      PLC account role
305 -h      This message
306 """ % sys.argv[0]
307
308 def printUsage(error = None):
309         global funclist
310         if error <> None:
311                 print "%s %s" %(sys.argv[0],error)
312         print USAGE
313         print "CMD:"
314         for name,function in funclist:
315                 print "%20s\t%20s" % (name, function.__doc__)
316         
317 def main():
318         global api, auth, anon
319
320         anon = {"AuthMethod":"anonymous"}
321         auth = None
322         user = None
323         password = None
324         role = 'admin'
325
326         (opts, argv) = getopt.getopt(sys.argv[1:], "u:p:r:h")
327         if len(argv)==0:
328                 printUsage()
329                 sys.exit(1)
330
331         for (opt, optval) in opts:
332                 if opt == '-u':
333                         user = optval
334                 elif opt == '-p':
335                         password = optval
336                 elif opt == '-r':
337                         role = optval
338                 elif opt == '-h':
339                         print USAGE
340                         sys.exit(0)
341
342         if user <> None:
343                 if password is None:
344                         try:
345                                 password = getpass.getpass()
346                         except (EOFError, KeyboardInterrupt):
347                                 print( "" )
348                                 sys.exit(1)
349                 auth = {}
350                 auth['Username'] = user
351                 auth['AuthMethod'] = "password"
352                 auth['AuthString'] = password
353                 auth['Role'] = role
354
355         cmd = functbl.get(argv[0], None)
356         if cmd is None:
357                 printUsage()
358                 sys.exit(1)
359
360         logger.setLevel(logging.DEBUG)
361         ch = logging.StreamHandler()
362         ch.setLevel(logging.DEBUG)
363         formatter = logging.Formatter('logger - %(message)s')
364         ch.setFormatter(formatter)
365         logger.addHandler(ch)
366         result = cmd(argv[1:])
367         if result <> None:
368                 print result
369
370 funclist = (("nodesDbg",nodesDbg),
371             ("siteId", siteId),
372             ("slices", slices),
373             ("pcu", getpcu),
374             ("siteNodes", getSiteNodes),
375             ("nodeBootState", nodeBootState),
376             ("nodePOD", nodePOD),
377             ("freezeSlices", suspendSlices),
378             ("unfreezeSlices", enableSlices),
379             ("disableSliceCreation",removeSliceCreation),
380             ("enableSliceCreation", enableSliceCreation),
381             ("renewAllSlices", renewAllSlices))
382
383 functbl = {}
384 for f in funclist:
385         functbl[f[0]]=f[1]
386
387 if __name__=="__main__":
388         import reboot
389         main()