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