basic display of nodes, pcus, and sites.
Stephen Soltesz [Tue, 2 Dec 2008 19:31:20 +0000 (19:31 +0000)]
updates to various files needed to enable this.

12 files changed:
monitor/database/info/history.py
monitor/util/__init__.py
monitor/wrapper/plccache.py
sitebad.py
web/MonitorWeb/dev.cfg
web/MonitorWeb/monitorweb/controllers.py
web/MonitorWeb/monitorweb/model.py
web/MonitorWeb/monitorweb/static/css/style.css
web/MonitorWeb/monitorweb/templates/nodelist.kid
web/MonitorWeb/monitorweb/templates/pculist.kid
web/MonitorWeb/monitorweb/templates/sitemenu.kid
web/MonitorWeb/prod.cfg [new file with mode: 0644]

index c871c78..dc53860 100644 (file)
@@ -40,8 +40,14 @@ class HistorySiteRecord(Entity):
 
        nodes_total = Field(Int,default=0)
        nodes_up = Field(Int,default=0)
+       slices_total = Field(Int,default=0)
        slices_used = Field(Int,default=0)
 
+       # all nodes offline and never-contact.
+       new = Field(Boolean,default=False)
+
+       enabled = Field(Boolean,default=False)
+
        status = Field(String,default="unknown")
 
        @classmethod
index f204047..8986f7f 100644 (file)
@@ -1 +1,34 @@
 __all__=["writepid","removepid","daemonize"]
+
+import time
+import math
+
+def diff_time(timestamp, abstime=True):
+       now = time.time()
+       if timestamp == None:
+               return "unknown"
+       if abstime:
+               diff = now - timestamp
+       else:
+               diff = timestamp
+       # return the number of seconds as a difference from current time.
+       t_str = ""
+       if diff < 60: # sec in min.
+               t = diff / 1
+               t_str = "%s sec ago" % int(math.ceil(t))
+       elif diff < 60*60: # sec in hour
+               t = diff / (60)
+               t_str = "%s min ago" % int(math.ceil(t))
+       elif diff < 60*60*24: # sec in day
+               t = diff / (60*60)
+               t_str = "%s hrs ago" % int(math.ceil(t))
+       elif diff < 60*60*24*14: # sec in week
+               t = diff / (60*60*24)
+               t_str = "%s days ago" % int(math.ceil(t))
+       elif diff <= 60*60*24*30: # approx sec in month
+               t = diff / (60*60*24*7)
+               t_str = "%s wks ago" % int(math.ceil(t))
+       elif diff > 60*60*24*30: # approx sec in month
+               t = diff / (60*60*24*30)
+               t_str = "%s mnths ago" % int(t)
+       return t_str
index 978e6bb..d4cfbbc 100755 (executable)
@@ -87,7 +87,7 @@ def init():
        api = plc.getCachedAuthAPI()
        l_sites = api.GetSites({'peer_id':None}, 
                                                        ['login_base', 'site_id', 'abbreviated_name', 'latitude', 
-                                                       'longitude', 'max_slices', 'slice_ids', 'node_ids' ])
+                                                       'longitude', 'max_slices', 'slice_ids', 'node_ids', 'enabled' ])
        l_nodes = api.GetNodes({'peer_id':None}, 
                                                        ['hostname', 'node_id', 'ports', 'site_id', 'version', 'last_updated', 
                                                         'date_created', 'last_contact', 'pcu_ids', 'nodenetwork_ids'])
index 12ae8c8..781cab6 100755 (executable)
@@ -10,7 +10,7 @@ from monitor import database
 from pcucontrol  import reboot
 from monitor import parser as parsermodule
 from monitor import config
-from monitor.database import HistorySiteRecord, FindbadNodeRecord
+from monitor.database.info.model import HistorySiteRecord, FindbadNodeRecord, session
 from monitor.wrapper import plc, plccache
 from monitor.const import MINUP
 
@@ -32,6 +32,19 @@ def main(config):
        
        checkAndRecordState(l_sites, l_plcsites)
 
+def getnewsite(nodelist):
+       new = True
+       for node in nodelist:
+               try:
+                       noderec = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname==node['hostname']).order_by(FindbadNodeRecord.date_checked.desc()).first()
+                       if noderec is not None and \
+                               noderec.plc_node_stats['last_contact'] != None:
+                               new = False
+               except:
+                       import traceback
+                       print traceback.print_exc()
+       return new
+
 def getnodesup(nodelist):
        up = 0
        for node in nodelist:
