www/printbadnodes.py
[monitor.git] / grouprins.py
1 #!/usr/bin/python
2
3 # This script is used to manipulate the operational state of nodes in
4 # different node groups.  These are basically set operations on nodes via the
5 # PLC api.
6
7 # Take the ng name as an argument....
8 # optionally, 
9 #  * get a list of nodes in the given nodegroup.
10 #  * set some or all in the set to rins.
11 #  * restart them all.
12 #  * do something else to them all.
13
14
15 import plc
16 api = plc.getAuthAPI()
17
18 import traceback
19 import config
20 import util.file
21 from optparse import OptionParser
22
23 import const
24 from nodecommon import *
25 from nodequery import verify,query_to_dict,node_select
26 import database
27 from unified_model import *
28 import os
29
30 import time
31 import parser as parsermodule
32
33 from model import *
34 import bootman          # debug nodes
35 import reboot           # down nodes without pcu
36 import mailmonitor      # down nodes with pcu
37 from emailTxt import mailtxt
38 #reboot.verbose = 0
39 import sys
40
41 class Reboot(object):
42         def __init__(self, fbnode):
43                 self.fbnode = fbnode
44
45         def _send_pcunotice(self, host):
46                 args = {}
47                 args['hostname'] = host
48                 try:
49                         args['pcu_id'] = plc.getpcu(host)['pcu_id']
50                 except:
51                         args['pcu_id'] = host
52                         
53                 m = PersistMessage(host, mailtxt.pcudown_one[0] % args,
54                                                                  mailtxt.pcudown_one[1] % args, True, db='pcu_persistmessages')
55
56                 loginbase = plc.siteId(host)
57                 m.send([const.TECHEMAIL % loginbase])
58
59         def pcu(self, host):
60                 # TODO: It should be possible to diagnose the various conditions of
61                 #               the PCU here, and send different messages as appropriate.
62                 print "'%s'" % self.fbnode['pcu']
63                 if self.fbnode['pcu'] == "PCU" or "PCUOK" in self.fbnode['pcu']:
64                         self.action = "reboot.reboot('%s')" % host
65
66                         pflags = PersistFlags(host, 2*60*60*24, db='pcu_persistflags')
67                         pflags.resetRecentFlag('pcutried')
68                         if not pflags.getRecentFlag('pcutried'):
69                                 pflags.setRecentFlag('pcutried')
70                                 try:
71                                         print "CALLING REBOOT!!!"
72                                         ret = reboot.reboot(host)
73
74                                         pflags.save()
75                                         return ret
76
77                                 except Exception,e:
78                                         print traceback.print_exc(); print e
79
80                                         # NOTE: this failure could be an implementation issue on
81                                         #               our end.  So, extra notices are confusing...
82                                         # self._send_pcunotice(host) 
83
84                                         pflags.setRecentFlag('pcufailed')
85                                         pflags.save()
86                                         return False
87                         else:
88                                 # we've tried the pcu recently, but it didn't work,
89                                 # so did we send a message about it recently?
90                                 if not pflags.getRecentFlag('pcumessagesent'): 
91
92                                         self._send_pcunotice(host)
93
94                                         pflags.setRecentFlag('pcumessagesent')
95                                         pflags.save()
96                                         # NOTE: this will result in just one message sent at a time.
97                                         return True
98
99                                 else:
100                                         print "GetRecentFlag()"
101                                         return False
102                 else:
103                         print "NO PCUOK"
104                         self.action = "None"
105                         return False
106
107         def mail(self, host):
108
109                 # Reset every 4 weeks or so
110                 pflags = PersistFlags(host, 27*60*60*24, db='mail_persistflags')
111                 if not pflags.getRecentFlag('endrecord'):
112                         node_end_record(host)
113                         pflags.setRecentFlag('endrecord')
114                         pflags.save()
115
116                 # Then in either case, run mailmonitor.reboot()
117                 self.action = "mailmonitor.reboot('%s')" % host
118                 try:
119                         return mailmonitor.reboot(host)
120                 except Exception, e:
121                         print traceback.print_exc(); print e
122                         return False
123
124 class RebootDebug(Reboot):
125
126         def direct(self, host):
127                 self.action = "bootman.reboot('%s', config, None)" % host
128                 return bootman.reboot(host, config, None)
129         
130 class RebootBoot(Reboot):
131
132         def direct(self, host):
133                 self.action = "bootman.reboot('%s', config, 'reboot')" % host
134                 return bootman.reboot(host, config, 'reboot')
135
136 class RebootDown(Reboot):
137
138         def direct(self, host):
139                 self.action = "None"
140                 return False    # this always fails, since the node will be down.
141
142 def set_node_to_rins(host, fb):
143
144         node = api.GetNodes(host, ['boot_state', 'last_contact', 'last_updated', 'date_created'])
145         record = {'observation' : node[0], 
146                           'model' : 'USER_REQUEST', 
147                           'action' : 'api.UpdateNode(%s, {"boot_state" : "rins"})' % host, 
148                           'time' : time.time()}
149         l = Log(host, record)
150
151         ret = api.UpdateNode(host, {'boot_state' : 'rins'})
152         if ret:
153                 # it's nice to see the current status rather than the previous status on the console
154                 node = api.GetNodes(host)[0]
155                 print l
156                 print "%-2d" % (i-1), nodegroup_display(node, fb)
157                 return l
158         else:
159                 print "FAILED TO UPDATE NODE BOOT STATE : %s" % host
160                 return None
161
162
163 try:
164         rebootlog = database.dbLoad("rebootlog")
165 except:
166         rebootlog = LogRoll()
167
168 parser = parsermodule.getParser(['nodesets'])
169 parser.set_defaults( timewait=0,
170                                         skip=0,
171                                         rins=False,
172                                         reboot=False,
173                                         findbad=False,
174                                         force=False, 
175                                         nosetup=False, 
176                                         verbose=False, 
177                                         stopkey=None,
178                                         stopvalue=None,
179                                         quiet=False,
180                                         )
181
182 parser.add_option("", "--stopselect", dest="stopselect", metavar="", 
183                                         help="The select string that must evaluate to true for the node to be considered 'done'")
184 parser.add_option("", "--findbad", dest="findbad", action="store_true", 
185                                         help="Re-run findbad on the nodes we're going to check before acting.")
186 parser.add_option("", "--force", dest="force", action="store_true", 
187                                         help="Force action regardless of previous actions/logs.")
188 parser.add_option("", "--rins", dest="rins", action="store_true", 
189                                         help="Set the boot_state to 'rins' for all nodes.")
190 parser.add_option("", "--reboot", dest="reboot", action="store_true", 
191                                         help="Actively try to reboot the nodes, keeping a log of actions.")
192
193 parser.add_option("", "--verbose", dest="verbose", action="store_true", 
194                                         help="Extra debug output messages.")
195 parser.add_option("", "--nosetup", dest="nosetup", action="store_true", 
196                                         help="Do not perform the orginary setup phase.")
197 parser.add_option("", "--skip", dest="skip", 
198                                         help="Number of machines to skip on the input queue.")
199 parser.add_option("", "--timewait", dest="timewait", 
200                                         help="Minutes to wait between iterations of 10 nodes.")
201
202 parser = parsermodule.getParser(['defaults'], parser)
203 config = parsermodule.parse_args(parser)
204
205 # COLLECT nodegroups, nodes and node lists
206 if config.nodegroup:
207         ng = api.GetNodeGroups({'name' : config.nodegroup})
208         nodelist = api.GetNodes(ng[0]['node_ids'])
209         hostnames = [ n['hostname'] for n in nodelist ]
210
211 if config.node or config.nodelist:
212         if config.node: hostnames = [ config.node ] 
213         else: hostnames = config.getListFromFile(config.nodelist)
214
215 fb = database.dbLoad("findbad")
216
217 if config.nodeselect:
218         hostnames = node_select(config.nodeselect, fb['nodes'].keys(), fb)
219
220 if config.findbad:
221         # rerun findbad with the nodes in the given nodes.
222         file = "findbad.txt"
223         util.file.setFileFromList(file, hostnames)
224         os.system("./findbad.py --cachenodes --debug=0 --dbname=findbad --increment --nodelist %s" % file)
225
226 # commands:
227 i = 1
228 count = 1
229 for host in hostnames:
230
231         #if 'echo' in host or 'hptest-1' in host: continue
232         try:
233                 try:
234                         node = api.GetNodes(host)[0]
235                 except:
236                         print traceback.print_exc(); 
237                         print "FAILED GETNODES for host: %s" % host
238                         continue
239                         
240                 print "%-2d" % i, nodegroup_display(node, fb)
241                 i += 1
242                 if i-1 <= int(config.skip): continue
243
244                 if config.stopselect:
245                         dict_query = query_to_dict(config.stopselect)
246                         fbnode = fb['nodes'][host]['values']
247                         observed_state = get_current_state(fbnode)
248
249                         if verify(dict_query, fbnode) and observed_state != "dbg ":
250                                 # evaluates to true, therefore skip.
251                                 print "%s evaluates true for %s ; skipping..." % ( config.stopselect, host )
252                                 continue
253
254                 if config.stopkey and config.stopvalue:
255                         fbnode = fb['nodes'][host]['values']
256                         observed_state = get_current_state(fbnode)
257
258                         if config.stopkey in fbnode:
259                                 if config.stopvalue in fbnode[config.stopkey] and observed_state != "dbg ":
260                                         print "%s has stopvalue; skipping..." % host
261                                         continue
262                         else:
263                                 print "stopkey %s not in fbnode record for %s; skipping..." % (config.stopkey, host)
264                                 print fbnode
265                                 continue
266
267                 if not config.force and rebootlog.find(host, {'action' : ".*reboot"}, 60*60*2):
268                         print "recently rebooted %s.  skipping... " % host
269                         continue
270
271                 if config.reboot:
272
273                         fbnode = fb['nodes'][host]['values']
274                         observed_state = get_current_state(fbnode)
275
276                         if       observed_state == "dbg ":
277                                 o = RebootDebug(fbnode)
278
279                         elif observed_state == "boot" :
280                                 if config.rins:
281                                         l = set_node_to_rins(host, fb)
282                                         if l: rebootlog.add(l)
283
284                                 o = RebootBoot(fbnode)
285
286                         elif observed_state == "down":
287                                 if config.rins:
288                                         l = set_node_to_rins(host, fb)
289                                         if l: rebootlog.add(l)
290
291                                 o = RebootDown(fbnode)
292
293
294                         if o.direct(host):
295                                 record = {'observation' : "DIRECT_SUCCESS: %s" % observed_state, 
296                                                   'action' : o.action,
297                                                   'model' : "none",
298                                                   'time' : time.time()}
299                         elif o.pcu(host):
300                                 record = {'observation' : "PCU_SUCCESS: %s" % observed_state, 
301                                                   'action' : o.action,
302                                                   'model' : "none",
303                                                   'time' : time.time()}
304                         elif o.mail(host):
305                                 record = {'observation' : "MAIL_SUCCESS: %s" % observed_state, 
306                                                   'action' : o.action,
307                                                   'model' : "none",
308                                                   'time' : time.time()}
309                         else:
310                                 record = {'observation' : "REBOOT_FAILED: %s" %  observed_state,
311                                                   'action' : "log failure",
312                                                   'model' : "none",
313                                                   'time' : time.time()}
314
315                                 print "ALL METHODS OF RESTARTING %s FAILED" % host
316                                 args = {}
317                                 args['hostname'] = host
318                                 m = PersistMessage(host, "ALL METHODS FAILED for %(hostname)s" % args,
319                                                                                          "CANNOT CONTACT", False, db='suspect_persistmessages')
320                                 m.reset()
321                                 m.send(['monitor-list@lists.planet-lab.org'])
322
323                         l = Log(host, record)
324                         print l
325                         rebootlog.add(l)
326         except KeyboardInterrupt:
327                 print "Killed by interrupt"
328                 sys.exit(0)
329         except:
330                 print traceback.print_exc();
331                 print "Continuing..."
332
333         time.sleep(1)
334         if count % 10 == 0:
335                 print "Saving rebootlog"
336                 database.dbDump("rebootlog", rebootlog)
337                 wait_time = int(config.timewait)
338                 print "Sleeping %d minutes" % wait_time
339                 ti = 0
340                 print "Minutes slept: ",
341                 sys.stdout.flush()
342                 while ti < wait_time:
343                         print "%s" % ti,
344                         sys.stdout.flush()
345                         time.sleep(60)
346                         ti = ti+1
347
348         count = count + 1
349
350 print "Saving rebootlog"
351 database.dbDump("rebootlog", rebootlog)