o Fixed removeSliceCreation and enableSliceCreation functions to work with
[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.8 2007/02/12 19:15:08 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, 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 suspendSlices(argv):
189         """Freeze all site slices."""
190
191         global api, 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.AddSliceAttribute(auth, slice, "plc_slice_state", "suspended")
204                 except Exception, exc:
205                         logger.info("suspendSlices:  %s" % exc)
206
207
208 def enableSlices(argv):
209         """Enable suspended site slices."""
210
211         global api, auth
212         if auth is None:
213                 printUsage("requires admin privs")
214                 sys.exit(1)
215
216         if argv[0].find(".") <> -1:
217                 slices = api.GetSlices(auth,[siteId(argv)])
218         else:
219                 gSlices = {'name':"%s_*"%argv[0]}
220                 slices = api.GetSlices(auth,gSlices)
221
222         for slice in slices:
223                 logger.info("unfreezing slice %s" % slice['name'])
224                 slice_attributes = api.GetSliceAttributes(auth,slice['slice_attribute_ids'])
225                 for slice_attribute in slice_attributes:
226                         if slice_attribute['name'] == "plc_slice_state":
227                                 api.DeleteSliceAttribute(auth, slice_attribute['slice_attribute_id'])
228
229 def _SetSliceMax(argv):
230         global api, auth
231         if auth is None:
232                 printUsage("requires admin privs")
233                 sys.exit(1)
234
235         name = argv[0]
236         val = int(argv[1])
237         if name.find(".") <> -1:
238                 site_ids = api.GetNodes(auth, [name], ['site_id'])
239                 if len(site_ids) == 1:
240                         site_id = [site_ids[0]['site_id']]
241                         loginbase = api.GetSites (auth, site_id, ["login_base"])
242                 else:
243                         printUsage("invalid hostname %s" % name)
244                         sys.exit(1)
245         else:
246                 site_ids = api.GetSites(auth, {'login_base': "%s" % name}, ['site_id'])
247                 if len(site_ids) == 1:
248                         siteid = site_ids[0]['site_id']
249                 loginbase = name
250
251         numslices = api.GetSites(auth, [siteid], ["max_slices"])[0]['max_slices']
252         try:
253                 api.UpdateSite(auth, siteid, {'max_slices': val})
254                 logger.info("_SetSliceMax:  %s max_slices was %d set to %d" % (loginbase,numslices,val))
255                 return numslices
256         except Exception, exc:
257                 logger.info("_SetSliceMax:  %s" % exc)
258
259 def removeSliceCreation(argv):
260         """Removes ability to create slices. Returns previous max_slices"""
261         argv.append(0)
262         _SetSliceMax(argv)
263
264 def enableSliceCreation(argv):
265         """QED"""
266         _SetSliceMax(argv)
267
268
269 USAGE = """
270 Usage: %s [-u user] [-p password] [-r role] CMD
271
272 Options:
273 -u      PLC account username
274 -p      PLC account password
275 -r      PLC account role
276 -h      This message
277 """ % sys.argv[0]
278
279 def printUsage(error = None):
280         global funclist
281         if error <> None:
282                 print "%s %s" %(sys.argv[0],error)
283         print USAGE
284         print "CMD:"
285         for name,function in funclist:
286                 print "%20s\t%20s" % (name, function.__doc__)
287         
288 def main():
289         global api, auth
290
291         auth = None
292         user = None
293         password = None
294         role = 'admin'
295
296         (opts, argv) = getopt.getopt(sys.argv[1:], "u:p:r:h")
297         if len(argv)==0:
298                 printUsage()
299                 sys.exit(1)
300
301         for (opt, optval) in opts:
302                 if opt == '-u':
303                         user = optval
304                 elif opt == '-p':
305                         password = optval
306                 elif opt == '-r':
307                         role = optval
308                 elif opt == '-h':
309                         print USAGE
310                         sys.exit(0)
311
312         if user <> None:
313                 if password is None:
314                         try:
315                                 password = getpass.getpass()
316                         except (EOFError, KeyboardInterrupt):
317                                 print( "" )
318                                 sys.exit(1)
319                 auth = {}
320                 auth['Username'] = user
321                 auth['AuthMethod'] = "password"
322                 auth['AuthString'] = password
323                 auth['Role'] = role
324
325         cmd = functbl.get(argv[0], None)
326         if cmd is None:
327                 printUsage()
328                 sys.exit(1)
329
330         logger.setLevel(logging.DEBUG)
331         ch = logging.StreamHandler()
332         ch.setLevel(logging.DEBUG)
333         formatter = logging.Formatter('logger - %(message)s')
334         ch.setFormatter(formatter)
335         logger.addHandler(ch)
336         result = cmd(argv[1:])
337         if result <> None:
338                 print result
339
340 funclist = (("nodesDbg",nodesDbg),
341             ("siteId", siteId),
342             ("slices", slices),
343             ("pcu", getpcu),
344             ("siteNodes", getSiteNodes),
345             ("nodeBootState", nodeBootState),
346             ("nodePOD", nodePOD),
347             ("freezeSlices", suspendSlices),
348             ("unfreezeSlices", enableSlices),
349             ("disableSliceCreation",removeSliceCreation),
350             ("enableSliceCreation", enableSliceCreation),
351             ("renewAllSlices", renewAllSlices))
352
353 functbl = {}
354 for f in funclist:
355         functbl[f[0]]=f[1]
356
357 if __name__=="__main__":
358         import reboot
359         main()