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