Fix for session.clear for newer versions of SQLAlchemy.
[monitor.git] / web / MonitorWeb / monitorweb / controllers.py
index 1ce95cc..c540888 100644 (file)
@@ -1,5 +1,5 @@
 import turbogears as tg
-from turbogears import controllers, expose, flash, exception_handler
+from turbogears import controllers, expose, flash, exception_handler, redirect
 from turbogears import widgets
 from cherrypy import request, response
 import cherrypy
@@ -7,20 +7,106 @@ import cherrypy
 # import logging
 # log = logging.getLogger("monitorweb.controllers")
 import re
+import os
 from monitor.database.info.model import *
 #from monitor.database.zabbixapi.model import *
-#from monitor.database.dborm import zab_session as session
-#from monitor.database.dborm import zab_metadata as metadata
 from monitor_xmlrpc import MonitorXmlrpcServer
+from controllers_local import LocalExtensions
 
+from monitor import util
 from monitor import reboot
+from monitor import bootman
 from monitor import scanapi
+from monitor import config
 import time
 
 from monitor.wrapper.plccache import plcdb_hn2lb as site_hn2lb
 
 from monitorweb.templates.links import *
 
+def session_clear_all():
+    session.flush()
+    try: 
+        session.expunge_all() 
+    except AttributeError: # SQLAlchemy < 0.5.1 
+        session.clear() 
+
+class ObjectQueryFields(widgets.WidgetsList):
+       """The WidgetsList defines the fields of the form."""
+       pass
+
+
+
+class NodeQueryFields(widgets.WidgetsList):
+       """The WidgetsList defines the fields of the form."""
+
+       object = widgets.RadioButtonList(label="Query Type", options=[('nodes', 'All Nodes'), 
+                                                                                                                         ('nodehistory', 'Single Node History'),
+                                                                                                                         #('sites', 'All Sites'),
+                                                                                                                         #('sitehistory', 'Single Site History'),
+                                                                                                                         ], default="nodes")
+       nodehistory_hostname = widgets.TextField(label="Hostname Node History", attrs={'size':30})
+
+       hostname = widgets.CheckBox(label="Hostname")
+       firewall = widgets.CheckBox(label="Firewall?")
+       fs_status = widgets.CheckBox(label="Filesystem Status")
+       ssh_status = widgets.CheckBox(label="SSH Status")
+       ssh_error = widgets.CheckBox(label="SSH Errors")
+       dns_status = widgets.CheckBox(label="DNS Status")
+       iptables_status = widgets.CheckBox(label="IP Tables Status")
+       nm_status = widgets.CheckBox(label="NM Status")
+       princeton_comon_dir = widgets.CheckBox(label="CoMon Dir")
+       princeton_comon_running = widgets.CheckBox(label="CoMon Running")
+       princeton_comon_procs = widgets.CheckBox(label="CoMon Processes")
+       external_dns_status = widgets.CheckBox(label="Hostname Resolves?")
+       kernel_version = widgets.CheckBox(label="Kernel")
+       bootcd_version = widgets.CheckBox(label="BootCD")
+       boot_server = widgets.CheckBox(label="Boot Server")
+       install_date = widgets.CheckBox(label="Installation Date")
+       observed_status = widgets.CheckBox(label="Observed Status")
+       uptime = widgets.CheckBox(label="Uptime")
+       traceroute = widgets.CheckBox(label="Traceroute")
+       port_status = widgets.CheckBox(label="Port Status")
+       plc_pcuid = widgets.CheckBox(label="PCU ID")
+       rpms = widgets.CheckBox(label="RPM")
+       rpmvalue = widgets.TextField(label="RPM Pattern")
+
+class QueryForm(widgets.TableForm):
+    template = """
+    <form xmlns:py="http://purl.org/kid/ns#"
+        id="queryform"
+        name="${name}"
+        action="${action}"
+        method="${method}"
+        class="tableform"
+        py:attrs="form_attrs"
+    >
+        <div py:for="field in hidden_fields"
+            py:replace="field.display(value_for(field), **params_for(field))"
+        />
+        <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs">
+            <tr py:for="i, field in enumerate(fields)"
+                class="${i%2 and 'odd' or 'even'}"
+            >
+                <th>
+                    <label class="fieldlabel" for="${field.field_id}" py:content="field.label" />
+                </th>
+                <td>
+                    <span py:replace="field.display(value_for(field), **params_for(field))" />
+                    <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" />
+                    <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" />
+                </td>
+            </tr>
+            <tr>
+                <td>&#160;</td>
+                <td py:content="submit.display(submit_text)" />
+            </tr>
+        </table>
+    </form>
+       """
+
+def getNodeQueryForm():
+       return QueryForm(fields=NodeQueryFields(), action="query")
 
 # make it easier group objects without invoking the elixir auto-write feature.
 class aggregate: pass
