update to db model. now uses automatic history on updates: acts_as_versioned()
authorStephen Soltesz <soltesz@cs.princeton.edu>
Wed, 25 Mar 2009 17:47:08 +0000 (17:47 +0000)
committerStephen Soltesz <soltesz@cs.princeton.edu>
Wed, 25 Mar 2009 17:47:08 +0000 (17:47 +0000)
updated files that used old '*Sync' and old messy Findbad*Records.

17 files changed:
bootman.py
findbad.py
findbadpcu.py
monitor/common.py
monitor/database/info/findbad.py
monitor/database/info/history.py
monitor/reboot.py
monitor/scanapi.py
monitor/wrapper/emailTxt.py
nodebad.py
nodegroups.py
nodeinfo.py
nodequery.py
pcubad.py
web/MonitorWeb/monitorweb/controllers.py
web/MonitorWeb/monitorweb/templates/links.py
web/MonitorWeb/monitorweb/templates/pcuview.kid

index 640f9ee..0cd88ec 100755 (executable)
@@ -82,11 +82,11 @@ class NodeConnection:
                        print "   ERROR:", x
                        print "   Possibly, unable to find valid configuration file"
 
-               if bm_continue and self.config and not self.config.quiet:
+               if bm_continue:
                        for key in bm.VARS.keys():
                                print key, " == ", bm.VARS[key]
                else:
-                       if self.config and not self.config.quiet: print "   Unable to read Node Configuration"
+                       print "   Unable to read Node Configuration"
                
 
        def compare_and_repair_nodekeys(self):
@@ -308,7 +308,8 @@ def reboot(hostname, config=None, forced_action=None):
        # NOTE: Nothing works if the bootcd is REALLY old.
        #       So, this is the first step.
        fbnode = FindbadNodeRecord.get_latest_by(hostname=hostname).to_dict()
-       if fbnode['category'] == "OLDBOOTCD":
+       print fbnode.keys()
+       if fbnode['observed_category'] == "OLDBOOTCD":
                print "...NOTIFY OWNER TO UPDATE BOOTCD!!!"
                args = {}
                args['hostname_list'] = "    %s" % hostname
@@ -594,6 +595,7 @@ def reboot(hostname, config=None, forced_action=None):
                        # actual solution appears to involve removing the bad files, and
                        # continually trying to boot the node.
                        "bminit-cfg-auth-getplc-update-installinit-validate-rebuildinitrd-netcfg-disk-update4-update3-update3-implementerror-update-debug-done",
+                       "bminit-cfg-auth-getplc-installinit-validate-exception-bmexceptmount-exception-noinstall-update-debug-done",
                        ]:
                sequences.update({n : "restart_bootmanager_rins"})
 
index f01f31f..12b0080 100755 (executable)
@@ -12,7 +12,7 @@ from monitor.util import file
 from pcucontrol.util import command
 from monitor import config
 
-from monitor.database.info.model import FindbadNodeRecordSync, FindbadNodeRecord, session
+from monitor.database.info.model import FindbadNodeRecord, session
 
 from monitor.sources import comon
 from monitor.wrapper import plc, plccache
@@ -53,9 +53,10 @@ def checkAndRecordState(l_nodes, cohash):
 
        # CREATE all the work requests
        for nodename in l_nodes:
-               fbnodesync = FindbadNodeRecordSync.findby_or_create(hostname=nodename, if_new_set={'round':0})
-               node_round   = fbnodesync.round
-               fbnodesync.flush()
+               #fbnodesync = FindbadNodeRecordSync.findby_or_create(hostname=nodename, if_new_set={'round':0})
+               #node_round   = fbnodesync.round
+               node_round = global_round - 1
+               #fbnodesync.flush()
 
                if node_round < global_round or config.force:
                        # recreate node stats when refreshed
@@ -86,16 +87,16 @@ def checkAndRecordState(l_nodes, cohash):
                        print "All results collected."
                        break
 
