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