@@ -107,9 +193,24 @@ def prep_pcu_for_display(pcu):
                agg.dns_short_status = 'Mismatch'
        return agg
 
+class ActionListWidget(widgets.Widget):
+       pass
+
 class NodeWidget(widgets.Widget):
        pass
 
+def prep_nodehist(node):
+       agg = aggregate()
+       agg.node = node
+       agg.loginbase = "unknown"
+       try:
+               agg.loginbase = PlcSite.query.get(node.plc_siteid).plc_site_stats['login_base']
+       except:
+               agg.loginbase = "exception"
+               
+
+       return agg
+
 def prep_node_for_display(node, pcuhash=None, preppcu=True, asofdate=None):
        agg = aggregate()
        agg.node = node
@@ -172,7 +273,7 @@ def prep_node_for_display(node, pcuhash=None, preppcu=True, asofdate=None):
        return agg
 
 
-class Root(controllers.RootController, MonitorXmlrpcServer):
+class Root(controllers.RootController, MonitorXmlrpcServer, LocalExtensions):
        @expose(template="monitorweb.templates.welcome")
        def index(self):
                # log.debug("Happy TurboGears Controller Responding For Duty")
@@ -197,8 +298,7 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
 
                return dict(now=time.ctime(), query=rquery)
 
-       @expose(template="monitorweb.templates.nodelist", allow_json=True)
-       def node2(self, filter=None):
+       def node_query(self, filter):
                nhquery = HistoryNodeRecord.query.all()
                query = []
                for nh in nhquery:
@@ -213,37 +313,88 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                        fb = FindbadNodeRecord.get_latest_by(hostname=q.hostname)
                        agg = prep_node_for_display(fb)
                        rquery.append(agg)
+               return rquery
 
-               #fbquery = FindbadNodeRecord.get_all_latest()
-               #fbpcus = FindbadPCURecord.get_all_latest()
-               #def fbtohash(fbpculist):
-               #       h = {}
-               #       for p in fbpculist:
-               #               h[p.plc_pcuid] = p
-#
-#              pcuhash = fbtohash(fbpcus)
-
-#              query = []
-#              for node in fbquery:
-#                      # NOTE: reformat some fields.
-#                      agg = prep_node_for_display(node, pcuhash)
-#                      if not agg.history:
-#                              continue
-#
-#                      if filter:
-#                              if agg.history.status == filter:
-#                                      query.append(agg)
-#                      else:
-#                              query.append(agg)
-                               
+       @expose("cheetah:monitorweb.templates.nodelist_plain", as_format="plain", 
+               accept_format="text/plain", content_type="text/plain")
+       @expose(template="monitorweb.templates.nodelist", allow_json=True)
+       def node2(self, filter=None):
+               rquery=self.node_query(filter)
                widget = NodeWidget(template='monitorweb.templates.node_template')
                return dict(now=time.ctime(), query=rquery, nodewidget=widget)
 