-       print FindbadNodeRecordSync.query.count()
+       #print FindbadNodeRecordSync.query.count()
        print FindbadNodeRecord.query.count()
        session.flush()
 
 def main():
        global global_round
 
-       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
 
        if config.increment:
                # update global round number to force refreshes across all nodes
@@ -145,8 +146,9 @@ def main():
 
        if config.increment:
                # update global round number to force refreshes across all nodes
-               fbsync.round = global_round
-               fbsync.flush()
+               #fbsync.round = global_round
+               #fbsync.flush()
+               pass
 
        return 0
 
index d00d7f7..c6e6a8f 100755 (executable)
@@ -14,7 +14,7 @@ import threading
 
 import monitor
 from monitor import config
-from monitor.database.info.model import FindbadPCURecordSync, FindbadPCURecord, session
+from monitor.database.info.model import FindbadPCURecord, session
 from monitor import database
 from monitor import util 
 from monitor.wrapper import plc, plccache
@@ -43,10 +43,11 @@ def checkPCUs(l_pcus, cohash):
        # CREATE all the work requests
        for pcuname in l_pcus:
                pcu_id = int(pcuname)
-               fbnodesync = FindbadPCURecordSync.findby_or_create(plc_pcuid=pcu_id, if_new_set={'round' : 0})
-               fbnodesync.flush()
+               #fbnodesync = FindbadPCURecordSync.findby_or_create(plc_pcuid=pcu_id, if_new_set={'round' : 0})
+               #fbnodesync.flush()
 
-               node_round   = fbnodesync.round
+               #node_round   = fbnodesync.round
+               node_round   = global_round - 1
                if node_round < global_round or config.force:
                        # recreate node stats when refreshed
                        #print "%s" % nodename
@@ -75,7 +76,7 @@ def checkPCUs(l_pcus, cohash):
                        print "All results collected."
                        break
 
-       print FindbadPCURecordSync.query.count()
+       #print FindbadPCURecordSync.query.count()
        print FindbadPCURecord.query.count()
        session.flush()
 
@@ -86,10 +87,10 @@ def main():
        l_pcus = plccache.l_pcus
        cohash = {}
 
-       fbsync = FindbadPCURecordSync.findby_or_create(plc_pcuid=0, 
-                                                                                       if_new_set={'round' : global_round})
+       #fbsync = FindbadPCURecordSync.findby_or_create(plc_pcuid=0, 
+                                                                                       #if_new_set={'round' : global_round})
 
-       global_round = fbsync.round
+       #global_round = fbsync.round
        api = plc.getAuthAPI()
 
        if config.site is not None:
@@ -139,8 +140,8 @@ def main():
 
        if config.increment:
                # update global round number to force refreshes across all nodes
-               fbsync.round = global_round
-               fbsync.flush()
+               #fbsync.round = global_round
+               #fbsync.flush()
                session.flush()
 
        return 0
index 6f88051..0f6dd40 100644 (file)
@@ -224,17 +224,17 @@ def email_exception(content=None):
 
 def changed_lessthan(last_changed, days):
        if datetime.now() - last_changed <= timedelta(days):
-               print "last changed less than %s" % timedelta(days)
+               #print "last changed less than %s" % timedelta(days)
                return True
        else:
-               print "last changed more than %s" % timedelta(days)
+               #print "last changed more than %s" % timedelta(days)
                return False
 
 def changed_greaterthan(last_changed, days):
        if datetime.now() - last_changed > timedelta(days):
-               print "last changed more than %s" % timedelta(days)
+               #print "last changed more than %s" % timedelta(days)
                return True
        else:
-               print "last changed less than %s" % timedelta(days)
+               #print "last changed less than %s" % timedelta(days)
                return False
        
index 66859b1..b437842 100644 (file)
@@ -4,54 +4,58 @@ from elixir import String, Integer as Int, DateTime, PickleType, Boolean
 from datetime import datetime,timedelta
 import elixir
 import traceback
