import repository from arizona
[raven.git] / tools / statuscheck / statusreport.py
1 #! /usr/bin/python
2
3 import os, sys, time, signal, shm, glob, getopt
4
5 resultroot = "results"
6
7 glo_shownest = False
8 glo_nodes = {}
9 glo_types = {}
10
11 glo_ignore_list = ["daemon.initscript", "daemon.statuscheck"]
12
13 # things that should be averaged when reporting statistics
14 glo_int_numeric_types = ["count", "resource"]
15 glo_float_numeric_types = ["bench_time"]
16 glo_numeric_types = glo_int_numeric_types + glo_float_numeric_types
17
18 glo_agg_types = ["error"]
19
20 def ensure_dict(dict, name, default):
21    sub_dict = dict.get(name)
22    if not sub_dict:
23        dict[name] = default
24        sub_dict = dict[name]
25    return sub_dict
26
27 def read_file(name):
28    try:
29       file = open(name)
30       lines = file.readlines()
31       file.close()
32    except OSError:
33       return None
34    except IOError:
35       return None
36
37    node = {}
38
39    for line in lines:
40       parts = line.split()
41
42       if len(parts) >= 3:
43           msg_type = parts[0]
44           msg_name = parts[1]
45           msg_result = parts[2]
46
47           type_dict = ensure_dict(node, msg_type, {})
48
49           type_dict[msg_name] = msg_result
50
51    return node
52
53 def count():
54    global glo_nodes
55    global glo_types
56
57    for nodename in glo_nodes.keys():
58       node = glo_nodes[nodename]
59       for type in node.keys():
60          type_dict = ensure_dict(glo_types, type, {})
61
62          count = ensure_dict(type_dict, "_count", 0)
63          glo_types["_count"] = count+1
64
65          type_nodes = ensure_dict(type_dict, "_nodes", [])
66          if not (node in type_nodes):
67             type_nodes.append(node)
68
69          node_type_dict = node[type]
70          for node_msg in node_type_dict.keys():
71             msg_dict = ensure_dict(type_dict, node_msg, {})
72
73             count = ensure_dict(msg_dict, "_count", 0)
74             msg_dict["_count"] = count+1
75
76             msg_nodes = ensure_dict(msg_dict, "_nodes", [])
77             if not (node in msg_nodes):
78                 msg_nodes.append(node)
79
80             node_result = node_type_dict[node_msg]
81
82             if type in glo_numeric_types:
83                # numeric types (like counts and resources) are summed so we
84                # can take an average
85                try:
86                   if type in glo_float_numeric_types:
87                      val = float(node_result)
88                   else:
89                      val = int(node_result)
90                except:
91                   val = 0
92
93                sum = ensure_dict(msg_dict, "_sum", 0)
94                msg_dict["_sum"] = sum + val
95
96                max = ensure_dict(msg_dict, "_max", val)
97                if val > max:
98                   msg_dict["_max"] = val
99
100                min = ensure_dict(msg_dict, "_min", val)
101                if val < min:
102                   msg_dict["_min"] = val
103             else:
104                # non-numeric types (error messages, package versions, etc) are
105                # added to a dict and counted
106                result_dict = ensure_dict(msg_dict, node_result, {})
107
108                count = ensure_dict(result_dict, "_count", 0)
109                result_dict["_count"] = count +1
110
111                result_nodes = ensure_dict(result_dict, "_nodes", [])
112                if not (node in result_nodes):
113                   result_nodes.append(node)
114
115 def read():
116    global glo_nodes
117    global resultdir
118
119    file_list = glob.glob(os.path.join(resultdir, "*"))
120    for file in file_list:
121        nodename = os.path.basename(file)
122        node = read_file(file)
123        glo_nodes[nodename] = node
124
125 def ignore(typename, msgname):
126    global glo_ignore_list
127
128    if msgname.startswith("_") or typename.startswith("_"):
129        return True
130
131    if typename+"."+msgname in glo_ignore_list:
132        return True
133
134    if typename+"." in glo_ignore_list:
135        return True
136
137    if "."+msgname in glo_ignore_list:
138        return True
139
140    return False
141
142 def get_msg(node, typename, msgname):
143    type = node.get(typename)
144    if type:
145       msg = type.get(msgname)
146       if msg:
147           return msg
148    return ""
149
150 def print_color_key():
151    print "green = node is ok", "<br>"
152    print "yellow = node is unreachable", "<br>"
153    print "orange = node has a disk or /tmp problem", "<br>"
154    print "red = node has a stork problem", "<br>"
155
156 def get_bg_color(node):
157    global glo_shownest
158
159    statuscheck_error = get_msg(node, "error", "statuscheck")
160    if statuscheck_error:
161       return "#FFFF00"
162
163    pacmand_running = get_msg(node, "daemon", "pacmand")
164    if pacmand_running == "not_running":
165       return "#FF0000"
166
167    if glo_shownest:
168        nest_running = get_msg(node, "daemon", "stork_nest_proxy")
169        if nest_running == "not_running":
170           return "#FF0000"
171
172    disk_error = get_msg(node, "error", "check_disk")
173    temp_error = get_msg(node, "error", "check_temp")
174    if disk_error or temp_error:
175       return "FF8040"
176
177
178    return "#00FF00"
179
180 def report_stats():
181    global glo_types
182
183    # a little manipulation to get time and error to the beginning
184    typekeys = ["time","error"]
185    for type in glo_types.keys():
186        if not type in typekeys:
187           typekeys.append(type)
188
189    print "<br><br>"
190
191    print "<table border=1>"
192
193    print "<tr><td>type</td><td>name</td><td>result</td><td>count</td></tr>"
194
195    for typename in typekeys:
196       if not ignore(typename, "any") and typename!="time":
197          for msgname in glo_types[typename].keys():
198             if not ignore(typename, msgname):
199                value = "&nbsp;"
200                if "_sum" in glo_types[typename][msgname]:
201                   sum = glo_types[typename][msgname].get("_sum", 0)
202                   count = glo_types[typename][msgname].get("_count", 0)
203                   min = glo_types[typename][msgname].get("_min", 0)
204                   max = glo_types[typename][msgname].get("_max", 0)
205                   if count > 0:
206                      avg = sum/count
207                      print "<tr>"
208                      print "<td>" + typename + "</td><td>" + msgname + "</td><td>" + "avg/min/max" + "</td>"
209                      print "<td>" + str(avg) + " / " + str(min) + " / " + str(max) + "</td>"
210                      print "</tr>"
211                else:
212                   for msgresult in glo_types[typename][msgname]:
213                      if not msgresult.startswith("_"):
214                         print "<tr>"
215                         print "<td>" + typename + "</td><td>" + msgname + "</td><td>" + msgresult + "</td>"
216                         print "<td>" + str(glo_types[typename][msgname][msgresult]["_count"]) + "</td>"
217                         print "</tr>"
218
219    print "</table>"
220
221 def report_nodes():
222    global glo_types
223    global glo_nodes
224
225    # a little manipulation to get time and error to the beginning
226    typekeys = ["time","error"]
227    for type in glo_types.keys():
228        if not type in typekeys:
229           typekeys.append(type)
230
231    print "<table border=1>"
232
233    print "<tr><td>node</td>"
234    for typename in typekeys:
235       if not ignore(typename, "any"):
236          count = 0
237          if typename in glo_agg_types:
238             count = 1
239          else:
240             for msgname in glo_types[typename].keys():
241                if not ignore(typename, msgname):
242                   count = count+1
243          print "<td align=center colspan = " + str(count) + ">" + typename + "</td>"
244    print "</tr>"
245
246    print "<tr><td>node</td>"
247    for typename in typekeys:
248       if not ignore(typename, "any"):
249          if typename in glo_agg_types:
250             print "<td align=center>&nbsp;</td>"
251          else:
252             for msgname in glo_types[typename].keys():
253                if not ignore(typename, msgname):
254                   print "<td align=center>" + msgname + "</td>"
255    print "</tr>"
256
257    nodenames = glo_nodes.keys()
258    nodenames.sort()
259    for nodename in nodenames:
260       node = glo_nodes[nodename]
261       print "<tr bgcolor=" + get_bg_color(node) + "><td>" + nodename + "</td>"
262       for typename in typekeys:
263          if not ignore(typename, "any"):
264             if typename in glo_agg_types:
265                print "<td>"
266             first = True
267             for msgname in glo_types[typename].keys():
268                if not ignore(typename, msgname):
269                   value = "&nbsp;"
270                   if typename in node:
271                      if msgname in node[typename]:
272                         if typename=="time":
273                            value = time.ctime(float(node[typename][msgname]))
274                         else:
275                            value = node[typename][msgname]
276
277                         # errors had spaces escaped with "_", so put them back
278                         # to normal for readability. For non-errors, change all
279                         # spaces to nbsp;. This will make the error line wrap,
280                         # but force the other fields to be single lines. 
281                         if typename=="error":
282                            value = value.replace("_", " ")
283                         else:
284                            value = value.replace(" ", "&nbsp;")
285
286                   if typename in glo_agg_types:
287                      if value != "&nbsp;":
288                         if first:
289                            first = False
290                         else:
291                            print ", "
292                         print msgname + "." + value
293                   else:
294                      print "<td>" + value + "</td>"
295             if typename in glo_agg_types:
296                print "</td>"
297       print "</tr>"
298
299    print "</table>"
300
301 def main():
302    global glo_types, glo_shownest, glo_ignore_list
303    global resultdir
304
305    slicename = "arizona_stork2"
306
307    # option processing
308    (options, args) = getopt.getopt(sys.argv[1:], '', ["slicename=","shownest"])
309    for opt in options:
310        optname = opt[0]
311
312        if optname == "--slicename":
313            slicename = opt[1]
314
315        if optname == "--shownest":
316            glo_shownest = True
317
318    resultdir = os.path.join(resultroot, slicename)
319
320    if not glo_shownest:
321       glo_ignore_list.append("daemon.stork_nest_proxy")
322       glo_ignore_list.append("package.stork-nestproxy")
323
324    #print "<html><head></head><body>"
325
326    print "<h1>", slicename, "</h1>"
327
328    print_color_key()
329
330    read()
331    count()
332    report_nodes()
333    report_stats()
334
335    #print "</html>"
336
337 if __name__ == "__main__":
338    main()
339