Moved some files around and merged from 1.0 branch:
[monitor.git] / www / printbadnodes.py
index ea355fe..47ef62e 100755 (executable)
@@ -1,13 +1,13 @@
 #!/usr/bin/python
-import soltesz
-from config import config
-from optparse import OptionParser
+from monitor import database
+from monitor import config
 import string
-
 import sys
+import time
 
 categories = {}
 ssherror = False
+fb = {}
 
 def sec2days(sec):
        if sec == "null":
@@ -63,11 +63,11 @@ def cmpState(l1, l2):
        return cmpMap(l1,l2,'state', map)
 
 def cmpCategoryVal(v1, v2):
-       map = array_to_priority_map([ None, 'ALPHA', 'PROD', 'OLDBOOTCD', 'UNKNOWN', 'ERROR', ])
+       map = array_to_priority_map([ None, 'ALPHA', 'PROD', 'OLDPROD', 'OLDBOOTCD', 'UNKNOWN', 'FORCED', 'ERROR', ])
        return cmpValMap(v1,v2,map)
 
 def cmpCategory(l1, l2):
-       map = array_to_priority_map([ 'ALPHA', 'PROD', 'OLDBOOTCD', 'UNKNOWN', 'ERROR', ])
+       map = array_to_priority_map([ 'ALPHA', 'PROD', 'OLDPROD', 'OLDBOOTCD', 'UNKNOWN', 'ERROR', ])
        return cmpMap(l1,l2,'category', map)
 
 def cmpPCU(l1, l2):
@@ -144,9 +144,32 @@ def ssh_error_to_str(str):
 
        return ssh_error
 
+def pcu_state(pcu_id):
+       global fb
+
+       if 'nodes' in fb and "id_%s" % pcu_id in fb['nodes'] \
+               and 'values' in fb['nodes']["id_%s" % pcu_id]:
+               rec = fb['nodes']["id_%s" % pcu_id]['values']
+               if 'reboot' in rec:
+                       rb = rec['reboot']
+                       if rb == 0 or rb == "0":
+                               return 0
+                       elif "NetDown" == rb  or "Not_Run" == rb:
+                               return 1
+                       else:
+                               return -1
+               else:
+                       return -1
+       else:
+               return -1 
+
 def fields_to_html(fields, vals):
        global categories
        global ssherror