+from elixir.ext.versioned import *
 
 from monitor.database.dborm import mon_metadata, mon_session
 __metadata__ = mon_metadata
 __session__  = mon_session
 
 
-class FindbadNodeRecordSync(Entity):
-       hostname = Field(String(250),primary_key=True) #,alternateMethodName='by_hostname')
-       round    = Field(Int,default=0)
+#class FindbadNodeRecordSync(Entity):
+#      hostname = Field(String(250),primary_key=True) #,alternateMethodName='by_hostname')
+#      round    = Field(Int,default=0)
        
-class FindbadPCURecordSync(Entity):
-       plc_pcuid = Field(Int,primary_key=True) #,alternateMethodName='by_pcuid')
-       round     = Field(Int,default=0)
+#class FindbadPCURecordSync(Entity):
+#      plc_pcuid = Field(Int,primary_key=True) #,alternateMethodName='by_pcuid')
+#      round     = Field(Int,default=0)
 
 class FindbadNodeRecord(Entity):
        @classmethod
        def get_all_latest(cls):
-               fbsync = FindbadNodeRecordSync.get_by(hostname="global")
-               if fbsync:
-                       return cls.query.filter_by(round=fbsync.round)
-               else:
-                       return []
+               return cls.query.all()
+               #fbsync = FindbadNodeRecordSync.get_by(hostname="global")
+               #if fbsync:
+               #       return cls.query.filter_by(round=fbsync.round)
+               #else:
+               #       return []
 
        @classmethod
        def get_latest_by(cls, **kwargs):
-               fbsync = FindbadNodeRecordSync.get_by(hostname="global")
-               if fbsync:
-                       kwargs['round'] = fbsync.round
-                       return cls.query.filter_by(**kwargs).order_by(FindbadNodeRecord.date_checked.desc())
-               else:
-                       return []
+               return cls.query.filter_by(**kwargs).first()
+               #fbsync = FindbadNodeRecordSync.get_by(hostname="global")
+               #if fbsync:
+               #       kwargs['round'] = fbsync.round
+               #       return cls.query.filter_by(**kwargs).order_by(FindbadNodeRecord.date_checked.desc())
+               #else:
+               #       return []
 
        @classmethod
        def get_latest_n_by(cls, n=3, **kwargs):
-               fbsync = FindbadNodeRecordSync.get_by(hostname="global")
-               kwargs['round'] = fbsync.round
-               ret = []
-               for i in range(0,n):
-                       kwargs['round'] = kwargs['round'] - i
-                       f = cls.query.filter_by(**kwargs).first()
-                       if f:
-                               ret.append(f)
-               return ret
+               return cls.query.filter_by(**kwargs)
+               #fbsync = FindbadNodeRecordSync.get_by(hostname="global")
+               #kwargs['round'] = fbsync.round
+               #ret = []
+               #for i in range(0,n):
+               #       kwargs['round'] = kwargs['round'] - i
+               #       f = cls.query.filter_by(**kwargs).first()
+               #       if f:
+               #               ret.append(f)
+               #return ret
 
 # ACCOUNTING
        date_checked = Field(DateTime,default=datetime.now)
        round = Field(Int,default=0)
-       hostname = Field(String,default=None)
+       hostname = Field(String,primary_key=True,default=None)
        loginbase = Field(String)
 
 # INTERNAL
@@ -79,23 +83,19 @@ class FindbadNodeRecord(Entity):
        observed_category = Field(String,default=None)
        observed_status = Field(String,default=None)
 
+       acts_as_versioned(ignore=['date_checked'])
        # NOTE: this is the child relation
        #action = ManyToOne('ActionRecord', required=False)
 
 class FindbadPCURecord(Entity):
        @classmethod
        def get_all_latest(cls):
-               fbsync = FindbadPCURecordSync.get_by(plc_pcuid=0)
-               if fbsync:
-                       return cls.query.filter_by(round=fbsync.round)
-               else:
-                       return []
+               return cls.query.all()
 
        @classmethod
        def get_latest_by(cls, **kwargs):
