Fix for session.clear for newer versions of SQLAlchemy.
[monitor.git] / web / MonitorWeb / monitorweb / controllers.py
1 import turbogears as tg
2 from turbogears import controllers, expose, flash, exception_handler, redirect
3 from turbogears import widgets
4 from cherrypy import request, response
5 import cherrypy
6 # from monitorweb import model
7 # import logging
8 # log = logging.getLogger("monitorweb.controllers")
9 import re
10 import os
11 from monitor.database.info.model import *
12 #from monitor.database.zabbixapi.model import *
13 from monitor_xmlrpc import MonitorXmlrpcServer
14 from controllers_local import LocalExtensions
15
16 from monitor import util
17 from monitor import reboot
18 from monitor import bootman
19 from monitor import scanapi
20 from monitor import config
21 import time
22
23 from monitor.wrapper.plccache import plcdb_hn2lb as site_hn2lb
24
25 from monitorweb.templates.links import *
26
27 def session_clear_all():
28     session.flush()
29     try: 
30         session.expunge_all() 
31     except AttributeError: # SQLAlchemy < 0.5.1 
32         session.clear() 
33
34 class ObjectQueryFields(widgets.WidgetsList):
35         """The WidgetsList defines the fields of the form."""
36         pass
37
38
39
40 class NodeQueryFields(widgets.WidgetsList):
41         """The WidgetsList defines the fields of the form."""
42
43         object = widgets.RadioButtonList(label="Query Type", options=[('nodes', 'All Nodes'), 
44                                                                                                                           ('nodehistory', 'Single Node History'),
45                                                                                                                           #('sites', 'All Sites'),
46                                                                                                                           #('sitehistory', 'Single Site History'),
47                                                                                                                           ], default="nodes")
48         nodehistory_hostname = widgets.TextField(label="Hostname Node History", attrs={'size':30})
49
50         hostname = widgets.CheckBox(label="Hostname")
51         firewall = widgets.CheckBox(label="Firewall?")
52         fs_status = widgets.CheckBox(label="Filesystem Status")
53         ssh_status = widgets.CheckBox(label="SSH Status")
54         ssh_error = widgets.CheckBox(label="SSH Errors")
55         dns_status = widgets.CheckBox(label="DNS Status")
56         iptables_status = widgets.CheckBox(label="IP Tables Status")
57         nm_status = widgets.CheckBox(label="NM Status")
58         princeton_comon_dir = widgets.CheckBox(label="CoMon Dir")
59         princeton_comon_running = widgets.CheckBox(label="CoMon Running")
60         princeton_comon_procs = widgets.CheckBox(label="CoMon Processes")
61         external_dns_status = widgets.CheckBox(label="Hostname Resolves?")
62         kernel_version = widgets.CheckBox(label="Kernel")
63         bootcd_version = widgets.CheckBox(label="BootCD")
64         boot_server = widgets.CheckBox(label="Boot Server")
65         install_date = widgets.CheckBox(label="Installation Date")
66         observed_status = widgets.CheckBox(label="Observed Status")
67         uptime = widgets.CheckBox(label="Uptime")
68         traceroute = widgets.CheckBox(label="Traceroute")
69         port_status = widgets.CheckBox(label="Port Status")
70         plc_pcuid = widgets.CheckBox(label="PCU ID")
71         rpms = widgets.CheckBox(label="RPM")
72         rpmvalue = widgets.TextField(label="RPM Pattern")
73
74 class QueryForm(widgets.TableForm):
75     template = """
76     <form xmlns:py="http://purl.org/kid/ns#"
77         id="queryform"
78         name="${name}"
79         action="${action}"
80         method="${method}"
81         class="tableform"
82         py:attrs="form_attrs"
83     >
84         <div py:for="field in hidden_fields"
85             py:replace="field.display(value_for(field), **params_for(field))"
86         />
87         <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs">
88             <tr py:for="i, field in enumerate(fields)"
89                 class="${i%2 and 'odd' or 'even'}"
90             >
91                 <th>
92                     <label class="fieldlabel" for="${field.field_id}" py:content="field.label" />
93                 </th>
94                 <td>
95                     <span py:replace="field.display(value_for(field), **params_for(field))" />
96                     <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" />
97                     <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" />
98                 </td>
99             </tr>
100             <tr>
101                 <td>&#160;</td>
102                 <td py:content="submit.display(submit_text)" />
103             </tr>
104         </table>
105     </form>
106         """
107
108 def getNodeQueryForm():
109         return QueryForm(fields=NodeQueryFields(), action="query")
110
111 # make it easier group objects without invoking the elixir auto-write feature.
112 class aggregate: pass
113
114
115 def query_to_dict(query):
116         """ take a url query string and chop it up """
117         val = {}
118         query_fields = query.split('&')
119         for f in query_fields:
120                 (k,v) = urllib.splitvalue(f)
121                 val[k] = v
122
123         return val
124
125 def format_ports(data, pcumodel=None):
126         retval = []
127         filtered_length=0
128
129         if pcumodel:
130                 supported_ports=reboot.model_to_object(pcumodel).supported_ports
131         else:
132                 # ports of a production node
133                 supported_ports=[22,80,806]
134
135         if data and len(data.keys()) > 0 :
136                 for port in supported_ports:
137                         try:
138                                 state = data[str(port)]
139                         except:
140                                 state = "unknown"
141
142                         if state == "filtered":
143                                 filtered_length += 1
144                                 
145                         retval.append( (port, state) )
146
147         if retval == []: 
148                 retval = [( "Closed/Filtered", "state" )]
149
150         if filtered_length == len(supported_ports):
151                 retval = [( "All Filtered", "state" )]
152
153         return retval
154
155 def format_pcu_shortstatus(pcu):
156         status = "error"
157         if pcu:
158                 if pcu.reboot_trial_status == str(0):
159                         status = "Ok"
160                 elif pcu.reboot_trial_status == "NetDown" or pcu.reboot_trial_status == "Not_Run":
161                         status = pcu.reboot_trial_status
162                 else:
163                         status = "error"
164
165         return status
166
167 def prep_pcu_for_display(pcu):
168         agg = aggregate()
169         agg.pcu = pcu 
170                 
171         try:
172                 agg.loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
173         except:
174                 agg.loginbase = "unknown"
175
176         agg.pcuhist = HistoryPCURecord.query.get(pcu.plc_pcuid)
177
178         agg.ports = format_ports(pcu.port_status, pcu.plc_pcu_stats['model'])
179         agg.status = format_pcu_shortstatus(pcu)
180
181         #print pcu.entry_complete
182         agg.entry_complete_str = pcu.entry_complete
183         #pcu.entry_complete_str += "".join([ f[0] for f in pcu.entry_complete.split() ])
184         if pcu.dns_status == "NOHOSTNAME":
185                 agg.dns_short_status = 'NoHost'
186         elif pcu.dns_status == "DNS-OK":
187                 agg.dns_short_status = 'Ok'
188         elif pcu.dns_status == "DNS-NOENTRY":
189                 agg.dns_short_status = 'NoEntry'
190         elif pcu.dns_status == "NO-DNS-OR-IP":
191                 agg.dns_short_status = 'NoHostOrIP'
192         elif pcu.dns_status == "DNS-MISMATCH":
193                 agg.dns_short_status = 'Mismatch'
194         return agg
195
196 class ActionListWidget(widgets.Widget):
197         pass
198
199 class NodeWidget(widgets.Widget):
200         pass
201
202 def prep_nodehist(node):
203         agg = aggregate()
204         agg.node = node
205         agg.loginbase = "unknown"
206         try:
207                 agg.loginbase = PlcSite.query.get(node.plc_siteid).plc_site_stats['login_base']
208         except:
209                 agg.loginbase = "exception"
210                 
211
212         return agg
213
214 def prep_node_for_display(node, pcuhash=None, preppcu=True, asofdate=None):
215         agg = aggregate()
216         agg.node = node
217
218         if node.plc_pcuid and preppcu:
219                 if pcuhash:
220                         pcu = pcuhash[node.plc_pcuid]
221                 else:
222                         pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid)
223
224                 if pcu:
225                         agg.pcu_status = pcu.reboot_trial_status
226                         agg.pcu_short_status = format_pcu_shortstatus(pcu)
227                         agg.pcu = prep_pcu_for_display(pcu)
228                 else:
229                         agg.pcu_short_status = "none"
230                         agg.pcu_status = "nodata"
231                         agg.pcu = None
232
233         else:
234                 agg.pcu_status = "nopcu"
235                 agg.pcu_short_status = "none"
236                 agg.pcu = None
237
238
239         if node.kernel_version:
240                 agg.kernel = node.kernel_version.split()[2]
241         else:
242                 agg.kernel = ""
243
244         try:
245                 agg.loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
246         except:
247                 agg.loginbase = "unknown"
248
249         if agg.loginbase:
250                 agg.site = HistorySiteRecord.by_loginbase(agg.loginbase)
251
252                 if asofdate:
253                         agg.site = agg.site.get_as_of(asofdate)
254
255                 if agg.site is None:
256                         # TODO: need a cleaner fix for this...
257                         agg.site = HistorySiteRecord.by_loginbase("pl")
258                         if not agg.site:
259                                 agg.site = HistorySiteRecord.by_loginbase("ple")
260
261         agg.history = HistoryNodeRecord.by_hostname(node.hostname)
262         if asofdate:
263                 agg.history = agg.history.get_as_of(asofdate)
264
265         agg.ports = format_ports(node.port_status)
266
267         try:
268                 exists = node.plc_node_stats['last_contact']
269         except:
270                 # TODO: this should not assign to the fb object!
271                 node.plc_node_stats = {'last_contact' : None}
272         
273         return agg
274
275
276 class Root(controllers.RootController, MonitorXmlrpcServer, LocalExtensions):
277         @expose(template="monitorweb.templates.welcome")
278         def index(self):
279                 # log.debug("Happy TurboGears Controller Responding For Duty")
280                 flash("Welcome To MyOps!")
281                 return dict(now=time.ctime())
282
283         @expose(template="monitorweb.templates.nodelist", allow_json=True)
284         def node3(self, filter=None):
285                 nhquery = HistoryNodeRecord.query.all()
286                 query = []
287                 for nh in nhquery:
288                         if filter:
289                                 if nh.status == filter:
290                                         query.append(nh)
291                         else:
292                                 query.append(nh)
293
294                 rquery=[]
295                 for q in query:
296                         fb = FindbadNodeRecord.get_latest_by(hostname=q.hostname)
297                         rquery.append(fb)
298
299                 return dict(now=time.ctime(), query=rquery)
300
301         def node_query(self, filter):
302                 nhquery = HistoryNodeRecord.query.all()
303                 query = []
304                 for nh in nhquery:
305                         if filter:
306                                 if nh.status == filter:
307                                         query.append(nh)
308                         else:
309                                 query.append(nh)
310
311                 rquery=[]
312                 for q in query:
313                         fb = FindbadNodeRecord.get_latest_by(hostname=q.hostname)
314                         agg = prep_node_for_display(fb)
315                         rquery.append(agg)
316                 return rquery
317
318         @expose("cheetah:monitorweb.templates.nodelist_plain", as_format="plain", 
319                 accept_format="text/plain", content_type="text/plain")
320         @expose(template="monitorweb.templates.nodelist", allow_json=True)
321         def node2(self, filter=None):
322                 rquery=self.node_query(filter)
323                 widget = NodeWidget(template='monitorweb.templates.node_template')
324                 return dict(now=time.ctime(), query=rquery, nodewidget=widget)
325
326         @expose("cheetah:monitorweb.templates.query_plain", as_format="plain", 
327                 accept_format="text/plain", content_type="text/plain")
328         @expose(template="monitorweb.templates.query", allow_json=True)
329         def query(self, **data):
330                 query = []
331
332                 for k in data:
333                         print k, data[k]
334
335                 fbquery = None
336                 
337                 if 'object' in data and data['object'] == "nodes":
338                         fbquery = FindbadNodeRecord.get_all_latest()
339                 elif 'object' in data and data['object'] == "nodehistory": 
340                         hostname = data['nodehistory_hostname']
341                         data['date_checked'] = 'date_checked'
342                         fbrecord = FindbadNodeRecord.get_by(hostname=hostname)
343                         fbquery = fbrecord.versions[-500:]
344
345                 if fbquery:
346                         for node in fbquery:
347                                 # NOTE: reformat some fields.
348                                 if type(node) is not type(FindbadNodeRecord):
349                                         agg = node.__dict__.copy()
350                                 else:
351                                         agg = node.to_dict()
352                                 if agg['plc_node_stats']:
353                                         agg.update(agg['plc_node_stats'])
354                                 if agg['install_date']:
355                                         agg['install_date'] = time.mktime(time.strptime(agg['install_date'], "%a %b %d %H:%M:%S %Y"))
356                                 if agg['kernel_version']:
357                                         agg['kernel_version'] = agg['kernel_version'].split()[2]
358                                 if 'traceroute' in data and agg['traceroute']:
359                                         agg['traceroute'] = "<pre>" + agg['traceroute'] + "</pre>"
360                                 if 'rpmvalue' in data and 'rpms' in data:
361                                         if agg['rpms']:
362                                                 rpm_list = agg['rpms'].split()
363                                                 rpm_list = filter(lambda x: re.match(data['rpmvalue'], x, re.I),
364                                                                   rpm_list)
365                                                 agg['rpms'] = " ".join(rpm_list)
366
367                                 query.append(agg)
368
369                 fields=data.copy()
370
371                 try: 
372                         del fields['object']
373                         del fields['rpmvalue']
374                         del fields['nodehistory_hostname']
375                 except: pass
376                 return dict(now=time.ctime(), query=query, fields=fields, data=data, queryform=getNodeQueryForm())
377
378         @expose(template="monitorweb.templates.nodefast", allow_json=True)
379         def node(self, filter=None):
380                 nhquery = HistoryNodeRecord.query.all()
381                 query = []
382                 for nh in nhquery:
383                         if filter:
384                                 if nh.status == filter:
385                                         agg = prep_nodehist(nh)
386                                         query.append(agg)
387                         else:
388                                 agg = prep_nodehist(nh)
389                                 query.append(agg)
390
391                 return dict(now=time.ctime(), query=query)
392
393         @expose(template="monitorweb.templates.nodelist")
394         def nodeslow(self, filter='boot'):
395                 print "NODE------------------"
396                 print "befor-len: ", len( [ i for i in session] )
397         session_clear_all()
398                 print "after-len: ", len( [ i for i in session] )
399                 fbquery = FindbadNodeRecord.get_all_latest()
400                 query = []
401                 filtercount = {'down' : 0, 'boot': 0, 'debug' : 0, 'diagnose' : 0, 'disabled': 0, 
402                                                 'neverboot' : 0, 'pending' : 0, 'all' : 0, None : 0}
403                 for node in fbquery:
404                         # NOTE: reformat some fields.
405                         agg = prep_node_for_display(node)
406
407                         if not agg.history:
408                                 continue
409
410                         if agg.history.status in ['down', 'offline']:
411                                 if node.plc_node_stats and node.plc_node_stats['last_contact'] != None:
412                                         filtercount['down'] += 1
413                                 else:
414                                         filtercount['neverboot'] += 1
415                         elif agg.history.status in ['good', 'online']:
416                                 filtercount['boot'] += 1
417                         elif agg.history.status in ['debug', 'monitordebug']:
418                                 filtercount['debug'] += 1
419                         else:
420                                 if filtercount.has_key(agg.history.status):
421                                         filtercount[agg.history.status] += 1
422                                 
423
424                         # NOTE: apply filter
425                         if filter == "neverboot":
426                                 if not node.plc_node_stats or node.plc_node_stats['last_contact'] == None:
427                                         query.append(agg)
428                         elif filter == "all":
429                                 query.append(agg)
430                         elif filter == agg.history.status:
431                                 query.append(agg)
432                         elif filter == 'boot':
433                                 query.append(agg)
434
435                                 
436                 widget = NodeWidget(template='monitorweb.templates.node_template')
437                 return dict(now=time.ctime(), query=query, fc=filtercount, nodewidget=widget)
438         
439         def nodeaction_handler(self, tg_exceptions=None):
440                 """Handle any kind of error."""
441                 print "NODEACTION_HANDLER------------------"
442
443                 if 'pcuid' in request.params:
444                         pcuid = request.params['pcuid']
445                 else:
446                         refurl = request.headers.get("Referer",link("pcu"))
447                         print refurl
448
449                         # TODO: do this more intelligently...
450                         uri_fields = urllib.splitquery(refurl)
451                         if uri_fields[1] is not None:
452                                 val = query_to_dict(uri_fields[1])
453                                 if 'pcuid' in val:
454                                         pcuid = val['pcuid']
455                                 elif 'hostname' in val:
456                                         pcuid = FindbadNodeRecord.get_latest_by(hostname=val['hostname']).plc_pcuid
457                                 else:
458                                         pcuid=None
459                         else:
460                                 pcuid=None
461
462                 cherry_trail = cherrypy._cputil.get_object_trail()
463                 for i in cherry_trail:
464                         print "trail: ", i
465
466                 print pcuid
467                 return self.pcuview(None, pcuid, **dict(exceptions=tg_exceptions))
468
469         def nodeaction(self, **data):
470                 print "NODEACTION------------------"
471                 for item in data.keys():
472                         print "%s %s" % ( item, data[item] )
473
474                 if 'hostname' in data:
475                         hostname = data['hostname']
476                 else:
477                         flash("No hostname given in submitted data")
478                         return
479
480                 if 'submit' in data or 'type' in data:
481                         try:
482                                 action = data['submit']
483                         except:
484                                 action = data['type']
485                 else:
486                         flash("No submit action given in submitted data")
487                         return
488
489                 if action == "Reboot":
490                         print "REBOOT: %s" % hostname
491                         ret = reboot.reboot_str(str(hostname))
492                         print ret
493                         if ret: raise RuntimeError("Error using PCU: " + str(ret))
494                         flash("Reboot appeared to work.  Allow at most 5 minutes.  Then run ExternalScan to check current status.")
495
496                 elif action == "ExternalScan":
497                         scanapi.externalprobe(str(hostname))
498                         flash("External Scan Successful!")
499                 elif action == "InternalScan":
500                         scanapi.internalprobe(str(hostname))
501                         flash("Internal Scan Successful!")
502                 else:
503                         # unknown action
504                         raise RuntimeError("Unknown action given")
505                 return
506
507         @expose(template="monitorweb.templates.simpleview")
508         def simpleview(self, **data):
509                 return self.pre_view(**data)
510
511         @expose(template="monitorweb.templates.simpleview")
512         def pcuview(self, **data):
513                 return self.pre_view(**data)
514
515         @expose(template="monitorweb.templates.detailview")
516         def detailview(self, **data):
517                 return self.pre_view(**data)
518
519
520         def pre_view(self, **data):
521         session_clear_all()
522
523                 loginbase=None
524                 loginbase_list=[]
525                 hostname=None
526                 pcuid=None
527                 since=20
528                 # if objtype is not None, then treat 'hostname' or 'loginbase' as a search pattern
529                 objtype=None
530
531                 exceptions = None
532                 sitequery=[]
533                 nodequery=[]
534                 pcuquery=[]
535                 actions=[]
536                 actions_list=[]
537
538                 for key in data:
539                         print key, data[key]
540
541                 if 'query' in data:
542                         obj = data['query']
543                         fields = obj.split(":")
544                         if len(fields) > 1:
545                                 objtype = fields[0]
546                                 obj = fields[1].replace("*", "%")
547                                 print "obj: %s"% obj
548
549                         if len(obj.split(".")) > 1 or objtype == "node": 
550                                 hostname = obj
551                         else: 
552                                 loginbase = obj
553
554                 if 'loginbase' in data:
555                         loginbase = data['loginbase']
556
557                 if 'hostname' in data:
558                         hostname = data['hostname']
559
560                 if 'pcuid' in data:
561                         try: pcuid = int(data['pcuid'])
562                         except: pcuid = None
563
564                 if 'since' in data:
565                         try: since = int(since)
566                         except: since = 20
567
568                 if pcuid:
569                         print "pcuid: %s" % pcuid
570                         pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
571                         loginbase_list += [ PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base'] ]
572
573                 if hostname:
574                         if not objtype:
575                                 nodes = [ FindbadNodeRecord.get_latest_by(hostname=hostname) ]
576                         else:
577                                 nodes = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname.like(hostname)) 
578
579                         for node in nodes:
580                                 lb = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
581                                 if lb not in loginbase_list:
582                                         loginbase_list += [ lb ]
583
584                 if loginbase:
585                         if not objtype:
586                                 loginbase_list = [ loginbase ]
587                         else:
588                                 loginbase_list = HistorySiteRecord.query.filter(HistorySiteRecord.loginbase.like(loginbase)) 
589                                 loginbase_list = [ l.loginbase for l in loginbase_list ]
590                         
591
592                 if loginbase_list:
593                         for loginbase in loginbase_list:
594                                 actions = ActionRecord.query.filter_by(loginbase=loginbase
595                                                                 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
596                                                                 ).order_by(ActionRecord.date_created.desc())
597                                 actions_list += [ a for a in actions ]
598                                 site = HistorySiteRecord.by_loginbase(loginbase)
599                                 if site:
600                                         sitequery.append(site)
601                                 # NOTE: because a single pcu may be assigned to multiple hosts,
602                                 # track unique pcus by their plc_pcuid, then turn dict into list
603                                 pcus = {}
604                                 for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
605                                                 # NOTE: reformat some fields.
606                                                 agg = prep_node_for_display(node)
607                                                 nodequery += [agg]
608                                                 if agg.pcu: 
609                                                         pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
610
611                                 for pcuid_key in pcus:
612                                         pcuquery += [pcus[pcuid_key]]
613
614                 actionlist_widget = ActionListWidget(template='monitorweb.templates.actionlist_template')
615                 return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions_list, actionlist_widget=actionlist_widget, since=since, exceptions=exceptions)
616
617
618         # TODO: add form validation
619         @expose(template="monitorweb.templates.pcuview")
620         @exception_handler(nodeaction_handler,"isinstance(tg_exceptions,RuntimeError)")
621         def pcuviewold(self, loginbase=None, pcuid=None, hostname=None, since=20, **data):
622         session_clear_all()
623                 sitequery=[]
624                 pcuquery=[]
625                 nodequery=[]
626                 actions=[]
627                 exceptions = None
628
629                 try: since = int(since)
630                 except: since = 7
631
632                 for key in data:
633                         print key, data[key]
634
635                 if 'submit' in data.keys() or 'type' in data.keys():
636                         if hostname: data['hostname'] = hostname
637                         self.nodeaction(**data)
638                 if 'exceptions' in data:
639                         exceptions = data['exceptions']
640
641                 if 'query' in data:
642                         obj = data['query']
643                         if len(obj.split(".")) > 1: hostname = obj
644                         else: loginbase=obj
645
646                 if pcuid:
647                         print "pcuid: %s" % pcuid
648                         pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
649                         loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
650
651                 if hostname:
652                         node = FindbadNodeRecord.get_latest_by(hostname=hostname)
653                         loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
654
655                 if loginbase:
656                         actions = ActionRecord.query.filter_by(loginbase=loginbase
657                                                         ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
658                                                         ).order_by(ActionRecord.date_created.desc())
659                         actions = [ a for a in actions ]
660                         sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
661                         pcus = {}
662                         for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
663                                         # NOTE: reformat some fields.
664                                         agg = prep_node_for_display(node)
665                                         nodequery += [agg]
666                                         if agg.pcu: #.pcu.plc_pcuid:    # not None
667                                                 #pcu = FindbadPCURecord.get_latest_by(plc_pcuid=agg.plc_pcuid)
668                                                 #prep_pcu_for_display(pcu)
669                                                 pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
670
671                         for pcuid_key in pcus:
672                                 pcuquery += [pcus[pcuid_key]]
673
674                 return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions, since=since, exceptions=exceptions)
675
676         @expose(template="monitorweb.templates.pcuhistory")
677         def pcuhistory(self, pcu_id=None):
678                 query = []
679                 if pcu_id:
680                         fbnode = HistoryPCURecord.get_by(plc_pcuid=pcu_id)
681                         l = fbnode.versions[-1000:]
682                         l.reverse()
683                         for pcu in l:
684                                 #prep_node_for_display(node)
685                                 query.append(pcu)
686
687                 return dict(query=query, pcu_id=pcu_id)
688
689         @expose(template="monitorweb.templates.nodescanhistory")
690         def nodescanhistory(self, hostname=None, length=10):
691                 try: length = int(length)
692                 except: length = 21
693
694                 fbnode = FindbadNodeRecord.get_by(hostname=hostname)
695                 # TODO: add links for earlier history if desired.
696                 l = fbnode.versions[-length:]
697                 l.reverse()
698                 query=[]
699                 for node in l:
700                         agg = prep_node_for_display(node, pcuhash=None, preppcu=False, asofdate=node.timestamp)
701                         query.append(agg)
702
703                 if 'length' in request.params: 
704                         del request.params['length']
705                 return dict(query=query, hostname=hostname, params=request.params)
706
707         @expose(template="monitorweb.templates.nodehistory")
708         def nodehistory(self, hostname=None):
709                 query = []
710                 if hostname:
711                         fbnode = HistoryNodeRecord.get_by(hostname=hostname)
712                         l = fbnode.versions[-100:]
713                         l.reverse()
714                         for node in l:
715                                 #prep_node_for_display(node)
716                                 query.append(node)
717
718                 return dict(query=query, hostname=hostname)
719
720         @expose(template="monitorweb.templates.sitehistory")
721         def sitehistory(self, loginbase=None):
722                 query = []
723                 if loginbase:
724                         fbsite = HistorySiteRecord.get_by(loginbase=loginbase)
725                         # TODO: add links for earlier history if desired.
726                         l = fbsite.versions[-1000:]
727                         l.reverse()
728                         for site in l:
729                                 query.append(site)
730                 return dict(query=query, loginbase=loginbase)
731
732
733         @expose("cheetah:monitorweb.templates.pculist_plain", as_format="plain", 
734                 accept_format="text/plain", content_type="text/plain")
735         @expose(template="monitorweb.templates.pculist")
736         def pcu(self, filter='all'):
737                 print "PCUVIEW------------------"
738                 print "befor-len: ", len( [ i for i in session] )
739         session_clear_all()
740                 print "after-len: ", len( [ i for i in session] )
741                 fbquery = FindbadPCURecord.get_all_latest()
742                 query = []
743                 filtercount = {'ok' : 0, 'NetDown': 0, 'Not_Run' : 0, 'pending' : 0, 'all' : 0}
744                 for node in fbquery:
745
746                         # NOTE: count filter
747                         if node.reboot_trial_status == str(0):
748                                 filtercount['ok'] += 1
749                         elif node.reboot_trial_status == 'NetDown' or node.reboot_trial_status == 'Not_Run':
750                                 filtercount[node.reboot_trial_status] += 1
751                         else:
752                                 filtercount['pending'] += 1
753
754                         pcuagg = prep_pcu_for_display(node)
755
756                         # NOTE: apply filter
757                         if filter == "all":
758                                 query.append(pcuagg)
759                         elif filter == "ok" and node.reboot_trial_status == str(0):
760                                 query.append(pcuagg)
761                         elif filter == node.reboot_trial_status:
762                                 query.append(pcuagg)
763                         elif filter == "pending":
764                                 # TODO: look in message logs...
765                                 if node.reboot_trial_status != str(0) and \
766                                         node.reboot_trial_status != 'NetDown' and \
767                                         node.reboot_trial_status != 'Not_Run':
768
769                                         query.append(pcuagg)
770                                 
771                 return dict(query=query, fc=filtercount)
772
773         @expose(template="monitorweb.templates.sitelist")
774         def site(self, filter='all'):
775                 print "SITE------------------"
776                 print "befor-len: ", len( [ i for i in session] )
777         session_clear_all()
778                 print "after-len: ", len( [ i for i in session] )
779                 filtercount = {'good' : 0, 'down': 0, 'online':0, 'offline' : 0, 'new' : 0, 'pending' : 0, 'all' : 0}
780                 fbquery = HistorySiteRecord.query.all()
781                 query = []
782                 for site in fbquery:
783                         # count filter
784                         filtercount['all'] += 1
785                         if site.new and site.slices_used == 0 and not site.enabled:
786                                 filtercount['new'] += 1
787                         elif not site.enabled:
788                                 filtercount['pending'] += 1
789                         elif site.status in ['good', 'online']:
790                                 filtercount['good'] += 1
791                         elif site.status in ['down', 'offline']:
792                                 filtercount['down'] += 1
793
794                         # apply filter
795                         if filter == "all":
796                                 query.append(site)
797                         elif filter == 'new' and site.new and site.slices_used == 0 and not site.enabled:
798                                 query.append(site)
799                         elif filter == "pending" and not site.enabled:
800                                 query.append(site)
801                         elif filter == 'good' and site.status in ['good', 'online']:
802                                 query.append(site)
803                         elif filter == 'down' and site.status in ['down', 'offline']:
804                                 query.append(site)
805                                 
806                 return dict(query=query, fc=filtercount)
807         @expose(template="monitorweb.templates.sitesummary")
808         def sitesummary(self, loginbase="princeton"):
809                 nodequery = []
810                 for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
811                         agg = prep_node_for_display(node)
812                         nodequery += [agg]
813                 
814                 return dict(nodequery=nodequery, loginbase=loginbase)
815
816         @expose(template="monitorweb.templates.summary")
817         def summary(self, since=7):
818                 sumdata = {}
819                 sumdata['nodes'] = {}
820                 sumdata['sites'] = {}
821                 sumdata['pcus'] = {}
822
823                 def summarize(query, type):
824                         for o in query:
825                                 if o.status not in sumdata[type]:
826                                         sumdata[type][o.status] = 0
827                                 sumdata[type][o.status] += 1
828
829                 fbquery = HistorySiteRecord.query.all()
830                 summarize(fbquery, 'sites')
831                 fbquery = HistoryPCURecord.query.all()
832                 summarize(fbquery, 'pcus')
833                 fbquery = HistoryNodeRecord.query.all()
834                 summarize(fbquery, 'nodes')
835
836                 if 'monitordebug' in sumdata['nodes']:
837                         d = sumdata['nodes']['monitordebug']
838                         del sumdata['nodes']['monitordebug']
839                         sumdata['nodes']['failboot'] = d
840                 
841                 return dict(sumdata=sumdata, setorder=['good', 'offline', 'down', 'online']) 
842
843         @expose(template="monitorweb.templates.actionsummary")
844         def actionsummary(self, since=7):
845                 from monitor.wrapper.emailTxt import mailtxt
846
847                 types = filter(lambda x: 'notice' in x, dir(mailtxt))
848                 results = {}
849
850                 print mon_metadata.bind
851                 if session.bind is None:
852                         #TODO: figure out why this value gets cleared out...
853                         session.bind = mon_metadata.bind
854                 result = session.execute("select distinct(action_type) from actionrecord;")
855
856                 types = [r[0] for r in result]
857
858                 try: since = int(since)
859                 except: since = 7
860
861                 for  t in types:
862                         acts = ActionRecord.query.filter(ActionRecord.action_type==t
863                                         ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since))
864                         results[t] = acts.count()
865                 return dict(results=results)
866
867         @expose(template="monitorweb.templates.actionlist")
868         def actionlist(self, since=7, action_type=None, loginbase=None):
869
870                 try: since = int(since)
871                 except: since = 7
872
873                 acts_query = ActionRecord.query.filter(
874                                           ActionRecord.date_created >= datetime.now() - timedelta(since)
875                                          )
876                 if loginbase:
877                         acts_query = acts_query.filter_by(loginbase=loginbase)
878
879                 if action_type:
880                         acts_query = acts_query.filter(ActionRecord.action_type==action_type)
881
882                 acts = acts_query.order_by(ActionRecord.date_created.desc())
883
884                 query = [ a for a in acts ]
885                 
886                 return dict(actions=query, action_type=action_type, since=since)
887
888         @cherrypy.expose()
889         def upload(self, log, **keywords):
890                 hostname = None
891                 logtype = None
892                 logtype_list = ['bm.log', ]
893
894                 if 'hostname' in keywords:
895                         hostname = keywords['hostname']
896                 if 'type' in keywords and keywords['type'] in logtype_list:
897                         logtype = keywords['type']
898
899                 if not hostname: return ""
900                 if not logtype: return "unknown logtype: %s" % logtype 
901
902                 short_target_filename = bootman.bootmanager_log_name(hostname)
903                 abs_target_filename = os.path.join(config.MONITOR_BOOTMANAGER_LOG, short_target_filename)
904                 print "write data: %s" % abs_target_filename
905                 util.file.dumpFile(abs_target_filename, log.file.read())
906                 bootman.bootmanager_log_action(hostname, short_target_filename, logtype)
907                 session.flush()
908
909                 print "redirecting 3"
910
911                 return dict()
912