modified *list templates with abreviated information
authorStephen Soltesz <soltesz@cs.princeton.edu>
Thu, 18 Dec 2008 00:57:49 +0000 (00:57 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Thu, 18 Dec 2008 00:57:49 +0000 (00:57 +0000)
consolidated *view templates into a single template, pcuview. should rename it.
updated findbad/findbadpcu to update the global round only after data collection is complete.
this solves the 'no information' errors when new scan is started.

17 files changed:
findbad.py
findbadpcu.py
nodebad.py
nodecommon.py
nodequery.py
pcubad.py
pcucontrol/models/racadm.py
pcucontrol/reboot.py
web/MonitorWeb/monitorweb/controllers.py
web/MonitorWeb/monitorweb/static/css/style.css
web/MonitorWeb/monitorweb/templates/actionlist.kid
web/MonitorWeb/monitorweb/templates/nodelist.kid
web/MonitorWeb/monitorweb/templates/nodeview.kid
web/MonitorWeb/monitorweb/templates/pculist.kid
web/MonitorWeb/monitorweb/templates/pcuview.kid
web/MonitorWeb/monitorweb/templates/sitelist.kid
web/MonitorWeb/monitorweb/templates/siteview.kid

index c7449d2..4d1beed 100755 (executable)
@@ -19,6 +19,7 @@ from monitor.wrapper import plc, plccache
 
 from nodequery import verify,query_to_dict,node_select
 import traceback
+from nodecommon import nmap_port_status
 
 #print "starting sqlfindbad.py"
 # QUERY all nodes.
@@ -35,6 +36,19 @@ round = 1
 global_round = round
 count = 0
 
+def collectNMAP(nodename, cohash):
+       #### RUN NMAP ###############################
+       values = {}
+       nmap = util.command.CMD()
+       print "nmap -oG - -P0 -p22,80,806 %s | grep Host:" % nodename
+       (oval,eval) = nmap.run_noexcept("nmap -oG - -P0 -p22,80,806 %s | grep Host:" % nodename)
+       # NOTE: an empty / error value for oval, will still work.
+       (values['port_status'], continue_probe) = nmap_port_status(oval)
+
+       values['date_checked'] = datetime.now()
+                       
+       return (nodename, values)
+
 def collectPingAndSSH(nodename, cohash):
        ### RUN PING ######################
        ping = command.CMD()
@@ -45,9 +59,9 @@ def collectPingAndSSH(nodename, cohash):
 
                if oval == "":
                        # An error occurred
-                       values['ping'] = "NOPING"
+                       values['ping_status'] = False
                else:
-                       values['ping'] = "PING"
+                       values['ping_status'] = True
 
                try:
                        for port in [22, 806]: 
@@ -55,13 +69,13 @@ def collectPingAndSSH(nodename, cohash):
 
                                (oval, errval) = ssh.run_noexcept2(""" <<\EOF
                                        echo "{"
-                                       echo '  "kernel":"'`uname -a`'",'
+                                       echo '  "kernel_version":"'`uname -a`'",'
                                        echo '  "bmlog":"'`ls /tmp/bm.log`'",'
-                                       echo '  "bootcd":"'`cat /mnt/cdrom/bootme/ID`'",'
-                                       echo '  "nm":"'`ps ax | grep nm.py | grep -v grep`'",'
-                                       echo '  "readonlyfs":"'`touch /var/log/monitor 2>&1`'",'
-                                       echo '  "dns":"'`host boot.planet-lab.org 2>&1`'",'
-                                       echo '  "princeton_comon":"'`ls -d /vservers/princeton_comon`'",'
+                                       echo '  "bootcd_version":"'`cat /mnt/cdrom/bootme/ID`'",'
+                                       echo '  "nm_status":"'`ps ax | grep nm.py | grep -v grep`'",'
+                                       echo '  "fs_status":"'`touch /var/log/monitor 2>&1`'",'
+                                       echo '  "dns_status":"'`host boot.planet-lab.org 2>&1`'",'
+                                       echo '  "princeton_comon_dir":"'`ls -d /vservers/princeton_comon`'",'
 
                                        ID=`grep princeton_comon /etc/passwd | awk -F : '{if ( $3 > 500 ) { print $3}}'` 
                                        echo '  "princeton_comon_running":"'`ls -d /proc/virtual/$ID`'",'
@@ -69,20 +83,20 @@ def collectPingAndSSH(nodename, cohash):
                                        echo "}"
 EOF                            """)
                                
-                               values['ssherror'] = errval
+                               values['ssh_error'] = errval
                                if len(oval) > 0:
                                        #print "OVAL: %s" % oval
                                        values.update(eval(oval))
-                                       values['sshport'] = port
+                                       values['ssh_portused'] = port
                                        break
                                else:
-                                       values.update({'kernel': "", 'bmlog' : "", 'bootcd' : '', 
-                                                                       'nm' : '', 
-                                                                       'readonlyfs' : '',
-                                                                       'dns' : '',
-                                                                       'princeton_comon' : "", 
+                                       values.update({'kernel_version': "", 'bmlog' : "", 'bootcd_version' : '', 
+                                                                       'nm_status' : '', 
+                                                                       'fs_status' : '',
+                                                                       'dns_status' : '',
+                                                                       'princeton_comon_dir' : "", 
                                                                        'princeton_comon_running' : "", 
-                                                                       'princeton_comon_procs' : "", 'sshport' : None})
+                                                                       'princeton_comon_procs' : "", 'ssh_portused' : None})
                except:
                        print traceback.print_exc()
                        sys.exit(1)
@@ -94,79 +108,79 @@ EOF                                """)
                #errval = ""
                #(oval, errval) = ssh.run_noexcept('echo `uname -a ; ls /tmp/bm.log`')
 
-               oval = values['kernel']
+               oval = values['kernel_version']
                if "2.6.17" in oval or "2.6.2" in oval:
-                       values['ssh'] = 'SSH'
-                       values['category'] = 'PROD'
+                       values['ssh_status'] = True
+                       values['observed_category'] = 'PROD'
                        if "bm.log" in values['bmlog']:
-                               values['state'] = 'DEBUG'
+                               values['observed_status'] = 'DEBUG'
                        else:
-                               values['state'] = 'BOOT'
+                               values['observed_status'] = 'BOOT'
                elif "2.6.12" in oval or "2.6.10" in oval:
-                       values['ssh'] = 'SSH'
-                       values['category'] = 'OLDPROD'
+                       values['ssh_status'] = True
+                       values['observed_category'] = 'OLDPROD'
                        if "bm.log" in values['bmlog']:
-                               values['state'] = 'DEBUG'
+                               values['observed_status'] = 'DEBUG'
                        else:
-                               values['state'] = 'BOOT'
+                               values['observed_status'] = 'BOOT'
                
                # NOTE: on 2.6.8 kernels, with 4.2 bootstrapfs, the chroot command fails.  I have no idea why.
                elif "2.4" in oval or "2.6.8" in oval:
                        b_getbootcd_id = False
-                       values['ssh'] = 'SSH'
-                       values['category'] = 'OLDBOOTCD'
-                       values['state'] = 'DEBUG'
+                       values['ssh_status'] = True
+                       values['observed_category'] = 'OLDBOOTCD'
+                       values['observed_status'] = 'DEBUG'
                elif oval != "":
-                       values['ssh'] = 'SSH'
-                       values['category'] = 'UNKNOWN'
+                       values['ssh_status'] = True
+                       values['observed_category'] = 'UNKNOWN'
                        if "bm.log" in values['bmlog']:
-                               values['state'] = 'DEBUG'
+                               values['observed_status'] = 'DEBUG'
                        else:
-                               values['state'] = 'BOOT'
+                               values['observed_status'] = 'BOOT'
                else:
                        # An error occurred.
                        b_getbootcd_id = False
-                       values['ssh'] = 'NOSSH'
-                       values['category'] = 'ERROR'
-                       values['state'] = 'DOWN'
+                       values['ssh_status'] = False
+                       values['observed_category'] = 'ERROR'
+                       values['observed_status'] = 'DOWN'
                        val = errval.strip()
-                       values['ssherror'] = val
-                       values['kernel'] = ""
+                       values['ssh_error'] = val
+                       values['kernel_version'] = ""
 
-               #values['kernel'] = val
+               #values['kernel_version'] = val
 
                if b_getbootcd_id:
                        # try to get BootCD for all nodes that are not 2.4 nor inaccessible
                        #(oval, errval) = ssh.run_noexcept('cat /mnt/cdrom/bootme/ID')
-                       oval = values['bootcd']
+                       oval = values['bootcd_version']
                        if "BootCD" in oval:
-                               values['bootcd'] = oval
+                               values['bootcd_version'] = oval
                                if "v2" in oval and \
                                        ( nodename is not "planetlab1.cs.unc.edu" and \
                                          nodename is not "planetlab2.cs.unc.edu" ):
-                                       values['category'] = 'OLDBOOTCD'
+                                       values['observed_category'] = 'OLDBOOTCD'
                        else:
-                               values['bootcd'] = ""
+                               values['bootcd_version'] = ""
                else:
-                       values['bootcd'] = ""
+                       values['bootcd_version'] = ""
 
                # TODO: get bm.log for debug nodes.
                # 'zcat /tmp/bm.log'
                
                #(oval, errval) = ssh.run_noexcept('ps ax | grep nm.py | grep -v grep')
-               oval = values['nm']
+               oval = values['nm_status']
                if "nm.py" in oval:
-                       values['nm'] = "Y"
+                       values['nm_status'] = "Y"
                else:
-                       values['nm'] = "N"
+                       values['nm_status'] = "N"
 
                continue_slice_check = True
                #(oval, errval) = ssh.run_noexcept('ls -d /vservers/princeton_comon')
-               oval = values['princeton_comon']
-               if "princeton_comon" in oval:
-                       values['princeton_comon'] = True
+               oval = values['princeton_comon_dir']
+               if "princeton_comon_dir" in oval:
+                       values['princeton_comon_dir'] = True
                else:
-                       values['princeton_comon'] = False
+                       values['princeton_comon_dir'] = False
                        continue_slice_check = False
 
                if continue_slice_check:
@@ -189,9 +203,9 @@ EOF                         """)
 
                        
                if nodename in cohash: 