-               fbsync = FindbadPCURecordSync.get_by(plc_pcuid=0)
-               kwargs['round'] = fbsync.round
-               return cls.query.filter_by(**kwargs).order_by(FindbadPCURecord.date_checked.desc())
+               return cls.query.filter_by(**kwargs)
+
 # ACCOUNTING
        date_checked = Field(DateTime)
        round = Field(Int,default=0)
@@ -110,3 +110,5 @@ class FindbadPCURecord(Entity):
 # INTERNAL
 # INFERRED
        reboot_trial_status = Field(String)
+
+       acts_as_versioned(ignore=['date_checked'])
index 6f04c44..3c5842a 100644 (file)
@@ -1,6 +1,8 @@
 from elixir import Entity, Field, OneToMany, ManyToOne, ManyToMany
 from elixir import options_defaults, using_options, setup_all
 from elixir import String, Integer as Int, DateTime, Boolean
+from elixir.ext.versioned import *
+
 from datetime import datetime,timedelta
 
 from monitor.database.dborm import mon_metadata, mon_session
@@ -13,6 +15,7 @@ class HistoryNodeRecord(Entity):
        last_checked = Field(DateTime,default=datetime.now)
        last_changed = Field(DateTime,default=datetime.now)
        status = Field(String,default="unknown")
+       acts_as_versioned(ignore=['last_changed', 'last_checked'])
 
        @classmethod
        def by_hostname(cls, hostname):
@@ -28,10 +31,13 @@ class HistoryPCURecord(Entity):
        last_valid = Field(DateTime,default=None)
        valid  = Field(String,default="unknown")
 
+       acts_as_versioned(ignore=['last_changed', 'last_checked'])
+
        @classmethod
        def by_pcuid(cls, pcuid):
                return cls.query.filter_by(pcuid=pcuid).first()
 
+
 class HistorySiteRecord(Entity):
        loginbase = Field(String(250),primary_key=True)
 
@@ -57,6 +63,7 @@ class HistorySiteRecord(Entity):
 
        penalty_level   = Field(Int, default=0)
        penalty_applied = Field(Boolean, default=False)
+       acts_as_versioned(ignore=['last_changed', 'last_checked'])
 
        @classmethod
        def by_loginbase(cls, loginbase):
index 289fb47..15d5c52 100755 (executable)
@@ -45,7 +45,7 @@ def get_pcu_values(pcu_id):
        from monitor.database.info.model import FindbadPCURecord
        print "pcuid: %s" % pcu_id
        try:
-               pcurec = FindbadPCURecord.get_latest_by(plc_pcuid=pcu_id).first()
+               pcurec = FindbadPCURecord.get_latest_by(plc_pcuid=pcu_id)
                if pcurec:
                        values = pcurec.to_dict()
                else:
index e1a09e8..963822d 100644 (file)
@@ -112,7 +112,7 @@ class ScanInterface(object):
        syncclass = None
        primarykey = 'hostname'
 
-       def __init__(self, round):
+       def __init__(self, round=1):
                self.round = round
                self.count = 1
 
@@ -133,22 +133,24 @@ class ScanInterface(object):
                try:
                        if values is None:
                                return
-
-                       fbnodesync = self.syncclass.findby_or_create(
-                                                                                               if_new_set={'round' : self.round},
+                       
+                       if self.syncclass:
+                               fbnodesync = self.syncclass.findby_or_create(
+                                                                                               #if_new_set={'round' : self.round},
                                                                                                **{ self.primarykey : nodename})
                        # NOTE: This code will either add a new record for the new self.round, 
                        #       OR it will find the previous value, and update it with new information.
                        #       The data that is 'lost' is not that important, b/c older
                        #       history still exists.  
                        fbrec = self.recordclass.findby_or_create(
-                                               **{'round':self.round, self.primarykey:nodename})
+                                               **{ self.primarykey:nodename})
 
                        fbrec.set( **values ) 
 
                        fbrec.flush()
