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