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