-                       fbnodesync.round = self.round
-                       fbnodesync.flush()
+                       if self.syncclass:
+                               fbnodesync.round = self.round
+                               fbnodesync.flush()
 
                        print "%d %s %s" % (self.count, nodename, values)
                        self.count += 1
@@ -160,7 +162,8 @@ class ScanInterface(object):
 
 class ScanNodeInternal(ScanInterface):
        recordclass = FindbadNodeRecord
-       syncclass = FindbadNodeRecordSync
+       #syncclass = FindbadNodeRecordSync
+       syncclass = None
        primarykey = 'hostname'
 
        def collectNMAP(self, nodename, cohash):
@@ -375,9 +378,9 @@ EOF                         """)
                return (nodename, values)
 
 def internalprobe(hostname):
-       fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
-                                                                                                       if_new_set={'round' : 1})
-       scannode = ScanNodeInternal(fbsync.round)
+       #fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
+       #                                                                                               if_new_set={'round' : 1})
+       scannode = ScanNodeInternal() # fbsync.round)
        try:
                (nodename, values) = scannode.collectInternal(hostname, {})
                scannode.record(None, (nodename, values))
@@ -388,9 +391,9 @@ def internalprobe(hostname):
                return False
 
 def externalprobe(hostname):
-       fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
-                                                                                                       if_new_set={'round' : 1})
-       scannode = ScanNodeInternal(fbsync.round)
+       #fbsync = FindbadNodeRecordSync.findby_or_create(hostname="global", 
+       #                                                                                               if_new_set={'round' : 1})
+       scannode = ScanNodeInternal() # fbsync.round)
        try:
                (nodename, values) = scannode.collectNMAP(hostname, {})
                scannode.record(None, (nodename, values))
@@ -402,7 +405,7 @@ def externalprobe(hostname):
 
 class ScanPCU(ScanInterface):
        recordclass = FindbadPCURecord
-       syncclass = FindbadPCURecordSync
+       syncclass = None
        primarykey = 'plc_pcuid'
 
        def collectInternal(self, pcuname, cohash):
index 675068a..385ac63 100644 (file)
@@ -231,12 +231,20 @@ This notice is simply to test whether notices work.
 
 Thank you very much for your help!
        """)
