Many small updates and fixes:
[monitor.git] / commands / nodebad.py
1 #!/usr/bin/python
2
3 import os
4 import sys
5 import string
6 import time
7 from datetime import datetime,timedelta
8
9
10 from monitor.common import *
11 from monitor.query import verify,query_to_dict,node_select
12
13 from monitor import config
14 from monitor.wrapper import plc,plccache
15 from monitor.const import MINUP
16 from monitor.database.info.model import  FindbadNodeRecord, HistoryNodeRecord
17 from monitor.database.dborm import  mon_session as session
18
19 from monitor.model import *
20
21 api = plc.getAuthAPI()
22
23 round = 1
24 count = 0
25 def main():
26     main2(config)
27
28 def main2(config):
29
30     l_plcnodes = plccache.l_nodes
31     l_nodes = get_nodeset(config)
32     
33     checkAndRecordState(l_nodes, l_plcnodes)
34
35 # Node states:
36
37 def check_node_state(rec, node):
38
39     node_state = rec.observed_status
40     if rec.plc_node_stats:
41         print rec.plc_node_stats
42         boot_state = rec.plc_node_stats['boot_state']
43         run_level = rec.plc_node_stats['run_level']
44         last_contact = rec.plc_node_stats['last_contact']
45         node.plc_nodeid = rec.plc_node_stats['node_id']
46     else:
47         boot_state = "unknown"
48         last_contact = None
49
50     if boot_state == 'disable': boot_state = 'disabled'
51     if boot_state == 'diag' or boot_state == 'diagnose': boot_state = 'safeboot'
52
53     if rec.plc_node_stats and len(rec.plc_node_stats['pcu_ids']) > 0:
54         node.haspcu = True
55     else:
56         node.haspcu = False
57
58     node.firewall = rec.firewall
59     node.plc_siteid = rec.plc_node_stats['site_id']
60
61     # NOTE: 'DOWN' and 'DEBUG'  are temporary states, so only need
62     #             'translations' into the node.status state
63     #        'BOOT' is a permanent state, but we want it to have a bit of
64     #            hysteresis (less than 0.5 days)
65     #################################################################
66     # "Initialize" the findbad states into nodebad status if they are not already set
67
68     if node_state == 'DOWN':
69         if boot_state == 'disabled' and changed_lessthan(node.last_changed, 60) and \
70             node.status != 'disabled':
71             # NOTE: if changed less than 2 months, then we can allow this. 
72             # otherwise, apply 'down' status after greater than 2 months (below).
73
74             print "changed status from %s to %s" % (node.status, boot_state)
75             node.status = boot_state
76             node.last_changed = datetime.now()
77
78         if node.status not in ['offline', 'down', 'disabled']:
79             print "changed status from %s to offline" % node.status
80             node.status = 'offline'
81             node.last_changed = datetime.now()
82
83     if node_state == 'DEBUG':
84         if boot_state != 'disabled' and boot_state != 'safeboot':
85             print "changed status from %s to failboot" % (node.status)
86             current_status = "failboot"
87         else:
88             print "changed status from %s to %s" % (node.status, boot_state)
89             current_status = boot_state
90
91         if current_status != node.status and \
92             current_status in ['failboot', 'disabled', 'safeboot']:
93
94             node.status = current_status
95             node.last_changed = datetime.now()
96
97     if node_state == 'BOOT' and node.status != 'online' and node.status != 'good':
98         print "changed status from %s to online" % node.status
99         node.status = 'online'
100         node.last_changed = datetime.now()
101
102     #################################################################
103     # Switch temporary hystersis states into their 'firm' states.
104     #      online -> good        after half a day
105     #      offline -> down        after two days
106     #      failboot -> down  after 30 days
107     #      safeboot -> failboot after 60 days
108     #      disabled -> down        after 60 days
109
110     if node.status == 'online' and changed_greaterthan(node.last_changed, 0.5):
111         print "changed status from %s to good" % node.status
112         node.status = 'good'
113         # NOTE: do not reset last_changed, or you lose how long it's been up.
114
115     if node.status == 'offline' and changed_greaterthan(node.last_changed, 2):
116         print "changed status from %s to down" % node.status
117         node.status = 'down'
118         # NOTE: do not reset last_changed, or you lose how long it's been down.
119
120     if node.status == 'failboot' and changed_greaterthan(node.last_changed, 30):
121         print "changed status from %s to down" % node.status
122         node.status = 'down'
123         # NOTE: do not reset last_changed, or you lose how long it's been down.
124
125     if node.status == 'safeboot' and changed_greaterthan(node.last_changed, 60):
126         print "changed status from %s to down" % node.status
127         # NOTE: change an admin mode back into failboot after two months.
128         node.status = 'failboot'
129         node.last_changed = datetime.now()
130
131     # extreme cases of offline nodes
132     if ( boot_state == 'disabled' or last_contact == None ) and \
133             changed_greaterthan(node.last_changed, 2*30) and \
134             node.status != 'down':
135         print "changed status from %s to down" % node.status
136         node.status = 'down'
137         node.last_changed = datetime.now()
138
139 def checkAndRecordState(l_nodes, l_plcnodes):
140     global count
141
142     for nodename in l_nodes:
143
144         nodehist = HistoryNodeRecord.findby_or_create(hostname=nodename, 
145                             if_new_set={'status' : 'offline', 
146                                         'last_changed' : datetime.now()})
147         nodehist.last_checked = datetime.now()
148
149         try:
150             # Find the most recent record
151             noderec = FindbadNodeRecord.get_latest_by(hostname=nodename)
152         except:
153             print "COULD NOT FIND %s" % nodename
154             import traceback
155             email_exception()
156             print traceback.print_exc()
157             continue
158
159         if not noderec:
160             print "none object for %s"% nodename
161             continue
162
163         try:
164             check_node_state(noderec, nodehist)
165         except:
166             print "check_node_state failed %s" % nodename
167             import traceback
168             email_exception(nodename)
169             print traceback.print_exc()
170             continue
171
172         count += 1
173         print "%d %35s %s since(%s)" % (count, nodename, nodehist.status, diff_time(time.mktime(nodehist.last_changed.timetuple())))
174
175     # NOTE: this commits all pending operations to the DB.  Do not remove. 
176     session.flush()
177
178     return True
179
180 if __name__ == '__main__':
181     from monitor import parser as parsermodule
182     parser = parsermodule.getParser(['nodesets'])
183     parser.set_defaults(filename=None, node=None, nodeselect=False, nodegroup=None, cachenodes=False)
184     parser = parsermodule.getParser(['defaults'], parser)
185     config = parsermodule.parse_args(parser)
186
187     try:
188         main2(config)
189     except Exception, err:
190         import traceback
191         print traceback.print_exc()
192         print "Exception: %s" % err
193         sys.exit(0)