@@ -62,9 +75,12 @@ def checkAndRecordState(l_sites, l_plcsites):
                        pf = HistorySiteRecord.findby_or_create(loginbase=sitename)
 
                        pf.last_checked = datetime.now()
+                       pf.slices_total = d_site['max_slices']
                        pf.slices_used = len(d_site['slice_ids'])
                        pf.nodes_total = len(lb2hn[sitename])
                        pf.nodes_up = getnodesup(lb2hn[sitename])
+                       pf.new = getnewsite(lb2hn[sitename])
+                       pf.enabled = d_site['enabled']
 
                        if pf.nodes_up >= MINUP:
                                if pf.status != "good": pf.last_changed = datetime.now()
@@ -76,7 +92,10 @@ def checkAndRecordState(l_sites, l_plcsites):
                        count += 1
                        print "%d %15s slices(%2s) nodes(%2s) up(%2s) %s" % (count, sitename, pf.slices_used, 
                                                                                        pf.nodes_total, pf.nodes_up, pf.status)
+                       pf.flush()
+
        print HistorySiteRecord.query.count()
+       session.flush()
 
        return True
 
index f1e675b..d5915c7 100644 (file)
@@ -30,12 +30,12 @@ server.environment="development"
 autoreload.package="monitorweb"
 
 
-server.socket_host="127.0.0.1"
+server.socket_host="monitor.planet-lab.org"
 server.socket_port=8080
-server.webpath="/monitor/"
-base_url_filter.on = False
-base_url_filter.base_url = "http://127.0.0.1:8080/monitor"
-base_url_filter.use_x_forwarded_host = True
+#server.webpath="/monitor/"
+#base_url_filter.on = False
+#base_url_filter.base_url = "http://127.0.0.1:8080/monitor"
+#base_url_filter.use_x_forwarded_host = True
 
 # Auto-Reload after code modification
 # autoreload.on = True
index d2978da..d4553dc 100644 (file)
@@ -51,14 +51,18 @@ class Root(controllers.RootController):
                                        node.pcu_status = pcu.reboot_trial_status
                                else:
                                        node.pcu_status = "nodata"
+                               node.pcu_short_status = format_pcu_shortstatus(pcu)
+
                        else:
                                node.pcu_status = "nopcu"
+                               node.pcu_short_status = "none"
 
                        if node.kernel_version:
                                node.kernel = node.kernel_version.split()[2]
                        else:
                                node.kernel = ""
 
+
                        # NOTE: count filters
                        if node.observed_status != 'DOWN':
                                filtercount[node.observed_status] += 1
@@ -102,7 +106,6 @@ class Root(controllers.RootController):
                        else:
                                filtercount['pending'] += 1
                                
-                       print reboot.pcu_name(node.plc_pcu_stats)
                        node.ports = format_ports(node)
                        node.status = format_pcu_shortstatus(node)
 
@@ -123,10 +126,32 @@ class Root(controllers.RootController):
                                
                return dict(query=query, fc=filtercount)
 
-       @expose(template="monitorweb.templates.pculist")
+       @expose(template="monitorweb.templates.sitelist")
        def site(self, filter='all'):
-               filtercount = {'ok' : 0, 'NetDown': 0, 'Not_Run' : 0, 'pending' : 0, 'all' : 0}
-               return dict(query=[], fc=filtercount)
+               filtercount = {'good' : 0, 'down': 0, 'new' : 0, 'pending' : 0, 'all' : 0}
+               fbquery = HistorySiteRecord.query.all()
+               query = []
+               for site in fbquery:
+                       # count filter
+                       filtercount['all'] += 1
+                       if site.new and site.slices_used == 0 and not site.enabled:
+                               filtercount['new'] += 1
+                       elif not site.enabled:
+                               filtercount['pending'] += 1
+                       else:
+                               filtercount[site.status] += 1
+
+                       # apply filter
+                       if filter == "all":
+                               query.append(site)
+                       elif filter == 'new' and site.new and site.slices_used == 0 and not site.enabled:
+                               query.append(site)
+                       elif filter == "pending" and not site.enabled:
+                               query.append(site)
+                       elif filter == site.status:
+                               query.append(site)
+                               
+               return dict(query=query, fc=filtercount)
 
        @expose(template="monitorweb.templates.pculist")
        def action(self, filter='all'):