-       offline_notice=("""Host %(hostname)s is offline""",
+       retry_bootman=("""Running BootManager on %(hostname)s""",
        """
 This notice is simply to let you know that:
     %(hostname)s
 
-is offline and or non-operational.  Please investigate, thank you very much for your help!
+appears stuck in a debug mode.  To try to correct this, we're trying to rerun BootManager.py.  
+If any action is needed from you, you will recieve additional notices.  Thank you!
+       """)
+       down_notice=("""Host %(hostname)s is down""",
+       """
+This notice is simply to let you know that:
+    %(hostname)s
+
+is down, disconnected from the network and/or non-operational.  Please investigate, thank you very much for your help!
        """)
 
        clear_penalty=("""All penalties have been cleared from site %(loginbase)s""",
index 90c3be0..a0490e4 100755 (executable)
@@ -44,35 +44,49 @@ def check_node_state(rec, node):
                boot_state = "unknown"
                last_contact = None
 
-       if node_state == 'DOWN' and ( node.status == 'online' or node.status == 'good' ):
+       # NOTE: 'DOWN' and 'DEBUG'  are temporary states, so only need
+       #                       'translations' into the node.status state
+       #               'BOOT' is a permanent state, but we want it to have a bit of
+       #                       hysteresis (less than 0.5 days)
+
+       #################################################################3
+       # "Translate" the findbad states into nodebad status.
+
+       if node_state == 'DOWN' and ( node.status != 'offline' and node.status != 'down' ) and boot_state != 'disable' :
                print "changed status from %s to offline" % node.status
                node.status = 'offline'
                node.last_changed = datetime.now()
 
-       if node_state == 'BOOT' and changed_lessthan(node.last_changed, 0.5) and node.status != 'online':
+       if node_state == 'DEBUG' and node.status != 'monitordebug':
+               print "changed status from %s to monitordebug" % (node.status)
+               node.status = "monitordebug"
+               node.last_changed = datetime.now()
+
+       if node_state == 'BOOT' and node.status != 'online' and node.status != 'good':
                print "changed status from %s to online" % node.status
                node.status = 'online'
                node.last_changed = datetime.now()
 
+       #################################################################3
+       # Switch temporary hystersis states into their 'firm' states.
+
        if node.status == 'online' and changed_greaterthan(node.last_changed, 0.5):
-               #send thank you notice, or on-line notice.
                print "changed status from %s to good" % node.status
                node.status = 'good'
                # NOTE: do not reset last_changed, or you lose how long it's been up.
 
-       #if node.status == 'offline' and changed_greaterthan(node.last_changed, 1): #  and pcu.status == 'good' 
-       #       # attempt reboots
-       #       pass
-       #if node.status == 'offline' and changed_greaterthan(node.last_changed, 1.5): # and node.has_pcu
-       #       # send PCU failure message
-       #       pass
-
        if node.status == 'offline' and changed_greaterthan(node.last_changed, 2):
                print "changed status from %s to down" % node.status
-               # send down node notice
                node.status = 'down'
-               node.last_changed = datetime.now()
+               # NOTE: do not reset last_changed, or you lose how long it's been down.
+
+       if node.status == 'monitordebug' and changed_greaterthan(node.last_changed, 14):
+               print "changed status from %s to down" % node.status
+               node.status = 'down'
+               # NOTE: do not reset last_changed, or you lose how long it's been down.
+               #node.last_changed = datetime.now()
 
+       # extreme cases of offline nodes
        if ( boot_state == 'disabled' or last_contact == None ) and \
                        changed_greaterthan(node.last_changed, 2*30) and \
                        node.status != 'down':
@@ -92,7 +106,7 @@ def checkAndRecordState(l_nodes, l_plcnodes):
 
                try:
                        # Find the most recent record
-                       noderec = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname==nodename).order_by(FindbadNodeRecord.date_checked.desc()).first()
+                       noderec = FindbadNodeRecord.get_latest_by(hostname=nodename)
                except:
                        print "COULD NOT FIND %s" % nodename
                        import traceback
index d6beb54..056f5b8 100755 (executable)
@@ -121,7 +121,7 @@ def main():
                i = 1
                for node in nodelist:
                        print "%-2d" % i, 
-                       fbrec = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname==node['hostname']).order_by(FindbadNodeRecord.date_checked.desc()).first()
+                       fbrec = FindbadNodeRecord.get_latest_by(hostname=node['hostname'])
                        fbdata = fbrec.to_dict()
                        print nodegroup_display(node, fbdata, config)
                        i += 1
index 3248707..e599d24 100755 (executable)
@@ -7,7 +7,7 @@ from monitor import *
 from monitor import util
 from monitor import parser as parsermodule
 
-from monitor import database
+from monitor.database.info.model import *
 from monitor import reboot
 
 import time
index 7a53df0..1f41ceb 100755 (executable)
@@ -16,7 +16,7 @@ import string
 from monitor.wrapper import plc, plccache
 api = plc.getAuthAPI()
 
-from monitor.database.info.model import FindbadNodeRecordSync, FindbadNodeRecord, FindbadPCURecord, session
+from monitor.database.info.model import FindbadNodeRecord, FindbadPCURecord, session
 from monitor import util
 from monitor import config
 
@@ -412,7 +412,7 @@ def main():
 
                try:
                        # Find the most recent record
-                       fb_noderec = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname==node).order_by(FindbadNodeRecord.date_checked.desc()).first()
+                       fb_noderec = FindbadNodeRecord.get_latest_by(hostname=node) 
                except:
                        print traceback.print_exc()
                        pass