-                       values['comonstats'] = cohash[nodename]
+                       values['comon_stats'] = cohash[nodename]
                else:
-                       values['comonstats'] = {'resptime':  '-1', 
+                       values['comon_stats'] = {'resptime':  '-1', 
                                                                        'uptime':    '-1',
                                                                        'sshstatus': '-1', 
                                                                        'lastcotop': '-1',
@@ -208,7 +222,11 @@ EOF                                """)
                except:
                        traceback.print_exc()
                plc_lock.release()
-               values['plcnode'] = d_node
+               values['plc_node_stats'] = d_node
+
+               ##### NMAP  ###################
+               (n, v) = collectNMAP(nodename, None)
+               values.update(v)
 
                ### GET PLC PCU ######################
                site_id = -1
@@ -220,7 +238,7 @@ EOF                         """)
 
                        site_id = d_node['site_id']
 
-               values['pcu'] = d_pcu
+               values['plc_pcuid'] = d_pcu
 
                ### GET PLC SITE ######################
                plc_lock.acquire()
@@ -234,8 +252,8 @@ EOF                         """)
                        traceback.print_exc()
                plc_lock.release()
 
-               values['plcsite'] = d_site 
-               values['date_checked'] = time.time()
+               values['plc_site_stats'] = d_site 
+               values['date_checked'] = datetime.now()
        except:
                print traceback.print_exc()
 
@@ -248,9 +266,9 @@ def recordPingAndSSH(request, result):
 
        try:
                if values is not None:
-                       fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
-                                                                                                                       if_new_set={'round' : global_round})
-                       global_round = fbsync.round
+                       #fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
+                       #                                                                                               if_new_set={'round' : global_round})
+                       #global_round = fbsync.round
                        fbnodesync = FindbadNodeRecordSync.findby_or_create(hostname=nodename,
                                                                                                                        if_new_set={'round' : global_round})
 
@@ -262,43 +280,38 @@ def recordPingAndSSH(request, result):
                        fbrec = FindbadNodeRecord.findby_or_create(
                                                round=global_round,
                                                hostname=nodename)
-                       before = fbrec.to_dict()
-                       print "BEFORE, ", before
-                       fbrec.flush()
-                       time.sleep(2)
-                       print "Setting VALUES"
-                       fbrec.set(  date_checked=datetime.fromtimestamp(values['date_checked']),
-                                               loginbase=values['loginbase'],
-                                               kernel_version=values['kernel'],
-                                               bootcd_version=values['bootcd'],
-                                               nm_status=values['nm'],
-                                               fs_status=values['readonlyfs'],
-                                               dns_status=values['dns'],
-                                               princeton_comon_dir=values['princeton_comon'],
-                                               princeton_comon_running=values['princeton_comon_running'],
-                                               princeton_comon_procs=values['princeton_comon_procs'],
-                                               plc_node_stats = values['plcnode'],
-                                               plc_site_stats = values['plcsite'],
-                                               plc_pcuid = values['pcu'],
-                                               comon_stats = values['comonstats'],
-                                               ping_status = (values['ping'] == "PING"),
-                                               ssh_portused = values['sshport'],
-                                               ssh_status = (values['ssh'] == "SSH"),
-                                               ssh_error = values['ssherror'],
-                                               observed_status = values['state'],
-                                               observed_category = values['category'])
-                       after = fbrec.to_dict()
-                       print "AFTER , ", after
-
-                       for v in before.keys():
-                               if before[v] == after[v]:
-                                       print "SAME FOR KEY %s" % v
-                               print "%s : %s\t%s" % ( v, before[v], after[v] )
+
+                       fbrec.set(  **values ) 
+                                               #date_checked=values['date_checked'],
+                                               #loginbase=values['loginbase'],
+                                               #kernel_version=values['kernel_version'],
+                                               #bootcd_version=values['bootcd_version'],
+                                               #nm_status=values['nm_status'],
+                                               #fs_status=values['fs_status'],
+                                               #dns_status=values['dns_status'],
+                                               #princeton_comon_dir=values['princeton_comon_dir'],
+                                               #princeton_comon_running=values['princeton_comon_running'],
+                                               #princeton_comon_procs=values['princeton_comon_procs'],
+                                               #plc_node_stats = values['plc_node_stats'],
+                                               #plc_site_stats = values['plc_site_stats'],
+                                               #plc_pcuid = values['plc_pcuid'],
+                                               #comon_stats = values['comon_stats'],
+                                               #ping_status = values['ping_status'],
+                                               #ssh_portused = values['ssh_portused'],
+                                               #ssh_status = values['ssh_status'],
+                                               #ssh_error = values['ssh_error'],
+                                               #observed_status = values['observed_status'],
+                                               #observed_category = values['observed_category'])
+
+                       #for v in before.keys():
+                       #       if before[v] == after[v]:
+                       #               print "SAME FOR KEY %s" % v
+                       #       print "%s : %s\t%s" % ( v, before[v], after[v] )
 
                        fbrec.flush()
                        fbnodesync.round = global_round
                        fbnodesync.flush()
-                       fbsync.flush()
+                       #fbsync.flush()
 
                        count += 1
                        print "%d %s %s" % (count, nodename, values)
@@ -312,6 +325,16 @@ def handle_exception(request, result):
        for i in result:
                print "Result: %s" % i
 
+def externalprobe(hostname):
+       try:
+               (nodename, values) = collectNMAP(hostname, {})
+               recordPingAndSSH(None, (nodename, values))
+               session.flush()
+               return True
+       except:
+               print traceback.print_exc()
+               return False
+
 def probe(hostname):
        try:
                (nodename, values) = collectPingAndSSH(hostname, {})
@@ -335,7 +358,7 @@ def checkAndRecordState(l_nodes, cohash):
                node_round   = fbnodesync.round
                fbnodesync.flush()
 
-               if node_round < global_round:
+               if node_round < global_round or config.force:
                        # recreate node stats when refreshed
                        #print "%s" % nodename
                        req = threadpool.WorkRequest(collectPingAndSSH, [nodename, cohash], {}, 
@@ -378,9 +401,6 @@ def main():
        if config.increment:
                # update global round number to force refreshes across all nodes
                global_round += 1
-               fbsync.round = global_round
-
-       fbsync.flush()
 
        cotop = comon.Comon()
        # lastcotop measures whether cotop is actually running.  this is a better
@@ -417,6 +437,11 @@ def main():
 
        checkAndRecordState(l_nodes, cohash)
 
+       if config.increment:
+               # update global round number to force refreshes across all nodes
+               fbsync.round = global_round
+               fbsync.flush()
+
        return 0
 
 
@@ -425,13 +450,16 @@ if __name__ == '__main__':
 
        parser = parsermodule.getParser(['nodesets'])
 
-       parser.set_defaults( increment=False, dbname="findbad", cachenodes=False)
+       parser.set_defaults( increment=False, dbname="findbad", cachenodes=False, 
+                                               force=False,)
        parser.add_option("", "--cachenodes", action="store_true",
                                                help="Cache node lookup from PLC")
        parser.add_option("", "--dbname", dest="dbname", metavar="FILE", 
                                                help="Specify the name of the database to which the information is saved")
        parser.add_option("-i", "--increment", action="store_true", dest="increment", 
                                                help="Increment round number to force refresh or retry")
+       parser.add_option("", "--force", action="store_true", dest="force", 
+                                               help="Force probe without incrementing global 'round'.")
 
        parser = parsermodule.getParser(['defaults'], parser)
        
index 468107d..0d06d1e 100755 (executable)
@@ -20,25 +20,13 @@ from monitor import database
 from monitor import util 
 from monitor.wrapper import plc, plccache
 from nodequery import pcu_select
+from nodecommon import nmap_port_status
 
 plc_lock = threading.Lock()
 global_round = 1
 errorState = {}
 count = 0
 
-def nmap_port_status(status):
-       ps = {}
-       l_nmap = status.split()
-       ports = l_nmap[4:]
-
-       continue_probe = False
-       for port in ports:
-               results = port.split('/')
-               ps[results[0]] = results[1]
-               if results[1] == "open":
-                       continue_probe = True
-       return (ps, continue_probe)
-
 def get_pcu(pcuname):
        plc_lock.acquire()
        try:
@@ -176,7 +164,16 @@ def collectPingAndSSH(pcuname, cohash):
 
                if b_except or not continue_probe: return (None, None, None)
 
-
+               #### RUN NMAP ###############################
+               if continue_probe:
+                       nmap = util.command.CMD()
+                       print "nmap -oG - -P0 -p22,23,80,443,5869,9100,16992 %s | grep Host:" % reboot.pcu_name(values['plc_pcu_stats'])
+                       (oval,eval) = nmap.run_noexcept("nmap -oG - -P0 -p22,23,80,443,5869,9100,16992 %s | grep Host:" % reboot.pcu_name(values['plc_pcu_stats']))
+                       # NOTE: an empty / error value for oval, will still work.
+                       (values['port_status'], continue_probe) = nmap_port_status(oval)
+               else:
+                       values['port_status'] = None
+                       
                #### COMPLETE ENTRY   #######################
 
                values['entry_complete'] = []
@@ -203,7 +200,8 @@ def collectPingAndSSH(pcuname, cohash):
                # If there are no nodes associated with this PCU, then we cannot continue.
                if len(values['plc_pcu_stats']['node_ids']) == 0:
                        continue_probe = False
-                       values['entry_complete'] += ['NoNodeIds']
+                       values['entry_complete'] += ['nodeids']
+
 
                #### DNS and IP MATCH #######################
                if values['plc_pcu_stats']['hostname'] is not None and values['plc_pcu_stats']['hostname'] is not "" and \
@@ -230,19 +228,11 @@ def collectPingAndSSH(pcuname, cohash):
                                values['plc_pcu_stats']['hostname'] = "No_entry_in_DB"
                                continue_probe = False
 
-               #### RUN NMAP ###############################
-               if continue_probe:
-                       nmap = util.command.CMD()
-                       (oval,eval) = nmap.run_noexcept("nmap -oG - -P0 -p22,23,80,443,5869,9100,16992 %s | grep Host:" % reboot.pcu_name(values['plc_pcu_stats']))
-                       # NOTE: an empty / error value for oval, will still work.
-                       (values['port_status'], continue_probe) = nmap_port_status(oval)
-               else:
-                       values['port_status'] = None
-                       
 
                ######  DRY RUN  ############################
                if 'node_ids' in values['plc_pcu_stats'] and len(values['plc_pcu_stats']['node_ids']) > 0:
-                       rb_ret = reboot.reboot_test_new(values['plc_pcu_stats']['nodenames'][0], values, continue_probe, 1, True)
+                       rb_ret = reboot.reboot_test_new(values['plc_pcu_stats']['nodenames'][0], 
+                                                                                       values, 1, True)
                else:
                        rb_ret = "Not_Run" # No nodes to test"
 
@@ -268,15 +258,15 @@ def recordPingAndSSH(request, result):
 
        if values is not None:
                pcu_id = int(nodename)
-               fbsync = FindbadPCURecordSync.findby_or_create(plc_pcuid=0, 
-                                                                                       if_new_set={'round': global_round})
-               global_round = fbsync.round
+               #fbsync = FindbadPCURecordSync.findby_or_create(plc_pcuid=0, 
+               #                                                                       if_new_set={'round': global_round})
+               #global_round = fbsync.round
                fbnodesync = FindbadPCURecordSync.findby_or_create(plc_pcuid=pcu_id, 
                                                                                        if_new_set={'round' : global_round})
 
                fbrec = FindbadPCURecord(
                                        date_checked=datetime.fromtimestamp(values['date_checked']),
-                                       round=fbsync.round,
+                                       round=global_round,
                                        plc_pcuid=pcu_id,
                                        plc_pcu_stats=values['plc_pcu_stats'],
                                        dns_status=values['dns_status'],
@@ -287,7 +277,7 @@ def recordPingAndSSH(request, result):
                fbnodesync.round = global_round
 
                fbnodesync.flush()
-               fbsync.flush()
+               #fbsync.flush()
                fbrec.flush()
 
                count += 1
@@ -379,7 +369,7 @@ def main():
                l_pcus = [pcu for pcu in sets.Set(pcus)]
 
        elif config.nodelist == None and config.pcuid == None:
-               print "Calling API GetPCUs() : refresh(%s)" % config.refresh
+               print "Calling API GetPCUs() : cachecalls(%s)" % config.cachecalls
                l_pcus  = [pcu['pcu_id'] for pcu in l_pcus]
        elif config.nodelist is not None:
                l_pcus = util.file.getListFromFile(config.nodelist)
@@ -391,11 +381,15 @@ def main():
        if config.increment:
                # update global round number to force refreshes across all nodes
                global_round += 1
-               fbsync.round = global_round
-       fbsync.flush()
 
        checkAndRecordState(l_pcus, cohash)
 
+       if config.increment:
+               # update global round number to force refreshes across all nodes
+               fbsync.round = global_round
+               fbsync.flush()
+               session.flush()
+
        return 0
 
 
index baa016c..f9f6edf 100755 (executable)
@@ -14,6 +14,7 @@ from monitor import config
 from monitor.wrapper import plc,plccache
 from monitor.const import MINUP
 from monitor.database.info.model import  FindbadNodeRecord, HistoryNodeRecord
+from monitor.database.dborm import  mon_session as session
 
 from monitor.model import *
 
@@ -54,6 +55,10 @@ def checkAndRecordState(l_nodes, l_plcnodes):
                        print traceback.print_exc()
                        continue
 
+               if not noderec:
+                       print "none object for %s"% nodename
+                       continue
+
                node_state = noderec.observed_status
                if noderec.plc_node_stats:
                        boot_state = noderec.plc_node_stats['boot_state']
@@ -80,6 +85,7 @@ def checkAndRecordState(l_nodes, l_plcnodes):
        # replace with another operations that also commits all pending ops, such
        # as session.commit() or flush() or something
        print HistoryNodeRecord.query.count()
+       session.flush()
 
        return True
 
index 082550b..051cd61 100644 (file)
@@ -122,6 +122,20 @@ def getvalue(fb, path):
             return None
     return values
 
+def nmap_port_status(status):
+       ps = {}
+       l_nmap = status.split()
+       ports = l_nmap[4:]
+
+       continue_probe = False
+       for port in ports:
+               results = port.split('/')
+               ps[results[0]] = results[1]
+               if results[1] == "open":
+                       continue_probe = True
+       return (ps, continue_probe)
+
+
 def nodegroup_display(node, fbdata, conf=None):
        node['current'] = get_current_state(fbdata)
 
index 48a5f73..bcebf15 100755 (executable)
@@ -256,7 +256,7 @@ def query_to_dict(query):
 def pcu_in(fbdata):
        #if 'plcnode' in fbdata:
        if 'plc_node_stats' in fbdata:
-               if 'pcu_ids' in fbdata['plc_node_stats']:
+               if fbdata['plc_node_stats'] and 'pcu_ids' in fbdata['plc_node_stats']:
                        if len(fbdata['plc_node_stats']['pcu_ids']) > 0:
                                return True
        return False
@@ -275,19 +275,28 @@ def pcu_select(str_query, nodelist=None):
 
        dict_query = query_to_dict(str_query)
        print "dict_query", dict_query
-
-       for noderec in fbquery:
-               if nodelist is not None: 
-                       if noderec.hostname not in nodelist: continue
-       
-               fb_nodeinfo  = noderec.to_dict()
-               if pcu_in(fb_nodeinfo):
-                       pcurec = FindbadPCURecord.get_latest_by(plc_pcuid=get(fb_nodeinfo, 'plc_node_stats.pcu_ids')[0]).first()
-                       if pcurec:
-                               pcuinfo = pcurec.to_dict()
-                               if verify(dict_query, pcuinfo):
-                                       nodenames.append(noderec.hostname)
-                                       pcunames.append(pcuinfo['plc_pcuid'])
+       print 'length %s' % len(fbpcuquery.all())
+
+       for pcurec in fbpcuquery:
+               pcuinfo = pcurec.to_dict()
+               if verify(dict_query, pcuinfo):
+                       #nodenames.append(noderec.hostname)
+                       #print 'appending %s' % pcuinfo['plc_pcuid']
+                       pcunames.append(pcuinfo['plc_pcuid'])
+
+       #for noderec in fbquery:
+       #       if nodelist is not None: 
+       #               if noderec.hostname not in nodelist: continue
+#      
+#              fb_nodeinfo  = noderec.to_dict()
+#              if pcu_in(fb_nodeinfo):
+#                      pcurec = FindbadPCURecord.get_latest_by(plc_pcuid=get(fb_nodeinfo, 
+#                                                                                                      'plc_node_stats.pcu_ids')[0]).first()
+#                      if pcurec:
+#                              pcuinfo = pcurec.to_dict()
+#                              if verify(dict_query, pcuinfo):
+#                                      nodenames.append(noderec.hostname)
+#                                      pcunames.append(pcuinfo['plc_pcuid'])
        return (nodenames, pcunames)
 
 def node_select(str_query, nodelist=None, fb=None):
index b31599f..6ca478f 100755 (executable)
--- a/pcubad.py
+++ b/pcubad.py
@@ -11,6 +11,7 @@ from pcucontrol  import reboot
 from monitor import parser as parsermodule
 from monitor import config
 from monitor.database.info.model import HistoryPCURecord, FindbadPCURecord
+from monitor.database.dborm import mon_session as session
 from monitor.wrapper import plc,plccache
 from monitor.const import MINUP
 
@@ -93,6 +94,7 @@ def checkAndRecordState(l_pcus, l_plcpcus):
        # replace with another operations that also commits all pending ops, such
        # as session.commit() or flush() or something
        print HistoryPCURecord.query.count()
+       session.flush()
 
        return True
 
index 8dec875..f4e69dc 100755 (executable)
@@ -100,7 +100,9 @@ def racadm_reboot(host, username, password, dryrun, state="powercycle"):
 
 from optparse import OptionParser
 parser = OptionParser()
-parser.set_defaults(ip="", user="", password="", state="powercycle")
+parser.set_defaults(ip="", user="", password="", dryrun=False, state="powercycle")
+parser.add_option("-d", "", dest="dryrun", action="store_true",
+                                       help="enable dryrun tests.  no action is taken")
 parser.add_option("-r", "", dest="ip", metavar="nodename.edu", 
                                        help="A single node name to add to the nodegroup")
 parser.add_option("-u", "", dest="user", metavar="username",
@@ -117,6 +119,6 @@ if __name__ == '__main__':
                options.user is not "" and \
                options.password is not "":
 
-               racadm_reboot(options.ip, options.user, options.password, False, options.state)
+               racadm_reboot(options.ip, options.user, options.password, options.dryrun, options.state)
        else:
                parser.print_help()
index 04fe4da..decaf1d 100755 (executable)
@@ -120,6 +120,7 @@ class Transport:
        HTTP   = 3
        HTTPS  = 4
        IPAL   = 5
+       DRAC   = 6
 
        TELNET_TIMEOUT = 120
 
@@ -231,7 +232,10 @@ class PCUControl(Transport,PCUModel,PCURecord):
                PCURecord.__init__(self, plc_pcu_record)
                type = None
                if self.port_status:
-                       if '22' in supported_ports and self.port_status['22'] == "open":
+                       # NOTE: prefer racadm port over ssh
+                       if '5869' in supported_ports and self.port_status['5869'] == "open":
+                               type = Transport.DRAC# DRAC cards user this port.
+                       elif '22' in supported_ports and self.port_status['22'] == "open":
                                type = Transport.SSH
                        elif '23' in supported_ports and self.port_status['23'] == "open":
                                type = Transport.TELNET
@@ -240,9 +244,6 @@ class PCUControl(Transport,PCUModel,PCURecord):
                                type = Transport.HTTPS
                        elif '80' in supported_ports and self.port_status['80'] == "open":
                                type = Transport.HTTP
-                       elif '5869' in supported_ports and self.port_status['5869'] == "open":
-                               # For DRAC cards. Racadm opens this port.
-                               type = Transport.HTTP
                        elif '9100' in supported_ports and self.port_status['9100'] == "open":
                                type = Transport.IPAL
                        elif '16992' in supported_ports and self.port_status['16992'] == "open":
@@ -343,7 +344,13 @@ class IPAL(PCUControl):
 
        def run(self, node_port, dryrun):
                if self.type == Transport.IPAL:
-                       return self.run_ipal(node_port, dryrun)
+                       ret = self.run_ipal(node_port, dryrun)
+                       if ret != 0:
+                               ret2 = self.run_telnet(node_port, dryrun)
+                               if ret2 != 0:
+                                       return ret
+                               return ret2
+                       return ret
                elif self.type == Transport.TELNET:
                        return self.run_telnet(node_port, dryrun)
                else:
@@ -636,10 +643,53 @@ class IntelAMT(PCUControl):
                return cmd.system(cmd_str, self.TELNET_TIMEOUT)
 
 class DRAC(PCUControl):
+       supported_ports = [22,443,5869]
        def run(self, node_port, dryrun):
+               if self.type == Transport.DRAC:
+                       print "trying racadm_reboot..."
+                       return racadm_reboot(self.host, self.username, self.password, node_port, dryrun)
+               elif self.type == Transport.SSH:
+                       return self.run_ssh(node_port, dryrun)
+               else:
+                       raise ExceptionNoTransport("No implementation for open ports")
 
-               print "trying racadm_reboot..."
-               racadm_reboot(self.host, self.username, self.password, node_port, dryrun)
+       def run_ssh(self, node_port, dryrun):
+               ssh_options="-o StrictHostKeyChecking=no "+\
+                           "-o PasswordAuthentication=yes "+\
+                                       "-o PubkeyAuthentication=no"
+               s = pxssh.pxssh()
+               if not s.login(self.host, self.username, self.password, ssh_options,
+                                               original_prompts="Dell", login_timeout=TELNET_TIMEOUT):
+                       raise ExceptionPassword("Invalid Password")
+
+               print "logging in..."
+               s.send("\r\n\r\n")
+               try:
+                       # Testing Reboot ?
+                       #index = s.expect(["DRAC 5", "[%s]#" % self.username ])
+                       # NOTE: be careful to escape any characters used by 're.compile'
+                       index = s.expect(["\$", "\[%s\]#" % self.username ])
+                       print "INDEX:", index
+                       if dryrun:
+                               if index == 0:
+                                       s.send("racadm getsysinfo")
+                               elif index == 1:
+                                       s.send("getsysinfo")
+                       else:
+                               if index == 0:
+                                       s.send("racadm serveraction powercycle")
+                               elif index == 1:
+                                       s.send("serveraction powercycle")
+                               
+                       s.send("exit")
+
+               except pexpect.EOF:
+                       raise ExceptionPrompt("EOF before expected Prompt")
+               except pexpect.TIMEOUT:
+                       print s
+                       raise ExceptionPrompt("Timeout before expected Prompt")
+
+               s.close()
 
                return 0
 
@@ -1080,7 +1130,7 @@ class ePowerSwitchOld(PCUControl):
                return 0
                
 class ManualPCU(PCUControl):
-       supported_ports = [22,23,80,443,9100,16992]
+       supported_ports = [22,23,80,443]
 
        def run(self, node_port, dryrun):
                if not dryrun:
@@ -1291,7 +1341,7 @@ def racadm_reboot(host, username, password, port, dryrun):
                logger.debug("runcmd raised exception %s" % err)
                if verbose:
                        logger.debug(err)
-               return -1
+               return err
 
 def pcu_name(pcu):
        if pcu['hostname'] is not None and pcu['hostname'] is not "":
@@ -1372,6 +1422,8 @@ class Unknown(PCUControl):
        supported_ports = [22,23,80,443,5869,9100,16992]
 
 def model_to_object(modelname):
+       if modelname is None:
+               return ManualPCU 
        if "AMT" in modelname:
                return IntelAMT
        elif "BayTech" in modelname:
index e5d0da2..a3e3021 100644 (file)
@@ -1,5 +1,6 @@
 import turbogears as tg
 from turbogears import controllers, expose, flash, exception_handler
+from turbogears import widgets
 from cherrypy import request, response
 import cherrypy
 # from monitorweb import model
@@ -20,28 +21,52 @@ from monitorweb.templates.links import *
 
 import findbad
 
-def format_ports(pcu):
+
+def query_to_dict(query):
+       """ take a url query string and chop it up """
+       val = {}
+       query_fields = query.split('&')
+       for f in query_fields:
+               (k,v) = urllib.splitvalue(f)
+               val[k] = v
+
+       return val
+
+def format_ports(data, pcumodel=None):
        retval = []
-       if pcu.port_status and len(pcu.port_status.keys()) > 0 :
-               obj = reboot.model_to_object(pcu.plc_pcu_stats['model'])
-               for port in obj.supported_ports:
+       filtered_length=0
+
+       if pcumodel:
+               supported_ports=reboot.model_to_object(pcumodel).supported_ports
+       else:
+               # ports of a production node
+               supported_ports=[22,80,806]
+
+       if data and len(data.keys()) > 0 :
+               for port in supported_ports:
                        try:
-                               state = pcu.port_status[str(port)]
+                               state = data[str(port)]
                        except:
                                state = "unknown"
+
+                       if state == "filtered":
+                               filtered_length += 1
                                
                        retval.append( (port, state) )
 
        if retval == []: 
                retval = [( "Closed/Filtered", "state" )]
 
+       if filtered_length == len(supported_ports):
+               retval = [( "All Filtered", "state" )]
+
        return retval
 
 def format_pcu_shortstatus(pcu):
        status = "error"
        if pcu:
                if pcu.reboot_trial_status == str(0):
-                       status = "ok"
+                       status = "Ok"
                elif pcu.reboot_trial_status == "NetDown" or pcu.reboot_trial_status == "Not_Run":
                        status = pcu.reboot_trial_status
                else:
@@ -56,21 +81,44 @@ def prep_pcu_for_display(pcu):
        except:
                pcu.loginbase = "unknown"
 
-       pcu.ports = format_ports(pcu)
+       pcu.ports = format_ports(pcu.port_status, pcu.plc_pcu_stats['model'])
        pcu.status = format_pcu_shortstatus(pcu)
 
+       #print pcu.entry_complete
+       pcu.entry_complete_str = pcu.entry_complete
+       #pcu.entry_complete_str += "".join([ f[0] for f in pcu.entry_complete.split() ])
+       if pcu.dns_status == "NOHOSTNAME":
+               pcu.dns_short_status = 'NoHost'
+       elif pcu.dns_status == "DNS-OK":
+               pcu.dns_short_status = 'Ok'
+       elif pcu.dns_status == "DNS-NOENTRY":
+               pcu.dns_short_status = 'NoEntry'
+       elif pcu.dns_status == "NO-DNS-OR-IP":
+               pcu.dns_short_status = 'NoHostOrIP'
+       elif pcu.dns_status == "DNS-MISMATCH":
+               pcu.dns_short_status = 'Mismatch'
+
+class NodeWidget(widgets.Widget):
+       pass
+
 def prep_node_for_display(node):
        if node.plc_pcuid:
                pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid).first()
                if pcu:
                        node.pcu_status = pcu.reboot_trial_status
+                       node.pcu_short_status = format_pcu_shortstatus(pcu)
+                       node.pcu = pcu
+                       prep_pcu_for_display(node.pcu)
                else:
+                       node.pcu_short_status = "none"
                        node.pcu_status = "nodata"
-               node.pcu_short_status = format_pcu_shortstatus(pcu)
+                       node.pcu = None
 
        else:
                node.pcu_status = "nopcu"
                node.pcu_short_status = "none"
+               node.pcu = None
+
 
        if node.kernel_version:
                node.kernel = node.kernel_version.split()[2]
@@ -81,7 +129,20 @@ def prep_node_for_display(node):
                node.loginbase = site_id2lb[node.plc_node_stats['site_id']]
        except:
                node.loginbase = "unknown"
-       
+
+       if node.loginbase:
+               node.site = HistorySiteRecord.by_loginbase(node.loginbase)
+
+       node.history = HistoryNodeRecord.by_hostname(node.hostname)
+
+       if node.port_status:
+               node.ports = format_ports(node.port_status)
+       try:
+               exists = node.plc_node_stats['last_contact']
+       except:
+               node.plc_node_stats = {'last_contact' : None}
+
+
 
 class Root(controllers.RootController):
        @expose(template="monitorweb.templates.welcome")
@@ -91,7 +152,7 @@ class Root(controllers.RootController):
                flash("Your application is now running")
                return dict(now=time.ctime())
 
-       @expose(template="monitorweb.templates.nodeview")
+       @expose(template="monitorweb.templates.pcuview")
        def nodeview(self, hostname=None):
                nodequery=[]
                if hostname:
@@ -100,7 +161,7 @@ class Root(controllers.RootController):
                                prep_node_for_display(node)
                                nodequery += [node]
 
-               return dict(nodequery=nodequery)
+               return self.pcuview(None, hostname) # dict(nodequery=nodequery)
 
        @expose(template="monitorweb.templates.nodelist")
        def node(self, filter='BOOT'):
@@ -116,7 +177,7 @@ class Root(controllers.RootController):
                        if node.observed_status != 'DOWN':
                                filtercount[node.observed_status] += 1
                        else:
-                               if node.plc_node_stats['last_contact'] != None:
+                               if node.plc_node_stats and node.plc_node_stats['last_contact'] != None:
                                        filtercount[node.observed_status] += 1
                                else:
                                        filtercount['neverboot'] += 1
@@ -129,7 +190,7 @@ class Root(controllers.RootController):
                                else:
                                        query.append(node)
                        elif filter == "neverboot":
-                               if node.plc_node_stats['last_contact'] == None:
+                               if not node.plc_node_stats or node.plc_node_stats['last_contact'] == None:
                                        query.append(node)
                        elif filter == "pending":
                                # TODO: look in message logs...
@@ -137,15 +198,22 @@ class Root(controllers.RootController):
                        elif filter == "all":
                                query.append(node)
                                
-               return dict(now=time.ctime(), query=query, fc=filtercount)
+               widget = NodeWidget(template='monitorweb.templates.node_template')
+               return dict(now=time.ctime(), query=query, fc=filtercount, nodewidget=widget)
        
        def nodeaction_handler(self, tg_exceptions=None):
                """Handle any kind of error."""
                refurl = request.headers.get("Referer",link("pcu"))
                print refurl
+
                # TODO: do this more intelligently...
-               if len(urllib.splitquery(refurl)) > 1:
-                       pcuid = urllib.splitvalue(urllib.splitquery(refurl)[1])[1]
+               uri_fields = urllib.splitquery(refurl)
+               if uri_fields[1] is not None:
+                       val = query_to_dict(uri_fields[1])
+                       if 'pcuid' in val:
+                               pcuid = val['pcuid']
+                       elif 'hostname' in val:
+                               pcuid = FindbadNodeRecord.get_latest_by(hostname=val['hostname']).first().plc_pcuid
                else:
                        pcuid=None
 
@@ -155,7 +223,6 @@ class Root(controllers.RootController):
 
                print pcuid
                return self.pcuview(pcuid, **dict(exceptions=tg_exceptions))
-               #return dict(pcuquery=[], nodequery=[], exceptions=tg_exceptions)
 
        def nodeaction(self, **data):
                for item in data.keys():
@@ -167,8 +234,11 @@ class Root(controllers.RootController):
                        flash("No hostname given in submitted data")
                        return
 
-               if 'submit' in data:
-                       action = data['submit']
+               if 'submit' in data or 'type' in data:
+                       try:
+                               action = data['submit']
+                       except:
+                               action = data['type']
                else:
                        flash("No submit action given in submitted data")
                        return
@@ -178,43 +248,86 @@ class Root(controllers.RootController):
                        ret = reboot.reboot_str(str(hostname))
                        print ret
                        if ret: raise RuntimeError("Error using PCU: " + ret)
+                       flash("Reboot appeared to work.  All at most 5 minutes.  Run ExternalScan to check current status.")
 
-               elif action == "ExternalProbe":
-                       raise RuntimeError("THIS IS A PROBLEM")
-
-               elif action == "DeepProbe":
+               elif action == "ExternalScan":
+                       findbad.externalprobe(str(hostname))
+                       flash("External Scan Successful!")
+               elif action == "InternalScan":
                        findbad.probe(str(hostname))
+                       flash("Internal Scan Successful!")
                else:
                        # unknown action
-                       flash("Unknown action given")
+                       raise RuntimeError("Unknown action given")
                return
 
        # TODO: add form validation
        @expose(template="monitorweb.templates.pcuview")
        @exception_handler(nodeaction_handler,"isinstance(tg_exceptions,RuntimeError)")
-       def pcuview(self, pcuid=None, **data):
+       def pcuview(self, loginbase=None, pcuid=None, hostname=None, **data):
+               sitequery=[]
                pcuquery=[]
                nodequery=[]
-               if 'submit' in data.keys():
+               exceptions = None
+
+               for key in data:
+                       print key, data[key]
+
+               if 'submit' in data.keys() or 'type' in data.keys():
+                       if hostname: data['hostname'] = hostname
                        self.nodeaction(**data)
                if 'exceptions' in data:
                        exceptions = data['exceptions']
-               else:
-                       exceptions = None
 
-               if pcuid:
+               if loginbase:
+                       sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
+                       pcus = {}
+                       for plcnode in site_lb2hn[loginbase]:
+                               for node in FindbadNodeRecord.get_latest_by(hostname=plcnode['hostname']):
+                                       # NOTE: reformat some fields.
+                                       prep_node_for_display(node)
+                                       nodequery += [node]
+                                       if node.plc_pcuid:      # not None
+                                               pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid).first()
+                                               prep_pcu_for_display(pcu)
+                                               pcus[node.plc_pcuid] = pcu
+
+                       for pcuid_key in pcus:
+                               pcuquery += [pcus[pcuid_key]]
+
+               if pcuid and hostname is None:
+                       print "pcuid: %s" % pcuid
                        for pcu in FindbadPCURecord.get_latest_by(plc_pcuid=pcuid):
                                # NOTE: count filter
                                prep_pcu_for_display(pcu)
                                pcuquery += [pcu]
-                       for nodename in pcu.plc_pcu_stats['nodenames']: 
-                               print "query for %s" % nodename
-                               node = FindbadNodeRecord.get_latest_by(hostname=nodename).first()
-                               print "%s" % node
-                               if node:
-                                       prep_node_for_display(node)
-                                       nodequery += [node]
-               return dict(pcuquery=pcuquery, nodequery=nodequery, exceptions=exceptions)
+                       if 'site_id' in pcu.plc_pcu_stats:
+                               sitequery = [HistorySiteRecord.by_loginbase(pcu.loginbase)]
+                               
+                       if 'nodenames' in pcu.plc_pcu_stats:
+                               for nodename in pcu.plc_pcu_stats['nodenames']: 
+                                       print "query for %s" % nodename
+                                       q = FindbadNodeRecord.get_latest_by(hostname=nodename)
+                                       node = q.first()
+                                       print "%s" % node.port_status
+                                       print "%s" % node.to_dict()
+                                       print "%s" % len(q.all())
+                                       if node:
+                                               prep_node_for_display(node)
+                                               nodequery += [node]
+
+               if hostname and pcuid is None:
+                       for node in FindbadNodeRecord.get_latest_by(hostname=hostname):
+                               # NOTE: reformat some fields.
+                               prep_node_for_display(node)
+                               sitequery = [node.site]
+                               nodequery += [node]
+                               if node.plc_pcuid:      # not None
+                                       pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid).first()
+                                       prep_pcu_for_display(pcu)
+                                       pcuquery += [pcu]
+                       
+               return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, exceptions=exceptions)
 
        @expose(template="monitorweb.templates.pculist")
        def pcu(self, filter='all'):
index 7bb4078..df07184 100644 (file)
@@ -57,12 +57,18 @@ a.info span{display: none}
 a.info:hover span{ /*the span will display just on :hover state*/\r
     display:block;\r
     position:absolute;\r
-    top:2em; left:2em; width:15em;\r
+    top:1em; left:-7em; width: 100%;\r
     border:1px solid #AAA;\r
        color:#DDD;\r
     background-color:black; \r
     text-align: center}\r
 \r
+div#legend a:hover span {display: block;\r
+   float: left; width: 30em;\r
+   padding: 5px; margin: 5px; z-index: 100;\r
+   color: #333; background: white;\r
+   font: 10px Verdana, sans-serif; text-align: left;}\r
+\r
 div#links a:hover span {display: block;\r
    /*position: absolute; top: 200px; left: 0; width: 125px;*/\r
    /*position: relative; top: 0px; left: 40; width: 30em;*/\r
@@ -84,14 +90,14 @@ a.right { float: right; }
 #portfiltered { background-color: gold; }\r
 \r
 #dns-DNS-OK { background-color: lightgreen; }\r
-#dns-NOHOSTNAME { background-color: white; }\r
+/*#dns-NOHOSTNAME { background-color: white; }*/\r
 #dns-DNS-MISMATCH { background-color: gold; }\r
 #dns-DNS-NOENTRY { background-color: indianred; }\r
 #dns-NO-DNS-OR-IP { background-color: indianred; }\r
 \r
 #status-NetDown { background-color: lightgrey; }\r
 #status-Not_Run  { background-color: lightgrey; }\r
-#status-ok     { background-color: darkseagreen; }\r
+#status-Ok     { background-color: darkseagreen; }\r
 #status-0     { background-color: darkseagreen; }\r
 #status-error  { background-color: indianred; }\r
 #status-none   { background-color: white; }\r
@@ -196,8 +202,6 @@ h2 {
 span.code {\r
   font-size: 120%;\r
   /*font-weight: bold;*/\r
-  margin: 20 20 20 20;\r
-  padding: 20 20 20 20;\r
 }\r
 \r
 #status_block {\r
index 843906a..eb79269 100644 (file)
@@ -4,6 +4,7 @@ layout_params['page_title'] = "Monitor Node View"
 from monitor.util import diff_time
 from monitor import config
 from time import mktime
+from links import *
 
 def zabbix_event_ack_link(eventid):
        return "http://" + config.MONITOR_HOSTNAME + "/zabbix/acknow.php?eventid=" + str(eventid)
@@ -40,7 +41,7 @@ def zabbix_event_ack_link(eventid):
                        <tbody>
                                <tr py:for="i,node in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
-                                       <td><a href="siteview?loginbase=${node[0]}">${node[0]}</a></td>
+                                       <td><a href="${link('pcuview', loginbase=node[0])}">${node[0]}</a></td>
                                        <td nowrap="true" py:content="node[1]"></td>
                                        <td nowrap='true' id="severity-${node[3]}" py:content="node[2]"></td>
                                        <td nowrap='true' py:content="diff_time(int(node[4]))"></td>
index 669f02f..5b4e7c3 100644 (file)
@@ -28,26 +28,13 @@ from links import *
                        <thead>
                                <tr>
                                        <th mochi:format="int"></th>
-                                       <th mochi:format="str">Site</th>
-                                       <th>Hostname</th>
-                                       <th>ping</th>
-                                       <!--th>ssh</th-->
-                                       <th>pcu</th>
-                                       <th>status</th>
-                                       <th>kernel</th>
-                                       <th>last_contact</th>
+                                       ${nodewidget.display(node=None, header=True)}
                                </tr>
                        </thead>
                        <tbody>
                                <tr py:for="i,node in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
-                                       <td><a href="${link('siteview', loginbase=node.loginbase)}">${node.loginbase}</a></td>
-                                       <td nowrap="true"><a target="_top" href="${link('nodeview', hostname=node.hostname)}" py:content="node.hostname"></a></td>
-                                       <td py:content="node.ping_status"></td>
-                                       <td id="status-${node.pcu_short_status}" py:content="node.pcu_short_status"></td>
-                                       <td py:content="node.observed_status"></td>
-                                       <td nowrap="true" py:content="node.kernel"></td>
-                                       <td py:content="diff_time(node.plc_node_stats['last_contact'])"></td>
+                                       ${nodewidget.display(node=node, header=None)}
                                </tr>
                        </tbody>
                </table>
index 354761c..dc2820e 100644 (file)
@@ -2,6 +2,8 @@
 <?python
 layout_params['page_title'] = "Monitor Node View"
 from monitor.util import diff_time
+from time import mktime
+from pcucontrol.reboot import pcu_name, model_to_object
 from links import *
 ?>
 <html  py:layout="'sitemenu.kid'"
@@ -18,8 +20,8 @@ from links import *
                                        <th>Hostname</th>
                                        <th>ping</th>
                                        <!--th>ssh</th-->
-                                       <th>pcu</th>
                                        <th>kernel</th>
+                                       <th>last_change</th>
                                        <th>last_contact</th>
                                </tr>
                        </thead>
@@ -34,16 +36,47 @@ from links import *
                                                        <span class="icon">${node.hostname}</span></a>
                                        </td>
                                        <td py:content="node.ping_status"></td>
-                                       <td py:if="node.pcu_short_status != 'none'" id="status-${node.pcu_short_status}">
-                                               <a href="${link('pcuview', pcuid=node.plc_node_stats['pcu_ids'])}">${node.pcu_short_status}</a></td>
-                                       <td py:if="node.pcu_short_status == 'none'" id="status-${node.pcu_short_status}">
-                                               ${node.pcu_short_status}</td>
                                        <td nowrap="true" py:content="node.kernel"></td>
+                                       <td py:content="diff_time(mktime(node.history.last_changed.timetuple()))"></td>
                                        <td py:content="diff_time(node.plc_node_stats['last_contact'])"></td>
                                </tr>
                        </tbody>
                </table>
-    <h3 py:if="node.pcu_short_status != 'none'">PCU Status</h3>
+    <h3 py:if="node.pcu is not None">Controlling PCU</h3>
+               <table py:if="node.pcu is not None" id="sortable_table" class="datagrid" border="1" width="100%">
+                       <thead>
+                               <tr>
+                                       <th mochi:format="int"></th>
+                                       <th>PCU Name</th>
+                                       <th>Model</th>
+                                       <th width="80%">Test Results</th>
+                               </tr>
+                       </thead>
+                       <tbody>
+                               <?python pcu = node.pcu ?>
+                               <tr>
+                                       <td></td>
+                                       <td nowrap="true" >
+                                               <a class="ext-link" href="${plc_pcu_uri_id(pcu.plc_pcu_stats['pcu_id'])}">
+                                                       <span class="icon">${pcu_name(pcu.plc_pcu_stats)}</span>
+                                               </a>
+                                       </td>
+                                       <td py:content="pcu.plc_pcu_stats['model']"></td>
+                                       <td width="20%" nowrap='true' align='center' id="status-${node.pcu_short_status}">
+                                               <div id="links">
+                                                       <a class="info" py:if="'error' in node.pcu_short_status" 
+                                                               href="${link('pcuview', pcuid=node.plc_pcuid)}">
+                                                               Error<span><pre>${node.pcu.reboot_trial_status}</pre></span></a>
+                                                       <a py:if="'error' not in node.pcu_short_status and 'none' not in node.pcu_short_status" 
+                                                               href="${link('pcuview', pcuid=node.plc_pcuid)}"
+                                                               py:content="node.pcu_short_status">Reboot Status</a>
+                                                       <span py:if="'none' in node.pcu_short_status" 
+                                                               py:content="node.pcu_short_status">Reboot Status</span>
+                                               </div>
+                                       </td>
+                               </tr>
+                       </tbody>
+               </table>
     <h3>Actions Taken</h3>
   </div>
 
index 99ad41a..f6043dd 100644 (file)
@@ -29,8 +29,7 @@ from links import *
                                        <th mochi:format="int"></th>
                                        <th mochi:format="str">Site</th>
                                        <th>PCU Name</th>
-                                       <th>Missing Fields</th>
-                                       <th>DNS Status</th>
+                                       <th>Config</th>
                                        <th nowrap='true' >Port Status</th>
                                        <th nowrap='true' width="80%">Test Results</th>
                                        <th>Model</th>
@@ -40,7 +39,13 @@ from links import *
                        <tbody>
                                <tr py:for="i,node in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
-                                       <td><a href="${link('siteview', loginbase=node.loginbase)}">${node.loginbase}</a></td>
+                                       <td nowrap='true'>
+                                               <div class='oneline'>
+                                               <a class='left' href="${link('pcuview', loginbase=node.loginbase)}">${node.loginbase}</a>
+                                               <a class='right' href="${plc_site_uri(node.loginbase)}">
+                                                       <img style='display: inline' border='0' src="static/images/extlink.gif" align='right'/></a>
+                                               </div>
+                                       </td>
                                        <td nowrap='true'>
                                                <div class='oneline'>
                                                <a class='left' href="${link('pcuview', pcuid=node.plc_pcuid)}">${pcu_name(node.plc_pcu_stats)}</a>
@@ -48,9 +53,8 @@ from links import *
                                                        <img style='display: inline' border='0' src="static/images/extlink.gif" align='right'/></a>
                                                </div>
                                        </td>
-                                       <td py:content="node.entry_complete"></td>
-                                       <td id="dns-${node.dns_status}" py:content="node.dns_status"></td>
-                                       <td>
+                                       <td py:content="node.entry_complete_str"></td>
+                                       <td nowrap='true'>
                                                <span py:for="port,state in node.ports" 
                                                id="port${state}" py:content="'%s, ' % port">80</span>
                                        </td>
@@ -58,7 +62,7 @@ from links import *
                                                <div id="links">
                                                <a class="info" py:if="'error' in node.status" 
                                                        href="${link('pcuview', pcuid=node.plc_pcuid)}">
-                                                       Error Message<span><pre>${node.reboot_trial_status}</pre></span></a>
+                                                       Error<span><pre>${node.reboot_trial_status}</pre></span></a>
                                                <a py:if="'error' not in node.status" 
                                                        href="${link('pcuview', pcuid=node.plc_pcuid)}"
                                                        py:content="node.status">Reboot Status</a>
index 4eed424..5bf82b8 100644 (file)
@@ -12,16 +12,40 @@ from links import *
          xmlns:mochi="http://www.mochi.org">
 
   <div py:match="item.tag == 'content'">
-    <h3>PCU Status</h3>
-               <table id="sortable_table" class="datagrid" border="1" width="100%">
+    <h3 py:if="len(sitequery) > 0">Site Status</h3>
+               <table py:if="len(sitequery) > 0" id="sub-table" border="1" width="100%">
+                       <thead>
+                               <tr>
+                                       <th>Site name</th>
+                                       <th>Enabled</th>
+                                       <th>Penalty</th>
+                                       <th>Slices/Max</th>
+                                       <th>Nodes/Total</th>
+                                       <th>Status</th>
+                               </tr>
+                       </thead>
+                       <tbody>
+                               <tr py:for="i,site in enumerate(sitequery)" class="${i%2 and 'odd' or 'even'}" >
+                                       <td nowrap="true"><a class="ext-link" href="${plc_site_uri(site.loginbase)}">
+                                                       <span class="icon">${site.loginbase}</span></a>
+                                       </td>
+                                       <td py:content="site.enabled"></td>
+                                       <td>n/a</td>
+                                       <td>${site.slices_used}/${site.slices_total}</td>
+                                       <td>${site.nodes_up} / ${site.nodes_total}</td>
+                                       <td id="site-${site.status}" py:content="diff_time(mktime(site.last_changed.timetuple()))"></td>
+                               </tr>
+                       </tbody>
+               </table>
+    <h3 py:if="len(pcuquery) != 0" >PCU Status</h3>
+               <table py:if="len(pcuquery) != 0" id="sortable_table" class="datagrid" border="1" width="100%">
                        <thead>
                                <tr>
                                        <th mochi:format="int"></th>
-                                       <th mochi:format="str">Site</th>
                                        <th>PCU Name</th>
                                        <th>Missing Fields</th>
                                        <th>DNS Status</th>
-                                       <th>Port Status</th>
+                                       <th nowrap='true'>Port Status</th>
                                        <th width="80%">Test Results</th>
                                        <th>Model</th>
                                        <th>Nodes</th>
@@ -30,10 +54,6 @@ from links import *
                        <tbody>
                                <tr py:for="i,pcu in enumerate(pcuquery)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
-                                       <td><a class="ext-link" href="${plc_site_uri_id(pcu.plc_pcu_stats['site_id'])}">
-                                                       <span class="icon">${pcu.loginbase}</span>
-                                               </a>
-                                       </td>
                                        <td nowrap="true" >
                                                <a class="ext-link" href="${plc_pcu_uri_id(pcu.plc_pcu_stats['pcu_id'])}">
                                                        <span class="icon">${pcu_name(pcu.plc_pcu_stats)}</span>
@@ -41,62 +61,91 @@ from links import *
                                        </td>
                                        <td py:content="pcu.entry_complete"></td>
                                        <td id="dns-${pcu.dns_status}" py:content="pcu.dns_status"></td>
-                                       <td>
+                                       <td nowrap='true'>
                                                <span py:for="port,state in pcu.ports" 
                                                id="port${state}" py:content="'%s, ' % port">80</span>
                                        </td>
-                                       <td width="40" id="status-${pcu.status}"><pre py:content="pcu.reboot_trial_status"></pre></td>
+                                       <td width="40" id="status-${pcu.status}"><pre class="results" py:content="pcu.reboot_trial_status"></pre></td>
                                        <td py:content="pcu.plc_pcu_stats['model']"></td>
                                        <td py:content="len(pcu.plc_pcu_stats['node_ids'])"></td>
                                </tr>
                        </tbody>
                </table>
-       <h4>Convenience Calls</h4>
-               <?python 
-                       if len(pcuquery) == 0: pcu = None
-               ?>
-               <div py:if="pcu is not None" class="code">
-                       <span   py:for="port,state in pcu.ports">
-                                       <span class="code" py:if="port == 22 and state == 'open'">
-                                               ssh -o PasswordAuthentication=yes -o PubkeyAuthentication=no 
-                                               ${pcu.plc_pcu_stats['username']}@${pcu_name(pcu.plc_pcu_stats)}
-                                       </span>
-                                       <span class="code" py:if="port == 23 and state == 'open'">
-                                               telnet ${pcu_name(pcu.plc_pcu_stats)}
-                                       </span>
-                                       <span class="code" py:if="port == 80 and state == 'open'">
-                                               <a href="http://${pcu_name(pcu.plc_pcu_stats)}">http://${pcu_name(pcu.plc_pcu_stats)}</a>
-                                       </span>
-                                       <span class="code" py:if="port == 443 and state == 'open'">
-                                               <a href="https://${pcu_name(pcu.plc_pcu_stats)}">https://${pcu_name(pcu.plc_pcu_stats)}</a>
-                                               <br/>
-                                               /usr/share/monitor/racadm.py -r ${pcu.plc_pcu_stats['ip']} 
-                                                       -u ${pcu.plc_pcu_stats['username']} -p '${pcu.plc_pcu_stats['password']}'
-                                               <br/>
-                                               /usr/share/monitor/pcucontrol/models/hpilo/locfg.pl 
-                                                       -f /usr/share/monitor/pcucontrol/models/hpilo/iloxml/Reset_Server.xml 
-                                                       -s ${pcu_name(pcu.plc_pcu_stats)}
-                                                       -u ${pcu.plc_pcu_stats['username']} 
-                                                       -p '${pcu.plc_pcu_stats['password']} ' | grep MESSAGE" 
-                                       </span>
-                                       <span class="code" py:if="port == 16992 and state == 'open'">
-                                               /usr/share/monitor/pcucontrol/models/intelamt/remoteControl -A 
-                                                       -verbose 'http://${pcu_name(pcu.plc_pcu_stats)}:16992/RemoteControlService' 
-                                                       -user admin -pass '${pcu.plc_pcu_stats['password']}'
-                                       </span>
-                       </span>
-               </div>
-       <h3>Controls</h3>
-               <table id="sortable_table" class="datagrid" border="1" width="100%">
+       <div class="oneline" id="legend" py:if="len(pcuquery) == 0">
+               <em>There no PCUs associated with this host.</em>
+       </div>
+       <div class="oneline" id="legend" py:if="len(pcuquery) > 0">
+               <em>Legend: </em>
+               <a class="info" href="#">DNS Status<span>
+                       <table border="1" align="center" width="100%">
+                               <tr><th colspan="2">Legend for 'DNS Status'</th></tr>
+
+                               <tr><td id="dns-DNS-OK">DNS-OK</td>
+                                       <td>This indicates that the DNS name and registered IP address match.</td>
+                               </tr>
+                               <tr><td id="dns-DNS-MISMATCH">DNS-MISMATCH</td>
+                                       <td>Sometimes, the registered IP and DNS IP address do not match.  
+                                               In these cases it is not clear which is correct, 
+                                               so an error is flagged.</td>
+                               </tr>
+                               <tr><td id="dns-DNS-NOENTRY">DNS-NOENTRY</td>
+                                       <td>While a hostname is provided in the registration, the hostname is not actually registered in DNS.</td>
+                               </tr>
+                               <tr><td id="dns-NOHOSTNAME">NOHOSTNAME</td>
+                                       <td>While we prefer that a hostname be registered, it is not
+                                       strictly required, since simply the IP address, if it is static, is enough to access the PCU.</td>
+                               </tr>
+                       </table>
+                       </span> </a> &nbsp;
+               <a class="info" href="#">Port Status<span>
+               <table border="1" align="center" width="100%">
+                       <tr><th colspan="2">Legend for 'Port Status'</th></tr>
+
+                       <tr><td id="portopen">Open</td>
+                               <td>Green port numbers are believed to be open.</td>
+                       </tr>
+                       <tr><td id="portfiltered">Filtered</td>
+                               <td>Gold port numbers are believed to be filtered or simply offline.</td>
+                       </tr>
+                       <tr><td id="portclosed">Closed</td>
+                               <td>Finally, red ports appear to be closed.</td>
+                       </tr>
+               </table>
+                               </span> </a> &nbsp;
+               <a class="info" href="#">Test Results<span>
+               <table border="1" align="center" width="100%">
+                       <tr><th colspan="2">Legend for 'Test Results'</th></tr>
+
+                       <tr><td id="status-0">OK</td>
+                               <td>The PCU is accessible, and short of actually rebooting the node, everything appears to work.</td>
+                       </tr>
+                       <tr><td id="status-NetDown">NetDown</td>
+                               <td>The PCU is inaccessible from the PlanetLab address block 128.112.139.0/25, or it is simply offline.</td>
+                       </tr>
+                       <tr><td id="status-Not_Run">Not_Run</td>
+                               <td>Previous errors, such as DNS or an incomplete configuration prevented the actual test from begin performed.</td>
+                       </tr>
+                       <tr><td id="status-error">Other Errors</td>
+                               <td>Other errors are reported by the test that are more specific to the block encountered by the script.</td>
+                       </tr>
+               </table>
+                               </span> </a>
+       </div>
+       <h3>Nodes</h3>
+               <p py:if="len(nodequery) == 0">
+                       There are no registered nodes for this site.
+               </p>
+               <table py:if="len(nodequery) > 0" id="sortable_table" class="datagrid" border="1" width="100%">
                        <thead>
                                <tr>
                                        <th mochi:format="int"></th>
                                        <th>Hostname</th>
                                        <th>last_contact</th>
                                        <th>Last_checked</th>
-                                       <th>External Probe</th>
-                                       <th>Internal Probe</th>
-                                       <th>Reboot</th>
+                                       <th nowrap='true'>Port Status</th>
+                                       <th></th>
+                                       <th></th>
+                                       <th></th>
                                </tr>
                        </thead>
                        <tbody>
@@ -108,26 +157,33 @@ from links import *
                                        </td>
                                        <td py:content="diff_time(node.plc_node_stats['last_contact'])"></td>
                                        <td py:content="diff_time(mktime(node.date_checked.timetuple()))"></td>
+                                       <td>
+                                               <span py:for="port,state in node.ports" 
+                                               id="port${state}" py:content="'%s, ' % port">80</span>
+                                       </td>
                                        <td>
                                                <!-- TODO: add some values/code to authenticate the operation.  -->
-                                               <form action="${link('pcuview', pcuid=pcu.plc_pcuid)}" name="nodeaction" method='post'>
+                                               <form action="${link('pcuview', hostname=node.hostname)}" name="externalscan${i}" method='post'>
                                                <input type='hidden' name='hostname' value='${node.hostname}'/> 
-                                               <input type='submit' name='submit' value='ExternalProbe' /> 
+                                               <input type='hidden' name='type' value='ExternalScan' /> 
                                                </form>
+                                               <a onclick='document.externalscan${i}.submit();' href="javascript: void(1);">ExternalScan</a>
                                        </td>
                                        <td>
                                                <!-- TODO: add some values/code to authenticate the operation.  -->
-                                               <form action="${link('pcuview', pcuid=pcu.plc_pcuid)}" name="nodeaction" method='post'>
+                                               <form action="${link('pcuview', hostname=node.hostname)}" name="internalscan${i}" method='post'>
                                                <input type='hidden' name='hostname' value='${node.hostname}'/> 
-                                               <input type='submit' name='submit' value='DeepProbe' /> 
+                                               <input type='hidden' name='type' value='InternalScan' /> 
                                                </form>
+                                               <a onclick='javascript: document.internalscan${i}.submit();' href="javascript: void(1);">InternalScan</a>
                                        </td>
-                                       <td>
+                                       <td py:if="len(pcuquery) > 0">
                                                <!-- TODO: add some values/code to authenticate the operation.  -->
-                                               <form action="${link('pcuview', pcuid=pcu.plc_pcuid)}" name="nodeaction" method='post'>
+                                               <form action="${link('pcuview', pcuid=pcu.plc_pcuid)}" name="reboot${i}" method='post'>
                                                <input type='hidden' name='hostname' value='${node.hostname}'/> 
-                                               <input type='submit' name='submit' value='Reboot' /> 
+                                               <input type='hidden' name='type' value='Reboot' /> 
                                                </form>
+                                               <a onclick='javascript: document.reboot${i}.submit();' href="javascript: void(1);">Reboot</a>
                                        </td>
                                </tr>
                        </tbody>
@@ -137,58 +193,46 @@ from links import *
                </div>
                <div id="status_block" class="flash"
             py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
-       <h3>Legend</h3>
-
-       <table border="1" align="center" width="80%">
-               <tr><th colspan="2">Legend for 'DNS Status'</th></tr>
-
-               <tr><td id="dns-DNS-OK">DNS-OK</td>
-                       <td>This indicates that the DNS name and registered IP address match.</td>
-               </tr>
-               <tr><td id="dns-DNS-MISMATCH">DNS-MISMATCH</td>
-                       <td>Sometimes, the registered IP and DNS IP address do not match.  
-                               In these cases it is not clear which is correct, 
-                               so an error is flagged.</td>
-               </tr>
-               <tr><td id="dns-DNS-NOENTRY">DNS-NOENTRY</td>
-                       <td>While a hostname is provided in the registration, the hostname is not actually registered in DNS.</td>
-               </tr>
-               <tr><td id="dns-NOHOSTNAME">NOHOSTNAME</td>
-                       <td>While we prefer that a hostname be registered, it is not
-                       strictly required, since simply the IP address, if it is static, is enough to access the PCU.</td>
-               </tr>
-               <tr><td>&nbsp;</td></tr>
-       <!--/table>
-       <table border=1-->
-               <tr><th colspan="2">Legend for 'Port Status'</th></tr>
-
-               <tr><td id="portopen">Open</td>
-                       <td>Green port numbers are believed to be open.</td>
-               </tr>
-               <tr><td id="portfiltered">Filtered</td>
-                       <td>Gold port numbers are believed to be filtered or simply offline.</td>
-               </tr>
-               <tr><td id="portclosed">Closed</td>
-                       <td>Finally, red ports appear to be closed.</td>
-               </tr>
-               <tr><td>&nbsp;</td></tr>
-       <!--/table>
-       <table border=1-->
-               <tr><th colspan="2">Legend for 'Test Results'</th></tr>
-
-               <tr><td id="status-0">OK</td>
-                       <td>The PCU is accessible, and short of actually rebooting the node, everything appears to work.</td>
-               </tr>
-               <tr><td id="status-NetDown">NetDown</td>
-                       <td>The PCU is inaccessible from the PlanetLab address block 128.112.139.0/25, or it is simply offline.</td>
-               </tr>
-               <tr><td id="status-Not_Run">Not_Run</td>
-                       <td>Previous errors, such as DNS or an incomplete configuration prevented the actual test from begin performed.</td>
-               </tr>
-               <tr><td id="status-error">Other Errors</td>
-                       <td>Other errors are reported by the test that are more specific to the block encountered by the script.</td>
-               </tr>
-       </table>
+       <h4 py:if="len(pcuquery) > 0">Convenience Calls</h4>
+               <?python 
+                       if len(pcuquery) == 0: pcu = None
+               ?>
+               <div py:if="pcu is not None" class="code">
+                       <span   py:for="port,state in pcu.ports">
+                                       <span class="code" py:if="port == 22 and state == 'open'">
+                                               ssh -o PasswordAuthentication=yes -o PubkeyAuthentication=no 
+                                               ${pcu.plc_pcu_stats['username']}@${pcu_name(pcu.plc_pcu_stats)}
+                                       </span>
+                                       <span class="code" py:if="port == 23 and state == 'open'">
+                                               telnet ${pcu_name(pcu.plc_pcu_stats)}
+                                       </span>
+                                       <span class="code" py:if="port == 80 and state == 'open'">
+                                               <a href="http://${pcu_name(pcu.plc_pcu_stats)}">http://${pcu_name(pcu.plc_pcu_stats)}</a>
+                                       </span>
+                                       <span class="code" py:if="port == 443 and state == 'open'">
+                                               <br/>
+                                               <a href="https://${pcu_name(pcu.plc_pcu_stats)}">https://${pcu_name(pcu.plc_pcu_stats)}</a>
+                                               <br/>
+                                               curl -s --form 'user=${pcu.plc_pcu_stats['username']}' 
+                                                               --form 'password=${pcu.plc_pcu_stats['password']}' 
+                                                               --insecure https://${pcu_name(pcu.plc_pcu_stats)}/cgi-bin/webcgi/index
+                                               <br/>
+                                               /usr/share/monitor/pcucontrol/models/racadm.py -r ${pcu.plc_pcu_stats['ip']} 
+                                                       -u ${pcu.plc_pcu_stats['username']} -p '${pcu.plc_pcu_stats['password']}'
+                                               <br/>
+                                               /usr/share/monitor/pcucontrol/models/hpilo/locfg.pl 
+                                                       -f /usr/share/monitor/pcucontrol/models/hpilo/iloxml/Reset_Server.xml 
+                                                       -s ${pcu_name(pcu.plc_pcu_stats)}
+                                                       -u ${pcu.plc_pcu_stats['username']} 
+                                                       -p '${pcu.plc_pcu_stats['password']} ' | grep MESSAGE" 
+                                       </span>
+                                       <span class="code" py:if="port == 16992 and state == 'open'">
+                                               /usr/share/monitor/pcucontrol/models/intelamt/remoteControl -A 
+                                                       -verbose 'http://${pcu_name(pcu.plc_pcu_stats)}:16992/RemoteControlService' 
+                                                       -user admin -pass '${pcu.plc_pcu_stats['password']}'
+                                       </span>
+                       </span>
+               </div>
 
   </div>
 
index 50b296e..a9b7685 100644 (file)
@@ -1,6 +1,8 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <?python
 layout_params['page_title'] = "Monitor Site List"
+from monitor.util import diff_time
+from time import mktime
 from links import *
 ?>
 <html py:layout="'sitemenu.kid'"
@@ -26,18 +28,28 @@ from links import *
                                <tr>
                                        <th></th>
                                        <th>Site name</th>
-                                       <th>Status</th>
-                                       <th mochi:format="int">Slices (created / max)</th>
-                                       <th mochi:format="int">Nodes (online / registered)</th>
+                                       <th>Enabled</th>
+                                       <th>Penalty</th>
+                                       <th mochi:format="int">Slices/Max</th>
+                                       <th mochi:format="int">Nodes/Total</th>
+                                       <th>Last Change</th>
                                </tr>
                        </thead>
                        <tbody>
                                <tr py:for="i,site in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
-                                       <td nowrap="true"><a href="${link('siteview', loginbase=site.loginbase)}">${site.loginbase}</a></td>
-                                       <td id="site-${site.status}" py:content="site.last_changed"></td>
+                                       <td nowrap="true">
+                                               <div class='oneline'>
+                                               <a class='left' href="${link('pcuview', loginbase=site.loginbase)}">${site.loginbase}</a>
+                                               <a class='right' href="${plc_site_uri(site.loginbase)}">
+                                                       <img style='display: inline' border='0' src="static/images/extlink.gif" align='right'/></a>
+                                               </div>
+                                       </td>
+                                       <td py:content="site.enabled"></td>
+                                       <td>n/a</td>
                                        <td>${site.slices_used}/${site.slices_total}</td>
                                        <td>${site.nodes_up} / ${site.nodes_total}</td>
+                                       <td id="site-${site.status}" py:content="diff_time(mktime(site.last_changed.timetuple()))"></td>
                                </tr>
                        </tbody>
                </table>
index 039a2b7..0999b31 100644 (file)
@@ -2,6 +2,7 @@
 <?python
 layout_params['page_title'] = "Monitor Site View"
 from monitor.util import diff_time
+from time import mktime
 from links import *
 ?>
 <html py:layout="'sitemenu.kid'"
@@ -14,10 +15,11 @@ from links import *
                        <thead>
                                <tr>
                                        <th>Site name</th>
-                                       <th>Status</th>
                                        <th>Enabled</th>
-                                       <th>Slices (created / max)</th>
-                                       <th>Nodes (online / registered)</th>
+                                       <th>Penalty</th>
+                                       <th>Slices/Max</th>
+                                       <th>Nodes/Total</th>
+                                       <th>Status</th>
                                </tr>
                        </thead>
                        <tbody>
@@ -25,15 +27,19 @@ from links import *
                                        <td nowrap="true"><a class="ext-link" href="${plc_site_uri(site.loginbase)}">
                                                        <span class="icon">${site.loginbase}</span></a>
                                        </td>
-                                 <td id="site-${site.status}" py:content="site.last_changed"></td>
-                                 <td py:content="site.enabled"></td>
-                                 <td>${site.slices_used}/${site.slices_total}</td>
-                                 <td>${site.nodes_up} / ${site.nodes_total}</td>
+                                       <td py:content="site.enabled"></td>
+                                       <td>n/a</td>
+                                       <td>${site.slices_used}/${site.slices_total}</td>
+                                       <td>${site.nodes_up} / ${site.nodes_total}</td>
+                                       <td id="site-${site.status}" py:content="diff_time(mktime(site.last_changed.timetuple()))"></td>
                                </tr>
                        </tbody>
                </table>
     <h3>Node List</h3>
-               <table id="sortable_table" class="datagrid" border="1" width="100%">
+               <p py:if="len(nodequery) == 0">
+                       There are no registered nodes for this PCU.
+               </p>
+               <table py:if="len(nodequery) > 0" id="sortable_table" class="datagrid" border="1" width="100%">
                        <thead>
                                <tr>
                                        <th mochi:format="int"></th>
@@ -49,7 +55,7 @@ from links import *
                                <tr py:for="i,node in enumerate(nodequery)" class="${i%2 and 'odd' or 'even'}" >
                                        <td></td>
                                        <td id="node-${node.observed_status}" nowrap="true">
-                                               <a href="${link('nodeview', hostname=node.hostname)}" py:content="node.hostname">your.host.org</a></td>
+                                               <a href="${link('pcuview', hostname=node.hostname)}" py:content="node.hostname">your.host.org</a></td>
                                        <td py:content="node.ping_status"></td>
                                        <td py:if="node.pcu_short_status != 'none'" id="status-${node.pcu_short_status}">
                                                <a href="${link('pcuview', pcuid=node.plc_node_stats['pcu_ids'])}">${node.pcu_short_status}</a></td>