+       @expose("cheetah:monitorweb.templates.query_plain", as_format="plain", 
+               accept_format="text/plain", content_type="text/plain")
+       @expose(template="monitorweb.templates.query", allow_json=True)
+       def query(self, **data):
+               query = []
+
+               for k in data:
+                       print k, data[k]
+
+               fbquery = None
+               
+               if 'object' in data and data['object'] == "nodes":
+                       fbquery = FindbadNodeRecord.get_all_latest()
+               elif 'object' in data and data['object'] == "nodehistory": 
+                       hostname = data['nodehistory_hostname']
+                       data['date_checked'] = 'date_checked'
+                       fbrecord = FindbadNodeRecord.get_by(hostname=hostname)
+                       fbquery = fbrecord.versions[-500:]
+
+               if fbquery:
+                       for node in fbquery:
+                               # NOTE: reformat some fields.
+                               if type(node) is not type(FindbadNodeRecord):
+                                       agg = node.__dict__.copy()
+                               else:
+                                       agg = node.to_dict()
+                                if agg['plc_node_stats']:
+                                        agg.update(agg['plc_node_stats'])
+                               if agg['install_date']:
+                                       agg['install_date'] = time.mktime(time.strptime(agg['install_date'], "%a %b %d %H:%M:%S %Y"))
+                               if agg['kernel_version']:
+                                       agg['kernel_version'] = agg['kernel_version'].split()[2]
+                               if 'traceroute' in data and agg['traceroute']:
+                                       agg['traceroute'] = "<pre>" + agg['traceroute'] + "</pre>"
+                               if 'rpmvalue' in data and 'rpms' in data:
+                                       if agg['rpms']:
+                                               rpm_list = agg['rpms'].split()
+                                               rpm_list = filter(lambda x: re.match(data['rpmvalue'], x, re.I),
+                                                                  rpm_list)
+                                               agg['rpms'] = " ".join(rpm_list)
+
+                               query.append(agg)
+
+               fields=data.copy()
+
+               try: 
+                       del fields['object']
+                       del fields['rpmvalue']
+                       del fields['nodehistory_hostname']
+               except: pass
+               return dict(now=time.ctime(), query=query, fields=fields, data=data, queryform=getNodeQueryForm())
+
+       @expose(template="monitorweb.templates.nodefast", allow_json=True)
+       def node(self, filter=None):
+               nhquery = HistoryNodeRecord.query.all()
+               query = []
+               for nh in nhquery:
+                       if filter:
+                               if nh.status == filter:
+                                       agg = prep_nodehist(nh)
+                                       query.append(agg)
+                       else:
+                               agg = prep_nodehist(nh)
+                               query.append(agg)
+
+               return dict(now=time.ctime(), query=query)
+
        @expose(template="monitorweb.templates.nodelist")
-       def node(self, filter='boot'):
+       def nodeslow(self, filter='boot'):
                print "NODE------------------"
                print "befor-len: ", len( [ i for i in session] )
-               session.flush(); session.clear()
+        session_clear_all()
                print "after-len: ", len( [ i for i in session] )
                fbquery = FindbadNodeRecord.get_all_latest()
                query = []
@@ -357,31 +508,48 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
        def simpleview(self, **data):
                return self.pre_view(**data)
 
+       @expose(template="monitorweb.templates.simpleview")
+       def pcuview(self, **data):
+               return self.pre_view(**data)
+
        @expose(template="monitorweb.templates.detailview")
        def detailview(self, **data):
                return self.pre_view(**data)
 
+
        def pre_view(self, **data):
-               session.flush(); session.clear()
+        session_clear_all()
 
                loginbase=None
+               loginbase_list=[]
                hostname=None
                pcuid=None
                since=20
+               # if objtype is not None, then treat 'hostname' or 'loginbase' as a search pattern
+               objtype=None
 
                exceptions = None
                sitequery=[]
                nodequery=[]
                pcuquery=[]
                actions=[]
+               actions_list=[]
 
                for key in data:
                        print key, data[key]
 
                if 'query' in data:
                        obj = data['query']