index 5d14475..0ce1a9e 100755 (executable)
--- a/pcubad.py
+++ b/pcubad.py
@@ -104,7 +104,7 @@ def checkAndRecordState(l_pcus, l_plcpcus):
 
                try:
                        # Find the most recent record
-                       pcurec = FindbadPCURecord.query.filter(FindbadPCURecord.plc_pcuid==pcuname).order_by(FindbadPCURecord.date_checked.desc()).first()
+                       pcurec = FindbadPCURecord.query.filter(FindbadPCURecord.plc_pcuid==pcuname).first()
                except:
                        print "COULD NOT FIND FB record for %s" % reboot.pcu_name(d_pcu)
                        import traceback
index 617c46a..1178aa1 100644 (file)
@@ -292,12 +292,14 @@ class Root(controllers.RootController):
                        exceptions = data['exceptions']
 
                if loginbase:
-                       actions = ActionRecord.query.filter_by(loginbase=loginbase).order_by(ActionRecord.date_created.desc())
+                       actions = ActionRecord.query.filter_by(loginbase=loginbase
+                                                       ).filter(ActionRecord.date_created >= datetime.now() - timedelta(7)
+                                                       ).order_by(ActionRecord.date_created.desc())
                        actions = [ a for a in actions ]
                        sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
                        pcus = {}
                        for plcnode in site_lb2hn[loginbase]:
-                               for node in FindbadNodeRecord.get_latest_by(hostname=plcnode['hostname']):
+                                       node = FindbadNodeRecord.get_latest_by(hostname=plcnode['hostname'])
                                        # NOTE: reformat some fields.
                                        prep_node_for_display(node)
                                        nodequery += [node]
@@ -311,18 +313,17 @@ class Root(controllers.RootController):
 
                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]
+                       pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
+                       # NOTE: count filter
+                       prep_pcu_for_display(pcu)
+                       pcuquery += [pcu]
                        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()
+                                       node = FindbadNodeRecord.get_latest_by(hostname=nodename)
                                        print "%s" % node.port_status
                                        print "%s" % node.to_dict()
                                        print "%s" % len(q.all())
@@ -331,13 +332,13 @@ class Root(controllers.RootController):
                                                nodequery += [node]
 
                if hostname and pcuid is None:
-                       for node in FindbadNodeRecord.get_latest_by(hostname=hostname):
+                               node = 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()
+                                       pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid)
                                        prep_pcu_for_display(pcu)
                                        pcuquery += [pcu]
                        
index 6b47bb1..2bc6917 100644 (file)
@@ -2,6 +2,8 @@ from monitor import config
 import turbogears as tg
 import urllib
 
+def plc_mail_uri(ticketid):
+       return config.RT_WEB_SERVER + "/Ticket/Display.html?id=" + str(ticketid)
 def plc_node_uri(hostname):
        return "https://" + config.PLC_WWW_HOSTNAME + "/db/nodes/index.php?nodepattern=" + str(hostname)
 def plc_site_uri(loginbase):
index a39810e..694fc4d 100644 (file)
@@ -194,7 +194,7 @@ from links import *
                <div id="status_block" class="flash"
             py:if="value_of('tg_flash', None)" py:content="tg_flash"></div>
 
-       <h4>Recent Actions</h4>
+       <h4>Actions Over the Last Week</h4>
                <p py:if="actions and len(actions) == 0">
                        There are no recent actions taken for this site.
                </p>
@@ -223,8 +223,9 @@ from links import *
                                        </td>
                                        <!--td py : content="diff_time(mktime(node.date_checked.timetuple()))"></td-->
                                        <td py:content="act.action_type"></td>
-                                       <td py:content="act.message_id"></td>
-                                       <td py:content="act.error_string"></td>
+                                       <td><a class="ext-link" href="${plc_mail_uri(act.message_id)}">
+                                                       <span class="icon">${act.message_id}</span></a></td>
+                                       <td><pre py:content="act.error_string"></pre></td>
                                </tr>
                        </tbody>
                </table>