o Fixed slices() function 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.7 2007/02/08 22:43:11 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         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, anon, 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                 continue
126                 if exp < newexp:
127                         newdate = time.asctime(time.localtime(newexp))
128                         ret = api.SliceRenew(auth,name,newexp)
129                         if ret == 0:
130                                 print "failed to renew %s" %name
131
132 def nodeBootState(argv):
133         """Sets boot state of a node."""
134
135         global api, anon, auth
136         if len(argv) < 1:
137                 printUsage("not enough arguments")
138                 sys.exit(1)
139                 
140         if len(argv) >=1:
141                 nodename = argv[0]
142         if len(argv) >=2:
143                 state = argv[1]
144
145         if auth is None:
146                 printUsage("requires admin privs")
147                 sys.exit(1)
148
149         node = api.GetNodes(auth, [nodename], ['node_id','boot_state'])
150         if len(node) == 1:
151                 node = node[0]
152                 try:
153                         logger.info("%s boot_state=%s" %(nodename, node['boot_state']))
154                         if len(argv) >=2 and not config.debug:
155                                 logger.info("Setting node %s boot_state=%s" %(nodename, state))
156                                 node_id = node['node_id']
157                                 api.UpdateNode(auth, node_id, {'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, 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 suspendSlices(argv):
189         """Freeze all site slices."""
190
191         global api, anon, auth
192         if auth is None:
193                 printUsage("requires admin privs")
194                 sys.exit(1)
195
196         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
197         else: siteslices = slices(argv)
198
199         for slice in siteslices:
200                 logger.info("Suspending slice %s" % slice)
201                 try:
202                         if not config.debug:
203                                 api.SliceAttributeAdd(auth, slice, "plc_slice_state", 
204                                 {"state" : "suspended"})
205                 except Exception, exc:
206                         logger.info("suspendSlices:  %s" % exc)
207
208
209 def enableSlices(argv):
210         """Enable suspended site slices."""
211
212         global api, anon, auth
213         if auth is None:
214                 printUsage("requires admin privs")
215                 sys.exit(1)
216
217         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
218         else: siteslices = slices(argv)
219
220         for slice in siteslices:
221                 logger.info("unfreezing slice %s" % slice)
222                 api.SliceAttributeDelete(auth, slice, "plc_slice_state")
223
224
225 def removeSliceCreation(argv):
226         """Removes ability to create slices. Returns previous max_slices"""
227
228         global api, anon, auth
229         if auth is None:
230                 printUsage("requires admin privs")
231                 sys.exit(1)
232
233         name = argv[0]
234         if name.find(".") <> -1:
235                 siteid = api.AnonAdmQuerySite (anon, {"node_hostname": name})
236                 loginbase = siteId(name)
237         else:
238                 siteid = api.AnonAdmQuerySite (anon, {"site_loginbase": name})          
239                 loginbase = name
240
241         numslices = api.AdmGetSites(auth, siteid, ["max_slices"])[0]['max_slices']
242         if len(siteid) == 1:
243                 logger.info("Removing slice creation for site %s" % loginbase)
244                 try:
245                         if not config.debug:
246                                 api.AdmUpdateSite(auth, siteid[0], {'max_slices': 0})
247                         return numslices
248                 except Exception, exc:
249                         logger.info("removeSliceCreation:  %s" % exc)
250         else:
251                 logger.debug("Cant find site for %s.  Cannot revoke creation." % loginbase)
252
253 def enableSliceCreation(argv):
254         """QED"""
255
256         global api, anon, auth
257         if auth is None:
258                 printUsage("requires admin privs")
259                 sys.exit(1)
260
261         if len(argv) < 2:
262                 printUsage("requires maxslice arg")
263                 sys.exit(1)
264
265         maxslices = int(argv[1])
266         name = argv[0]
267         if name.find(".") <> -1:
268                 siteid = api.AnonAdmQuerySite (anon, {"node_hostname": name})
269                 loginbase = siteId(name)
270         else:
271                 siteid = api.AnonAdmQuerySite (anon, {"site_loginbase": name})          
272                 loginbase = name
273
274         if len(siteid) == 1:
275                 logger.info("Enabling slice creation for site %s" % loginbase)
276                 try:
277                         if not config.debug:
278                                 api.AdmUpdateSite(auth, siteid[0], {"max_slices" : maxslices})
279                 except Exception, exc:
280                         logger.info("API:  %s" % exc)
281         else:
282                 logger.debug("Cant find site for %s.  Cannot enable creation." % loginbase)
283
284
285
286 USAGE = """
287 Usage: %s [-u user] [-p password] [-r role] CMD
288
289 Options:
290 -u      PLC account username
291 -p      PLC account password
292 -r      PLC account role
293 -h      This message
294 """ % sys.argv[0]
295
296 def printUsage(error = None):
297         global funclist
298         if error <> None:
299                 print "%s %s" %(sys.argv[0],error)
300         print USAGE
301         print "CMD:"
302         for name,function in funclist:
303                 print "%20s\t%20s" % (name, function.__doc__)
304         
305 def main():
306         global api, auth, anon
307
308         anon = {"AuthMethod":"anonymous"}
309         auth = None
310         user = None
311         password = None
312         role = 'admin'
313
314         (opts, argv) = getopt.getopt(sys.argv[1:], "u:p:r:h")
315         if len(argv)==0:
316                 printUsage()
317                 sys.exit(1)
318
319         for (opt, optval) in opts:
320                 if opt == '-u':
321                         user = optval
322                 elif opt == '-p':
323                         password = optval
324                 elif opt == '-r':
325                         role = optval
326                 elif opt == '-h':
327                         print USAGE
328                         sys.exit(0)
329
330         if user <> None:
331                 if password is None:
332                         try:
333                                 password = getpass.getpass()
334                         except (EOFError, KeyboardInterrupt):
335                                 print( "" )
336                                 sys.exit(1)
337                 auth = {}
338                 auth['Username'] = user
339                 auth['AuthMethod'] = "password"
340                 auth['AuthString'] = password
341                 auth['Role'] = role
342
343         cmd = functbl.get(argv[0], None)
344         if cmd is None:
345                 printUsage()
346                 sys.exit(1)
347
348         logger.setLevel(logging.DEBUG)
349         ch = logging.StreamHandler()
350         ch.setLevel(logging.DEBUG)
351         formatter = logging.Formatter('logger - %(message)s')
352         ch.setFormatter(formatter)
353         logger.addHandler(ch)
354         result = cmd(argv[1:])
355         if result <> None:
356                 print result
357
358 funclist = (("nodesDbg",nodesDbg),
359             ("siteId", siteId),
360             ("slices", slices),
361             ("pcu", getpcu),
362             ("siteNodes", getSiteNodes),
363             ("nodeBootState", nodeBootState),
364             ("nodePOD", nodePOD),
365             ("freezeSlices", suspendSlices),
366             ("unfreezeSlices", enableSlices),
367             ("disableSliceCreation",removeSliceCreation),
368             ("enableSliceCreation", enableSliceCreation),
369             ("renewAllSlices", renewAllSlices))
370
371 functbl = {}
372 for f in funclist:
373         functbl[f[0]]=f[1]
374
375 if __name__=="__main__":
376         import reboot
377         main()