-                       if len(obj.split(".")) > 1: hostname = obj
-                       else: loginbase=obj
+                       fields = obj.split(":")
+                       if len(fields) > 1:
+                               objtype = fields[0]
+                               obj = fields[1].replace("*", "%")
+                               print "obj: %s"% obj
+
+                       if len(obj.split(".")) > 1 or objtype == "node": 
+                               hostname = obj
+                       else: 
+                               loginbase = obj
 
                if 'loginbase' in data:
                        loginbase = data['loginbase']
@@ -400,39 +568,58 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                if pcuid:
                        print "pcuid: %s" % pcuid
                        pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
-                       loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
+                       loginbase_list += [ PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base'] ]
 
                if hostname:
-                       node = FindbadNodeRecord.get_latest_by(hostname=hostname)
-                       loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
-
-               if loginbase:
-                       actions = ActionRecord.query.filter_by(loginbase=loginbase
-                                                       ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
-                                                       ).order_by(ActionRecord.date_created.desc())
-                       actions = [ a for a in actions ]
-                       sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
-                       # NOTE: because a single pcu may be assigned to multiple hosts,
-                       # track unique pcus by their plc_pcuid, then turn dict into list
-                       pcus = {}
-                       for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
-                                       # NOTE: reformat some fields.
-                                       agg = prep_node_for_display(node)
-                                       nodequery += [agg]
-                                       if agg.pcu: 
-                                               pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
+                       if not objtype:
+                               nodes = [ FindbadNodeRecord.get_latest_by(hostname=hostname) ]
+                       else:
+                               nodes = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname.like(hostname)) 
 
-                       for pcuid_key in pcus:
-                               pcuquery += [pcus[pcuid_key]]
+                       for node in nodes:
+                               lb = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
+                               if lb not in loginbase_list:
+                                       loginbase_list += [ lb ]
 
-               return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions, since=since, exceptions=exceptions)
+               if loginbase:
+                       if not objtype:
+                               loginbase_list = [ loginbase ]
+                       else:
+                               loginbase_list = HistorySiteRecord.query.filter(HistorySiteRecord.loginbase.like(loginbase)) 
+                               loginbase_list = [ l.loginbase for l in loginbase_list ]
+                       
+
+               if loginbase_list:
+                       for loginbase in loginbase_list:
+                               actions = ActionRecord.query.filter_by(loginbase=loginbase
+                                                               ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
+                                                               ).order_by(ActionRecord.date_created.desc())
+                               actions_list += [ a for a in actions ]
+                               site = HistorySiteRecord.by_loginbase(loginbase)
+                               if site:
+                                       sitequery.append(site)
+                               # NOTE: because a single pcu may be assigned to multiple hosts,
+                               # track unique pcus by their plc_pcuid, then turn dict into list
+                               pcus = {}
+                               for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
+                                               # NOTE: reformat some fields.
+                                               agg = prep_node_for_display(node)
+                                               nodequery += [agg]
+                                               if agg.pcu: 
+                                                       pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
+
+                               for pcuid_key in pcus:
+                                       pcuquery += [pcus[pcuid_key]]
+
+               actionlist_widget = ActionListWidget(template='monitorweb.templates.actionlist_template')
+               return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions_list, actionlist_widget=actionlist_widget, since=since, exceptions=exceptions)
 
 
        # TODO: add form validation
        @expose(template="monitorweb.templates.pcuview")
        @exception_handler(nodeaction_handler,"isinstance(tg_exceptions,RuntimeError)")
-       def pcuview(self, loginbase=None, pcuid=None, hostname=None, since=20, **data):
-               session.flush(); session.clear()
+       def pcuviewold(self, loginbase=None, pcuid=None, hostname=None, since=20, **data):
+        session_clear_all()
                sitequery=[]
                pcuquery=[]
                nodequery=[]
@@ -491,7 +678,7 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                query = []
                if pcu_id:
                        fbnode = HistoryPCURecord.get_by(plc_pcuid=pcu_id)
-                       l = fbnode.versions[-100:]
+                       l = fbnode.versions[-1000:]
                        l.reverse()
                        for pcu in l:
                                #prep_node_for_display(node)