+       pcu_colorMap = { -1 : 'indianred',
+                                         0 : 'darkseagreen',
+                                         1 : 'gold', }
+
        colorMap = { 'PING'  : 'darkseagreen',
                                 'NOPING': 'darksalmon',
                                 'SSH': 'darkseagreen',
@@ -199,7 +222,11 @@ def fields_to_html(fields, vals):
                                r_str += "<td %s>%s</td>" % (bgcolor, f)
                elif f == 'PCU':
                        if len(vals['plcnode']['pcu_ids']) > 0:
-                               url = "<a href='/cgi-bin/printbadpcus.php#id%s'>PCU</a>" % vals['plcnode']['pcu_ids'][0]
+                               #print "pcu_id: %s<br>" % vals['plcnode']['pcu_ids'][0]
+                               #print "state: %s<br>" % pcu_state(vals['plcnode']['pcu_ids'][0])
+                               #print "color: %s<br>" % pcu_colorMap[pcu_state(vals['plcnode']['pcu_ids'][0])]
+                               bgcolor = "bgcolor='%s'" % pcu_colorMap[pcu_state(vals['plcnode']['pcu_ids'][0])]
+                               url = "<a href='/cgi-bin/monitor/printbadpcus.php?id=%s'>PCU</a>" % vals['plcnode']['pcu_ids'][0]
                                r_str += "<td nowrap %s>%s</td>" % (bgcolor, url)
                else:
                        r_str += "<td nowrap %s>%s</td>" % (bgcolor, f)
@@ -208,10 +235,59 @@ def fields_to_html(fields, vals):
        
        return r_str
 
+def my_diff_time(timestamp):
+        now = time.time()
+        if timestamp == None:
+                return "not yet contacted"
+        diff = now - timestamp
+        # return the number of seconds as a difference from current time.
+        t_str = ""
+        if diff < 60: # sec in min.
+                t = diff
+                t_str = "%s sec ago" % t
+        elif diff < 60*60: # sec in hour
+                t = diff // (60)
+                t_str = "%s min ago" % int(t)
+        elif diff < 60*60*24: # sec in day
+                t = diff // (60*60)
+                t_str = "%s hours ago" % int(t)
+        elif diff < 60*60*24*7: # sec in week
+                t = diff // (60*60*24)
+                t_str = "%s days ago" % int(t)
+        elif diff < 60*60*24*30: # approx sec in month
+                t = diff // (60*60*24*7)
+                t_str = "%s weeks ago" % int(t)
+        elif diff > 60*60*24*30 and diff < 60*60*24*30*2: # approx sec in month
+                month = int( diff // (60*60*24*30) )
+                weeks = (diff - (month * (60*60*24*30))) // (60*60*24*7) 
+                if weeks == 0:
+                        t_str = "%s month ago" % int(month)
+                elif weeks == 4:
+                        t_str = "2 months ago"
+                else:
+                        t_str = "%s month and %s weeks ago" % ( int(month) , int(weeks) )
+        elif diff >= 60*60*24*30*2:                
+                month =  diff // (60*60*24*30)
+                t_str = "%s months ago" % int(month)
+        return t_str
+
+
+def main(sitefilter, catfilter, statefilter, comonfilter, nodeonlyfilter):
+       global fb
+       import os
+       import datetime
+       if nodeonlyfilter == None:
+               print "<html><body>\n"
 
+               try:
+                       mtime = os.stat("/var/lib/monitor-server/production.findbad.pkl")[-2]
+                       print "Last Updated: %s GMT" % datetime.datetime.fromtimestamp(mtime)
+               except:
+                       pass
 
-def main(sitefilter):
-       db = soltesz.dbLoad(config.dbname)
+
+       db = database.dbLoad(config.dbname)
+       fb = database.dbLoad("findbadpcus")
 
        ## Field widths used for printing
        maxFieldLengths = { 'nodename' : -45,
@@ -222,6 +298,7 @@ def main(sitefilter):
                                                'state' : 5, 
                                                'kernel' : 10.65, 
                                                'comonstats' : 5, 
+                                               'last_contact' : 10.65,
                                                'plcsite' : 12,
                                                'bootcd' : 10.65}
        ## create format string based on config.fields
@@ -230,6 +307,7 @@ def main(sitefilter):
        format_fields = []
        for f in config.fields.split(','):
                fields[f] = "%%(%s)s" % f
+               #print f
                #if f in maxFieldLengths:
                #       fields[f] = "%%(%s)%ds" % (f, maxFieldLengths[f])
                #else:
@@ -258,6 +336,11 @@ def main(sitefilter):
        # d2 was an array of [{node}, {}, ...]
        # the bysite is a loginbase dict of [{node}, {node}]
        d2 = []
+       import re
+       if sitefilter != None:
+               sf = re.compile(sitefilter)
+       else:
+               sf = None
        for nodename in l_nodes: 
                vals=d_n[nodename]['values'] 
                v = {}
@@ -283,7 +366,7 @@ def main(sitefilter):
 
                v['site_string'] = site_string
                v['loginbase'] = loginbase
-               if (sitefilter != None and loginbase == sitefilter) or sitefilter == None:
+               if (sitefilter != None and sf.match(loginbase) != None) or sitefilter == None:
                        d2.append(v)
                        
 
@@ -313,23 +396,52 @@ def main(sitefilter):
                d2.sort(cmp=cmpCategory)
        
 
+       if catfilter != None:   cf = re.compile(catfilter)
+       else:                                   cf = None
+
+       if statefilter != None: stf = re.compile(statefilter)
+       else:                                   stf = None
+
+       if comonfilter != None: cmf = re.compile(comonfilter)
+       else:                                   cmf = None
+
+
+       output_str = ""
        #l_loginbase = bysite.keys()
        #l_loginbase.sort()
-       print "<table width=80% border=1>"
+       if nodeonlyfilter == None:
+               output_str += "<table width=80% border=1>"
+
        prev_sitestring = ""
        for row in d2:
 
+               vals = row
+
+               #added by guto about last contact information
+               if (catfilter != None and cf.match(vals['category']) == None):
+                       continue
+
+               if (statefilter != None and stf.match(vals['state']) == None):
+                       continue
+
+               if (comonfilter != None and comonfilter in vals['comonstats'] and vals['comonstats'][comonfilter] != 'null'):
+                       continue
+
+               if nodeonlyfilter != None:
+                       output_str += vals['nodename']
+                       continue
+
                site_string = row['site_string']
                if site_string != prev_sitestring:
-                       print "<tr><td bgcolor=lightblue nowrap>" 
-                       print site_string
-                       print "</td>"
+                       output_str += "<tr><td bgcolor=lightblue nowrap>" 
+                       output_str += site_string
+                       output_str += "</td>"
                else:
-                       print "<tr><td>&nbsp;</td>"
+                       output_str += "<tr><td>&nbsp;</td>"
 
                prev_sitestring = site_string
 
-               vals = row
+                       
                # convert uname values into a single kernel version string
                if 'kernel' in vals:
                        kernel = vals['kernel'].split()
@@ -367,6 +479,16 @@ def main(sitefilter):
                        vals['reboot'] = "%s" % vals['reboot']
                        vals['reboot'] = vals['reboot'].replace(" ", "_")
 
+               if 'nodename' in vals:
+                       url = "<a href='https://%s/db/nodes/index.php?nodepattern=%s'>%s</a>" % (config.MONITOR_HOSTNAME, vals['nodename'], vals['nodename'])
+                       vals['nodename'] = url
+
+               if 'plcnode' in vals:
+                       if vals['plcnode']['status'] == "GN_FAILED":
+                               vals['last_contact'] = "UNKNOWN"
+                       else:
+                               vals['last_contact'] = my_diff_time(vals['plcnode']['last_contact'])
+
                try:
                        str_fields = []
                        count = 0
@@ -377,20 +499,26 @@ def main(sitefilter):
                        print >>sys.stderr, vals
 
                s = fields_to_html(str_fields, vals)
-               print s
+               output_str += s
                        
-               print "\n</tr>"
+               output_str += "\n</tr>"
 
-       print "</table>"
-       print "<table>"
+       if nodeonlyfilter == None:
+               output_str += "</table>"
        keys = categories.keys()
        keys.sort()
+       print "<table>"
        for cat in keys:
                print "<tr>"
                print "<th nowrap align=left>Total %s</th>" % cat
                print "<td align=left>%s</td>" % categories[cat]
                print "</tr>"
-       print "</table>"
+       if nodeonlyfilter == None:
+               print "</table>"
+
+       print output_str
+       if nodeonlyfilter == None:
+               print "</body></html>\n"
 
 
 
@@ -407,34 +535,39 @@ if __name__ == '__main__':
                myfilter = form.getvalue("site")
        else:
                myfilter = None
-       parser = OptionParser()
-       parser.set_defaults(cmpdays=False, 
-                                               comon="sshstatus", 
-                                               fields="nodename,ping,ssh,pcu,category,state,kernel,bootcd", 
-                                               dbname="findbad", # -070724-1", 
-                                               cmpping=False, 
-                                               cmpdns=False, 
-                                               cmploginbase=False, 
-                                               cmpssh=False, 
-                                               cmpcategory=False,
-                                               cmpstate=False)
-       parser.add_option("", "--fields",       dest="fields", help="")
-       parser.add_option("", "--dbname",       dest="dbname", help="")
-       parser.add_option("", "--days",         dest="cmpdays", action="store_true", help="")
-       parser.add_option("", "--ping",         dest="cmpping", action="store_true", help="")
-       parser.add_option("", "--dns",          dest="cmpdns", action="store_true", help="")
-       parser.add_option("", "--ssh",          dest="cmpssh",  action="store_true", help="")
-       parser.add_option("", "--loginbase",dest="cmploginbase",action="store_true", help="")
-       parser.add_option("", "--category",     dest="cmpcategory", action="store_true", help="")
-       parser.add_option("", "--kernel",       dest="cmpkernel", action="store_true", help="")
-       parser.add_option("", "--state",        dest="cmpstate", action="store_true", help="")
-       parser.add_option("", "--comon",        dest="comon",   help="")
-       config = config(parser)
-       config.parse_args()
+
+       if form.has_key('category'):
+               mycategory = form.getvalue("category")
+       else:
+               mycategory = None
+
+       if form.has_key('state'):
+               mystate = form.getvalue("state")
+       else:
+               mystate = None
+
+       if form.has_key('comon'):
+               mycomon = form.getvalue("comon")
+       else:
+               mycomon = None
+
+       if form.has_key('nodeonly'):
+               mynodeonly = form.getvalue("nodeonly")
+       else:
+               mynodeonly = None
+
+       config.cmpdays=False
+       config.comon="sshstatus"
+       config.fields="nodename,ping,ssh,pcu,category,state,last_contact,kernel,bootcd"
+       config.dbname="findbad"
+       config.cmpping=False 
+       config.cmpdns=False
+       config.cmploginbase=False
+       config.cmpssh=False 
+       config.cmpcategory=False
+
        print "Content-Type: text/html\r\n"
-       print "<html><body>\n"
        if len(sys.argv) > 1:
                if sys.argv[1] == "ssherror":
                        ssherror = True
-       main(myfilter)
-       print "</body></html>\n"
+       main(myfilter, mycategory, mystate, mycomon,mynodeonly)