updated so that plc.py can be used also nicely from the command line
[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.1 2006/11/14 19:27:09 faiyaza 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)
22 anon = None
23 auth = None
24
25 def nodesDbg(argv):
26         """Returns list of nodes in dbg as reported by PLC"""
27
28         global api, anon, auth
29         dbgNodes = []
30         allnodes = api.AnonAdmGetNodes(anon, [], ['hostname','boot_state'])
31         for node in allnodes:
32                 if node['boot_state'] == 'dbg': dbgNodes.append(node['hostname'])
33         logger.info("%s nodes in debug according to PLC." %len(dbgNodes))
34         return dbgNodes
35
36
37 def siteId(argv):
38         """Returns loginbase for given nodename"""
39
40         global api, anon, auth
41         nodename = argv[0]
42         site_id = api.AnonAdmQuerySite (anon, {"node_hostname": nodename})
43         if len(site_id) == 1:
44                 loginbase = api.AnonAdmGetSites (anon, 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 nodeBootState(argv):
107         """Sets boot state of a node."""
108
109         global api, anon, auth
110         if len(argv) <> 2:
111                 printUsage("not enough arguments")
112                 sys.exit(1)
113                 
114         nodename = argv[0]
115         state = argv[1]
116         
117         if auth is None:
118                 printUsage("requires admin privs")
119                 sys.exit(1)
120
121         node_id = api.AnonAdmQueryNode(anon, {'node_hostname' : nodename})
122         if len(node_id) == 1:
123                 logger.info("Setting node %s to %s" %(nodename, state))
124                 try:
125                         if not config.debug:
126                                 api.AdmUpdateNode(auth, node_id[0], {'boot_state': state})
127                 except Exception, exc:
128                         logger.info("nodeBootState:  %s" % exc)
129         else:
130                 logger.info("Cant find node %s to toggle boot state" % nodename)
131
132 def nodePOD(argv):
133         """Sends Ping Of Death to node."""
134
135         global api, anon, auth
136         nodename = argv[0]
137         if auth is None:
138                 printUsage("requires admin privs")
139                 sys.exit(1)
140
141         node_id = api.AnonAdmQueryNode(anon, {'node_hostname' : nodename})
142         if len(node_id) == 1:
143                 logger.info("Sending POD to %s" % nodename)
144                 try:
145                         if not config.debug:
146                                 api.AdmRebootNode(auth, node_id[0])
147                 except Exception, exc:
148                         logger.info("nodePOD:  %s" % exc)
149         else:
150                 logger.info("Cant find node %s to send POD." % nodename)
151
152 def suspendSlices(argv):
153         """Freeze all site slices."""
154
155         global api, anon, auth
156         if auth is None:
157                 printUsage("requires admin privs")
158                 sys.exit(1)
159
160         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
161         else: siteslices = slices(argv)
162
163         for slice in siteslices:
164                 logger.info("Suspending slice %s" % slice)
165                 try:
166                         if not config.debug:
167                                 api.SliceAttributeAdd(auth, slice, "plc_slice_state", 
168                                 {"state" : "suspended"})
169                 except Exception, exc:
170                         logger.info("suspendSlices:  %s" % exc)
171
172
173 def enableSlices(argv):
174         """Enable suspended site slices."""
175
176         global api, anon, auth
177         if auth is None:
178                 printUsage("requires admin privs")
179                 sys.exit(1)
180
181         api = xmlrpclib.Server(XMLRPC_SERVER, verbose=False)
182
183         if argv[0].find(".") <> -1: siteslices = slices([siteId(argv)])
184         else: siteslices = slices(argv)
185
186         for slice in siteslices:
187                 logger.info("unfreezing slice %s" % slice)
188                 api.SliceAttributeDelete(auth, slice, "plc_slice_state")
189
190
191 def removeSliceCreation(argv):
192         """Removes ability to create slices. Returns previous max_slices"""
193
194         global api, anon, auth
195         if auth is None:
196                 printUsage("requires admin privs")
197                 sys.exit(1)
198
199         name = argv[0]
200         if name.find(".") <> -1:
201                 siteid = api.AnonAdmQuerySite (anon, {"node_hostname": name})
202                 loginbase = siteId(name)
203         else:
204                 siteid = api.AnonAdmQuerySite (anon, {"site_loginbase": name})          
205                 loginbase = name
206
207         numslices = api.AdmGetSites(auth, siteid, ["max_slices"])[0]['max_slices']
208         if len(siteid) == 1:
209                 logger.info("Removing slice creation for site %s" % loginbase)
210                 try:
211                         if not config.debug:
212                                 api.AdmUpdateSite(auth, siteid[0], {'max_slices': 0})
213                         return numslices
214                 except Exception, exc:
215                         logger.info("removeSliceCreation:  %s" % exc)
216         else:
217                 logger.debug("Cant find site for %s.  Cannot revoke creation." % loginbase)
218
219 def enableSliceCreation(argv):
220         """QED"""
221
222         global api, anon, auth
223         if auth is None:
224                 printUsage("requires admin privs")
225                 sys.exit(1)
226
227         maxslices = int(argv[1])
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         if len(siteid) == 1:
237                 logger.info("Enabling slice creation for site %s" % loginbase)
238                 try:
239                         if not config.debug:
240                                 api.AdmUpdateSite(auth, siteid[0], {"max_slices" : maxslices})
241                 except Exception, exc:
242                         logger.info("API:  %s" % exc)
243         else:
244                 logger.debug("Cant find site for %s.  Cannot enable creation." % loginbase)
245
246
247
248 USAGE = """
249 Usage: %s [-u user] [-p password] [-r role] CMD
250
251 Options:
252 -u      PLC account username
253 -p      PLC account password
254 -r      PLC account role
255 -h      This message
256 """ % sys.argv[0]
257
258 def printUsage(error = None):
259         global funclist
260         if error <> None:
261                 print "%s %s" %(sys.argv[0],error)
262         print USAGE
263         print "CMD:"
264         for name,function in funclist:
265                 print "%20s\t%20s" % (name, function.__doc__)
266         
267 def main():
268         global api, auth, anon
269
270         anon = {"AuthMethod":"anonymous"}
271         auth = None
272         user = None
273         password = None
274         role = 'admin'
275
276         (opts, argv) = getopt.getopt(sys.argv[1:], "u:p:r:h")
277         if len(argv)==0:
278                 printUsage()
279                 sys.exit(1)
280
281         for (opt, optval) in opts:
282                 if opt == '-u':
283                         user = optval
284                 elif opt == '-p':
285                         password = optval
286                 elif opt == '-r':
287                         role = optval
288                 elif opt == '-h':
289                         print USAGE
290                         sys.exit(0)
291
292         if user <> None:
293                 if password is None:
294                         try:
295                                 password = getpass.getpass()
296                         except (EOFError, KeyboardInterrupt):
297                                 print( "" )
298                                 sys.exit(1)
299                 auth = {}
300                 auth['Username'] = user
301                 auth['AuthMethod'] = "password"
302                 auth['AuthString'] = password
303                 auth['Role'] = role
304
305         cmd = functbl.get(argv[0], None)
306         if cmd is None:
307                 printUsage()
308                 sys.exit(1)
309
310         logger.setLevel(logging.DEBUG)
311         ch = logging.StreamHandler()
312         ch.setLevel(logging.DEBUG)
313         formatter = logging.Formatter('logger - %(message)s')
314         ch.setFormatter(formatter)
315         logger.addHandler(ch)
316         result = cmd(argv[1:])
317         print result
318
319 funclist = (("nodesDbg",nodesDbg),
320             ("siteId", siteId),
321             ("slices", slices),
322             ("pcu", getpcu),
323             ("siteNodes", getSiteNodes),
324             ("nodeBootState", nodeBootState),
325             ("nodePOD", nodePOD),
326             ("freezeSlices", suspendSlices),
327             ("unfreezeSlices", enableSlices),
328             ("disableSliceCreation",removeSliceCreation),
329             ("enableSliceCreation", enableSliceCreation))
330
331 functbl = {}
332 for f in funclist:
333         functbl[f[0]]=f[1]
334
335 if __name__=="__main__":
336         import reboot
337         main()