@@ -536,18 +723,20 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                if loginbase:
                        fbsite = HistorySiteRecord.get_by(loginbase=loginbase)
                        # TODO: add links for earlier history if desired.
-                       l = fbsite.versions[-100:]
+                       l = fbsite.versions[-1000:]
                        l.reverse()
                        for site in l:
                                query.append(site)
                return dict(query=query, loginbase=loginbase)
 
 
+       @expose("cheetah:monitorweb.templates.pculist_plain", as_format="plain", 
+               accept_format="text/plain", content_type="text/plain")
        @expose(template="monitorweb.templates.pculist")
        def pcu(self, filter='all'):
                print "PCUVIEW------------------"
                print "befor-len: ", len( [ i for i in session] )
-               session.flush(); session.clear()
+        session_clear_all()
                print "after-len: ", len( [ i for i in session] )
                fbquery = FindbadPCURecord.get_all_latest()
                query = []
@@ -585,7 +774,7 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
        def site(self, filter='all'):
                print "SITE------------------"
                print "befor-len: ", len( [ i for i in session] )
-               session.flush(); session.clear()
+        session_clear_all()
                print "after-len: ", len( [ i for i in session] )
                filtercount = {'good' : 0, 'down': 0, 'online':0, 'offline' : 0, 'new' : 0, 'pending' : 0, 'all' : 0}
                fbquery = HistorySiteRecord.query.all()
@@ -658,6 +847,14 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                types = filter(lambda x: 'notice' in x, dir(mailtxt))
                results = {}
 
+               print mon_metadata.bind
+               if session.bind is None:
+                       #TODO: figure out why this value gets cleared out...
+                       session.bind = mon_metadata.bind
+               result = session.execute("select distinct(action_type) from actionrecord;")
+
+               types = [r[0] for r in result]
+
                try: since = int(since)
                except: since = 7
 
@@ -668,19 +865,48 @@ class Root(controllers.RootController, MonitorXmlrpcServer):
                return dict(results=results)
 
        @expose(template="monitorweb.templates.actionlist")
-       def actionlist(self, action_type='down_notice', since=7, loginbase=None):
+       def actionlist(self, since=7, action_type=None, loginbase=None):
 
                try: since = int(since)
                except: since = 7
 
+               acts_query = ActionRecord.query.filter(
+                                         ActionRecord.date_created >= datetime.now() - timedelta(since)
+                                        )
                if loginbase:
-                       acts = ActionRecord.query.filter_by(loginbase=loginbase
-                               ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
-                               ).order_by(ActionRecord.date_created.desc())
-               else:
-                       acts = ActionRecord.query.filter(ActionRecord.action_type==action_type
-                               ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
-                               ).order_by(ActionRecord.date_created.desc())
+                       acts_query = acts_query.filter_by(loginbase=loginbase)
+
+               if action_type:
+                       acts_query = acts_query.filter(ActionRecord.action_type==action_type)
+
+               acts = acts_query.order_by(ActionRecord.date_created.desc())
+
                query = [ a for a in acts ]
                
                return dict(actions=query, action_type=action_type, since=since)
+
+       @cherrypy.expose()
+       def upload(self, log, **keywords):
+               hostname = None
+               logtype = None
+               logtype_list = ['bm.log', ]
+
+               if 'hostname' in keywords:
+                       hostname = keywords['hostname']
+               if 'type' in keywords and keywords['type'] in logtype_list:
+                       logtype = keywords['type']
+
+               if not hostname: return ""
+               if not logtype: return "unknown logtype: %s" % logtype 
+
+               short_target_filename = bootman.bootmanager_log_name(hostname)
+               abs_target_filename = os.path.join(config.MONITOR_BOOTMANAGER_LOG, short_target_filename)
+               print "write data: %s" % abs_target_filename
+               util.file.dumpFile(abs_target_filename, log.file.read())
+               bootman.bootmanager_log_action(hostname, short_target_filename, logtype)
+               session.flush()
+
+               print "redirecting 3"
+
+               return dict()
+