index b570416..f3fda87 100644 (file)
@@ -11,6 +11,7 @@ from elixir import String, Unicode, Integer, DateTime
 
 options_defaults['autosetup'] = False
 
+from monitor.database.info.model import *
 
 # your data model
 
index 59053a2..072c484 100644 (file)
@@ -10,18 +10,20 @@ html, body {
   padding: 0;\r
 }\r
 \r
-td, th {padding:2px;border:none;}\r
+td, th {padding:1px;border:none;}\r
 tr th {text-align:left;background-color:#f0f0f0;color:#333;}\r
 tr.odd td {background-color:#edf3fe;}\r
 tr.even td {background-color:#fff;}\r
 \r
 #header {\r
-  height: 80px;\r
-  width: 777px;\r
-  background: blue URL('../images/header_inner.png') no-repeat;\r
+  height: 40px;\r
+  width: 780px;\r
+  /*background: blue URL('../images/header_inner.png') no-repeat;*/\r
   border-left: 1px solid #aaa;\r
   border-right: 1px solid #aaa;\r
   margin: 0 auto 0 auto;\r
+  text-align: center;\r
+  font-size: 180%;\r
 }\r
 \r
 a.link, a, a.active {\r
@@ -42,7 +44,16 @@ a.link, a, a.active {
 #status-Not_Run  { background-color: lightgrey; }\r
 #status-ok     { background-color: darkseagreen; }\r
 #status-0     { background-color: darkseagreen; }\r
-#status-error  { background-color: indianred; width="200px"; }\r
+#status-error  { background-color: indianred; }\r
+#status-none   { background-color: white; }\r
+\r
+#site-good { background-color : darkseagreen; }\r
+#site-down { background-color: indianred; }\r
+\r
+/*#nps-table { background-color: lightgrey; }*/\r
+/*#sub-table { background-color: lightgrey; }*/\r
+/* td, th {padding:2px;border:none;} */\r
+/* tr th {text-align:left;background-color:#f0f0f0;color:#333;} */\r
 \r
 #main_content {\r
   color: black;\r
@@ -112,7 +123,7 @@ h2 {
   padding: 10px;\r
   font-size: 80%;\r
   text-align: center;\r
-  width: 757px;\r
+  width: 765px;\r
   margin: 0 auto 1em auto;\r
 }\r
 \r
index dc3bf92..5e5dec1 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 Node View"
+from monitor.util import diff_time
+from time import mktime
 ?>
 <html py:layout="'sitemenu.kid'"
       xmlns:py="http://purl.org/kid/ns#">
@@ -20,12 +22,12 @@ layout_params['page_title'] = "Monitor Node View"
                <tbody>
                <tr>
                <td colspan="5">
-               <table border="1">
+               <table id="sub-table" border="1" width="100%">
                        <thead>
                                <tr>
                                        <th>Hostname</th>
                                        <th>ping</th>
-                                       <th>ssh</th>
+                                       <!--th>ssh</th-->
                                        <th>pcu</th>
                                        <th>status</th>
                                        <th>kernel</th>
@@ -36,11 +38,11 @@ layout_params['page_title'] = "Monitor Node View"
                                <tr py:for="i,node in enumerate(query)" class="${i%2 and 'odd' or 'even'}" >
                                  <td nowrap="true" py:content="node.hostname"></td>
                                  <td py:content="node.ping_status"></td>
-                                 <td py:content="node.ssh_status"></td>
-                                 <td py:content="node.pcu_status"></td>
+                                 <!--td py:content="node.ssh_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="node.plc_node_stats['last_contact']"></td>
+                                 <td py:content="diff_time(node.plc_node_stats['last_contact'])"></td>
                                </tr>
                        </tbody>
                </table>
index fc1b835..1ca6e36 100644 (file)
@@ -15,7 +15,7 @@ def pcu_link(pcu):
       xmlns:py="http://purl.org/kid/ns#">
 
   <div py:match="item.tag == 'content'">
-       <table width="100%">
+       <table id="sub-table" width="100%">
                <thead>
                        <tr>
                                <th><a href="${tg.url('pcu', filter='ok')}">Ok(${fc['ok']})</a></th>
@@ -28,7 +28,7 @@ def pcu_link(pcu):
                <tbody>
                <tr>
                <td colspan="5">
-               <table border="1">
+               <table border="1" width="100%">
                        <thead>
                                <tr>
                                        <th>Site</th>
@@ -52,7 +52,7 @@ def pcu_link(pcu):
                                                <span py:for="port,state in node.ports" 
                                                id="port${state}" py:content="'%s, ' % port">80</span>
                                        </td>
-                                       <td id="status-${node.status}" py:content="node.reboot_trial_status"></td>
+                                       <td width="40" id="status-${node.status}" py:content="node.reboot_trial_status"></td>
                                        <td py:content="node.plc_pcu_stats['model']"></td>
                                        <td py:content="len(node.plc_pcu_stats['node_ids'])"></td>
                                </tr>
index 23e9d41..d46e500 100644 (file)
@@ -6,11 +6,11 @@
   </head>
 
   <body>
-    <h1>Monitor : ${page_title}</h1>
-       <table valign="top" border="1" bgcolor="white" align="center" width="800">
+    <div id="header">Monitor : ${page_title}</div>
+       <table valign="top" border="1" bgcolor="white" align="center" width="60em">
        <tr>
                <td>
-                       <table>
+                       <table id="nps-table" width="100%">
                        <thead>
                        <tr>
                                <th><a href="${tg.url('node')}">Nodes</a></th>
@@ -32,6 +32,6 @@
        </tr>
        </table>
 
-    <div class="footer">Copywrite XYZ</div>
+    <div id="footer">Copywrite © 2007-2008 The Trustees of Princeton University</div>
   </body>
 </html>
diff --git a/web/MonitorWeb/prod.cfg b/web/MonitorWeb/prod.cfg
new file mode 100644 (file)
index 0000000..f1e675b
--- /dev/null
@@ -0,0 +1,76 @@
+[global]
+# This is where all of your settings go for your development environment
+# Settings that are the same for both development and production
+# (such as template engine, encodings, etc.) all go in
+# monitorweb/config/app.cfg
+
+# DATABASE
+
+# driver://username:password@host:port/database
+
+# pick the form for your database
+# sqlalchemy.dburi="postgres://username@hostname/databasename"
+# sqlalchemy.dburi="mysql://username:password@hostname:port/databasename"
+# sqlalchemy.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite"
+
+# If you have sqlite, here's a simple default to get you started
+# in development
+sqlalchemy.dburi="sqlite:///devdata.sqlite"
+
+
+# SERVER
+
+# Some server parameters that you may want to tweak
+# server.socket_port=8080
+
+# Enable the debug output at the end on pages.
+# log_debug_info_filter.on = False
+
+server.environment="development"
+autoreload.package="monitorweb"
+
+
+server.socket_host="127.0.0.1"
+server.socket_port=8080
+server.webpath="/monitor/"
+base_url_filter.on = False
+base_url_filter.base_url = "http://127.0.0.1:8080/monitor"
+base_url_filter.use_x_forwarded_host = True
+
+# Auto-Reload after code modification
+# autoreload.on = True
+
+# Set to True if you'd like to abort execution if a controller gets an
+# unexpected parameter. False by default
+tg.strict_parameters = True
+
+# LOGGING
+# Logging configuration generally follows the style of the standard
+# Python logging module configuration. Note that when specifying
+# log format messages, you need to use *() for formatting variables.
+# Deployment independent log configuration is in monitorweb/config/log.cfg
+[logging]
+
+[[loggers]]
+[[[monitorweb]]]
+level='DEBUG'
+qualname='monitorweb'
+handlers=['debug_out']
+
+[[[allinfo]]]
+level='INFO'
+handlers=['debug_out']
+
+[[[access]]]
+level='INFO'
+qualname='turbogears.access'
+handlers=['access_out']
+propagate=0
+
+
+[[[database]]]
+# Set to INFO to make SQLAlchemy display SQL commands
+level='ERROR'
+qualname='sqlalchemy.engine'
+handlers=['debug_out']
+propagate=0