1 import turbogears as tg
2 from turbogears import controllers, expose, flash, exception_handler
3 from turbogears import widgets
4 from cherrypy import request, response
6 # from monitorweb import model
8 # log = logging.getLogger("monitorweb.controllers")
10 from monitor.database.info.model import *
11 #from monitor.database.zabbixapi.model import *
12 from monitor_xmlrpc import MonitorXmlrpcServer
14 from monitor import reboot
15 from monitor import scanapi
18 from monitor.wrapper.plccache import plcdb_hn2lb as site_hn2lb
20 from monitorweb.templates.links import *
22 class ObjectQueryFields(widgets.WidgetsList):
23 """The WidgetsList defines the fields of the form."""
28 class NodeQueryFields(widgets.WidgetsList):
29 """The WidgetsList defines the fields of the form."""
31 object = widgets.RadioButtonList(label="Query Type", options=[('nodes', 'All Nodes'),
32 ('nodehistory', 'Single Node History'),
33 #('sites', 'All Sites'),
34 #('sitehistory', 'Single Site History'),
36 nodehistory_hostname = widgets.TextField(label="Hostname Node History", attrs={'size':30})
38 hostname = widgets.CheckBox(label="Hostname")
39 firewall = widgets.CheckBox(label="Firewall?")
40 dns_status = widgets.CheckBox(label="DNS Status")
41 external_dns_status = widgets.CheckBox(label="Hostname Resolves?")
42 kernel_version = widgets.CheckBox(label="Kernel")
43 observed_status = widgets.CheckBox(label="Observed Status")
44 port_status = widgets.CheckBox(label="Port Status")
45 rpms = widgets.CheckBox(label="RPM")
46 rpmvalue = widgets.TextField(label="RPM Pattern")
48 class QueryForm(widgets.TableForm):
50 <form xmlns:py="http://purl.org/kid/ns#"
57 <div py:for="field in hidden_fields"
58 py:replace="field.display(value_for(field), **params_for(field))"
60 <table border="0" cellspacing="0" cellpadding="2" py:attrs="table_attrs">
61 <tr py:for="i, field in enumerate(fields)"
62 class="${i%2 and 'odd' or 'even'}"
65 <label class="fieldlabel" for="${field.field_id}" py:content="field.label" />
68 <span py:replace="field.display(value_for(field), **params_for(field))" />
69 <span py:if="error_for(field)" class="fielderror" py:content="error_for(field)" />
70 <span py:if="field.help_text" class="fieldhelp" py:content="field.help_text" />
75 <td py:content="submit.display(submit_text)" />
81 def getNodeQueryForm():
82 return QueryForm(fields=NodeQueryFields(), action="query")
84 # make it easier group objects without invoking the elixir auto-write feature.
88 def query_to_dict(query):
89 """ take a url query string and chop it up """
91 query_fields = query.split('&')
92 for f in query_fields:
93 (k,v) = urllib.splitvalue(f)
98 def format_ports(data, pcumodel=None):
103 supported_ports=reboot.model_to_object(pcumodel).supported_ports
105 # ports of a production node
106 supported_ports=[22,80,806]
108 if data and len(data.keys()) > 0 :
109 for port in supported_ports:
111 state = data[str(port)]
115 if state == "filtered":
118 retval.append( (port, state) )
121 retval = [( "Closed/Filtered", "state" )]
123 if filtered_length == len(supported_ports):
124 retval = [( "All Filtered", "state" )]
128 def format_pcu_shortstatus(pcu):
131 if pcu.reboot_trial_status == str(0):
133 elif pcu.reboot_trial_status == "NetDown" or pcu.reboot_trial_status == "Not_Run":
134 status = pcu.reboot_trial_status
140 def prep_pcu_for_display(pcu):
145 agg.loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
147 agg.loginbase = "unknown"
149 agg.pcuhist = HistoryPCURecord.query.get(pcu.plc_pcuid)
151 agg.ports = format_ports(pcu.port_status, pcu.plc_pcu_stats['model'])
152 agg.status = format_pcu_shortstatus(pcu)
154 #print pcu.entry_complete
155 agg.entry_complete_str = pcu.entry_complete
156 #pcu.entry_complete_str += "".join([ f[0] for f in pcu.entry_complete.split() ])
157 if pcu.dns_status == "NOHOSTNAME":
158 agg.dns_short_status = 'NoHost'
159 elif pcu.dns_status == "DNS-OK":
160 agg.dns_short_status = 'Ok'
161 elif pcu.dns_status == "DNS-NOENTRY":
162 agg.dns_short_status = 'NoEntry'
163 elif pcu.dns_status == "NO-DNS-OR-IP":
164 agg.dns_short_status = 'NoHostOrIP'
165 elif pcu.dns_status == "DNS-MISMATCH":
166 agg.dns_short_status = 'Mismatch'
169 class NodeWidget(widgets.Widget):
172 def prep_nodehist(node):
175 agg.loginbase = "unknown"
177 agg.loginbase = PlcSite.query.get(node.plc_siteid).plc_site_stats['login_base']
179 agg.loginbase = "exception"
184 def prep_node_for_display(node, pcuhash=None, preppcu=True, asofdate=None):
188 if node.plc_pcuid and preppcu:
190 pcu = pcuhash[node.plc_pcuid]
192 pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid)
195 agg.pcu_status = pcu.reboot_trial_status
196 agg.pcu_short_status = format_pcu_shortstatus(pcu)
197 agg.pcu = prep_pcu_for_display(pcu)
199 agg.pcu_short_status = "none"
200 agg.pcu_status = "nodata"
204 agg.pcu_status = "nopcu"
205 agg.pcu_short_status = "none"
209 if node.kernel_version:
210 agg.kernel = node.kernel_version.split()[2]
215 agg.loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
217 agg.loginbase = "unknown"
220 agg.site = HistorySiteRecord.by_loginbase(agg.loginbase)
223 agg.site = agg.site.get_as_of(asofdate)
226 # TODO: need a cleaner fix for this...
227 agg.site = HistorySiteRecord.by_loginbase("pl")
229 agg.site = HistorySiteRecord.by_loginbase("ple")
231 agg.history = HistoryNodeRecord.by_hostname(node.hostname)
233 agg.history = agg.history.get_as_of(asofdate)
235 agg.ports = format_ports(node.port_status)
238 exists = node.plc_node_stats['last_contact']
240 # TODO: this should not assign to the fb object!
241 node.plc_node_stats = {'last_contact' : None}
246 class Root(controllers.RootController, MonitorXmlrpcServer):
247 @expose(template="monitorweb.templates.welcome")
249 # log.debug("Happy TurboGears Controller Responding For Duty")
250 flash("Welcome To MyOps!")
251 return dict(now=time.ctime())
253 @expose(template="monitorweb.templates.nodelist", allow_json=True)
254 def node3(self, filter=None):
255 nhquery = HistoryNodeRecord.query.all()
259 if nh.status == filter:
266 fb = FindbadNodeRecord.get_latest_by(hostname=q.hostname)
269 return dict(now=time.ctime(), query=rquery)
271 @expose(template="monitorweb.templates.nodelist", allow_json=True)
272 def node2(self, filter=None):
273 nhquery = HistoryNodeRecord.query.all()
277 if nh.status == filter:
284 fb = FindbadNodeRecord.get_latest_by(hostname=q.hostname)
285 agg = prep_node_for_display(fb)
288 widget = NodeWidget(template='monitorweb.templates.node_template')
289 return dict(now=time.ctime(), query=rquery, nodewidget=widget)
291 @expose(template="monitorweb.templates.query", allow_json=True)
292 def query(self, **data):
300 if 'object' in data and data['object'] == "nodes":
301 fbquery = FindbadNodeRecord.get_all_latest()
302 elif 'object' in data and data['object'] == "nodehistory":
303 hostname = data['nodehistory_hostname']
304 data['date_checked'] = 'date_checked'
305 fbrecord = FindbadNodeRecord.get_by(hostname=hostname)
306 fbquery = fbrecord.versions[-500:]
310 # NOTE: reformat some fields.
311 if type(node) is not type(FindbadNodeRecord):
312 agg = node.__dict__.copy()
315 agg.update(agg['plc_node_stats'])
316 if 'rpmvalue' in data and 'rpms' in data:
318 rpm_list = agg['rpms'].split()
319 rpm_list = filter(lambda x: data['rpmvalue'] in x, rpm_list)
320 agg['rpms'] = " ".join(rpm_list)
328 del fields['rpmvalue']
329 del fields['nodehistory_hostname']
331 return dict(now=time.ctime(), query=query, fields=fields, data=data, queryform=getNodeQueryForm())
333 @expose(template="monitorweb.templates.nodefast", allow_json=True)
334 def node(self, filter=None):
335 nhquery = HistoryNodeRecord.query.all()
339 if nh.status == filter:
340 agg = prep_nodehist(nh)
343 agg = prep_nodehist(nh)
346 return dict(now=time.ctime(), query=query)
348 @expose(template="monitorweb.templates.nodelist")
349 def nodeslow(self, filter='boot'):
350 print "NODE------------------"
351 print "befor-len: ", len( [ i for i in session] )
352 session.flush(); session.clear()
353 print "after-len: ", len( [ i for i in session] )
354 fbquery = FindbadNodeRecord.get_all_latest()
356 filtercount = {'down' : 0, 'boot': 0, 'debug' : 0, 'diagnose' : 0, 'disabled': 0,
357 'neverboot' : 0, 'pending' : 0, 'all' : 0, None : 0}
359 # NOTE: reformat some fields.
360 agg = prep_node_for_display(node)
365 if agg.history.status in ['down', 'offline']:
366 if node.plc_node_stats and node.plc_node_stats['last_contact'] != None:
367 filtercount['down'] += 1
369 filtercount['neverboot'] += 1
370 elif agg.history.status in ['good', 'online']:
371 filtercount['boot'] += 1
372 elif agg.history.status in ['debug', 'monitordebug']:
373 filtercount['debug'] += 1
375 if filtercount.has_key(agg.history.status):
376 filtercount[agg.history.status] += 1
380 if filter == "neverboot":
381 if not node.plc_node_stats or node.plc_node_stats['last_contact'] == None:
383 elif filter == "all":
385 elif filter == agg.history.status:
387 elif filter == 'boot':
391 widget = NodeWidget(template='monitorweb.templates.node_template')
392 return dict(now=time.ctime(), query=query, fc=filtercount, nodewidget=widget)
394 def nodeaction_handler(self, tg_exceptions=None):
395 """Handle any kind of error."""
396 print "NODEACTION_HANDLER------------------"
398 if 'pcuid' in request.params:
399 pcuid = request.params['pcuid']
401 refurl = request.headers.get("Referer",link("pcu"))
404 # TODO: do this more intelligently...
405 uri_fields = urllib.splitquery(refurl)
406 if uri_fields[1] is not None:
407 val = query_to_dict(uri_fields[1])
410 elif 'hostname' in val:
411 pcuid = FindbadNodeRecord.get_latest_by(hostname=val['hostname']).plc_pcuid
417 cherry_trail = cherrypy._cputil.get_object_trail()
418 for i in cherry_trail:
422 return self.pcuview(None, pcuid, **dict(exceptions=tg_exceptions))
424 def nodeaction(self, **data):
425 print "NODEACTION------------------"
426 for item in data.keys():
427 print "%s %s" % ( item, data[item] )
429 if 'hostname' in data:
430 hostname = data['hostname']
432 flash("No hostname given in submitted data")
435 if 'submit' in data or 'type' in data:
437 action = data['submit']
439 action = data['type']
441 flash("No submit action given in submitted data")
444 if action == "Reboot":
445 print "REBOOT: %s" % hostname
446 ret = reboot.reboot_str(str(hostname))
448 if ret: raise RuntimeError("Error using PCU: " + str(ret))
449 flash("Reboot appeared to work. Allow at most 5 minutes. Then run ExternalScan to check current status.")
451 elif action == "ExternalScan":
452 scanapi.externalprobe(str(hostname))
453 flash("External Scan Successful!")
454 elif action == "InternalScan":
455 scanapi.internalprobe(str(hostname))
456 flash("Internal Scan Successful!")
459 raise RuntimeError("Unknown action given")
462 @expose(template="monitorweb.templates.simpleview")
463 def simpleview(self, **data):
464 return self.pre_view(**data)
466 @expose(template="monitorweb.templates.detailview")
467 def detailview(self, **data):
468 return self.pre_view(**data)
470 def pre_view(self, **data):
471 session.flush(); session.clear()
478 # if objtype is not None, then treat 'hostname' or 'loginbase' as a search pattern
493 fields = obj.split(":")
496 obj = fields[1].replace("*", "%")
499 if len(obj.split(".")) > 1 or objtype == "node":
504 if 'loginbase' in data:
505 loginbase = data['loginbase']
507 if 'hostname' in data:
508 hostname = data['hostname']
511 try: pcuid = int(data['pcuid'])
515 try: since = int(since)
519 print "pcuid: %s" % pcuid
520 pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
521 loginbase_list += [ PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base'] ]
525 nodes = [ FindbadNodeRecord.get_latest_by(hostname=hostname) ]
527 nodes = FindbadNodeRecord.query.filter(FindbadNodeRecord.hostname.like(hostname))
530 lb = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
531 if lb not in loginbase_list:
532 loginbase_list += [ lb ]
536 loginbase_list = [ loginbase ]
538 loginbase_list = HistorySiteRecord.query.filter(HistorySiteRecord.loginbase.like(loginbase))
539 loginbase_list = [ l.loginbase for l in loginbase_list ]
543 for loginbase in loginbase_list:
544 actions = ActionRecord.query.filter_by(loginbase=loginbase
545 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
546 ).order_by(ActionRecord.date_created.desc())
547 actions_list += [ a for a in actions ]
548 site = HistorySiteRecord.by_loginbase(loginbase)
550 sitequery.append(site)
551 # NOTE: because a single pcu may be assigned to multiple hosts,
552 # track unique pcus by their plc_pcuid, then turn dict into list
554 for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
555 # NOTE: reformat some fields.
556 agg = prep_node_for_display(node)
559 pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
561 for pcuid_key in pcus:
562 pcuquery += [pcus[pcuid_key]]
564 return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions_list, since=since, exceptions=exceptions)
567 # TODO: add form validation
568 @expose(template="monitorweb.templates.pcuview")
569 @exception_handler(nodeaction_handler,"isinstance(tg_exceptions,RuntimeError)")
570 def pcuview(self, loginbase=None, pcuid=None, hostname=None, since=20, **data):
571 session.flush(); session.clear()
578 try: since = int(since)
584 if 'submit' in data.keys() or 'type' in data.keys():
585 if hostname: data['hostname'] = hostname
586 self.nodeaction(**data)
587 if 'exceptions' in data:
588 exceptions = data['exceptions']
592 if len(obj.split(".")) > 1: hostname = obj
596 print "pcuid: %s" % pcuid
597 pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
598 loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
601 node = FindbadNodeRecord.get_latest_by(hostname=hostname)
602 loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
605 actions = ActionRecord.query.filter_by(loginbase=loginbase
606 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
607 ).order_by(ActionRecord.date_created.desc())
608 actions = [ a for a in actions ]
609 sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
611 for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
612 # NOTE: reformat some fields.
613 agg = prep_node_for_display(node)
615 if agg.pcu: #.pcu.plc_pcuid: # not None
616 #pcu = FindbadPCURecord.get_latest_by(plc_pcuid=agg.plc_pcuid)
617 #prep_pcu_for_display(pcu)
618 pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
620 for pcuid_key in pcus:
621 pcuquery += [pcus[pcuid_key]]
623 return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions, since=since, exceptions=exceptions)
625 @expose(template="monitorweb.templates.pcuhistory")
626 def pcuhistory(self, pcu_id=None):
629 fbnode = HistoryPCURecord.get_by(plc_pcuid=pcu_id)
630 l = fbnode.versions[-100:]
633 #prep_node_for_display(node)
636 return dict(query=query, pcu_id=pcu_id)
638 @expose(template="monitorweb.templates.nodescanhistory")
639 def nodescanhistory(self, hostname=None, length=10):
640 try: length = int(length)
643 fbnode = FindbadNodeRecord.get_by(hostname=hostname)
644 # TODO: add links for earlier history if desired.
645 l = fbnode.versions[-length:]
649 agg = prep_node_for_display(node, pcuhash=None, preppcu=False, asofdate=node.timestamp)
652 if 'length' in request.params:
653 del request.params['length']
654 return dict(query=query, hostname=hostname, params=request.params)
656 @expose(template="monitorweb.templates.nodehistory")
657 def nodehistory(self, hostname=None):
660 fbnode = HistoryNodeRecord.get_by(hostname=hostname)
661 l = fbnode.versions[-100:]
664 #prep_node_for_display(node)
667 return dict(query=query, hostname=hostname)
669 @expose(template="monitorweb.templates.sitehistory")
670 def sitehistory(self, loginbase=None):
673 fbsite = HistorySiteRecord.get_by(loginbase=loginbase)
674 # TODO: add links for earlier history if desired.
675 l = fbsite.versions[-100:]
679 return dict(query=query, loginbase=loginbase)
682 @expose(template="monitorweb.templates.pculist")
683 def pcu(self, filter='all'):
684 print "PCUVIEW------------------"
685 print "befor-len: ", len( [ i for i in session] )
686 session.flush(); session.clear()
687 print "after-len: ", len( [ i for i in session] )
688 fbquery = FindbadPCURecord.get_all_latest()
690 filtercount = {'ok' : 0, 'NetDown': 0, 'Not_Run' : 0, 'pending' : 0, 'all' : 0}
694 if node.reboot_trial_status == str(0):
695 filtercount['ok'] += 1
696 elif node.reboot_trial_status == 'NetDown' or node.reboot_trial_status == 'Not_Run':
697 filtercount[node.reboot_trial_status] += 1
699 filtercount['pending'] += 1
701 pcuagg = prep_pcu_for_display(node)
706 elif filter == "ok" and node.reboot_trial_status == str(0):
708 elif filter == node.reboot_trial_status:
710 elif filter == "pending":
711 # TODO: look in message logs...
712 if node.reboot_trial_status != str(0) and \
713 node.reboot_trial_status != 'NetDown' and \
714 node.reboot_trial_status != 'Not_Run':
718 return dict(query=query, fc=filtercount)
720 @expose(template="monitorweb.templates.sitelist")
721 def site(self, filter='all'):
722 print "SITE------------------"
723 print "befor-len: ", len( [ i for i in session] )
724 session.flush(); session.clear()
725 print "after-len: ", len( [ i for i in session] )
726 filtercount = {'good' : 0, 'down': 0, 'online':0, 'offline' : 0, 'new' : 0, 'pending' : 0, 'all' : 0}
727 fbquery = HistorySiteRecord.query.all()
731 filtercount['all'] += 1
732 if site.new and site.slices_used == 0 and not site.enabled:
733 filtercount['new'] += 1
734 elif not site.enabled:
735 filtercount['pending'] += 1
736 elif site.status in ['good', 'online']:
737 filtercount['good'] += 1
738 elif site.status in ['down', 'offline']:
739 filtercount['down'] += 1
744 elif filter == 'new' and site.new and site.slices_used == 0 and not site.enabled:
746 elif filter == "pending" and not site.enabled:
748 elif filter == 'good' and site.status in ['good', 'online']:
750 elif filter == 'down' and site.status in ['down', 'offline']:
753 return dict(query=query, fc=filtercount)
754 @expose(template="monitorweb.templates.sitesummary")
755 def sitesummary(self, loginbase="princeton"):
757 for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
758 agg = prep_node_for_display(node)
761 return dict(nodequery=nodequery, loginbase=loginbase)
763 @expose(template="monitorweb.templates.summary")
764 def summary(self, since=7):
766 sumdata['nodes'] = {}
767 sumdata['sites'] = {}
770 def summarize(query, type):
772 if o.status not in sumdata[type]:
773 sumdata[type][o.status] = 0
774 sumdata[type][o.status] += 1
776 fbquery = HistorySiteRecord.query.all()
777 summarize(fbquery, 'sites')
778 fbquery = HistoryPCURecord.query.all()
779 summarize(fbquery, 'pcus')
780 fbquery = HistoryNodeRecord.query.all()
781 summarize(fbquery, 'nodes')
783 if 'monitordebug' in sumdata['nodes']:
784 d = sumdata['nodes']['monitordebug']
785 del sumdata['nodes']['monitordebug']
786 sumdata['nodes']['failboot'] = d
788 return dict(sumdata=sumdata, setorder=['good', 'offline', 'down', 'online'])
790 @expose(template="monitorweb.templates.actionsummary")
791 def actionsummary(self, since=7):
792 from monitor.wrapper.emailTxt import mailtxt
794 types = filter(lambda x: 'notice' in x, dir(mailtxt))
797 print mon_metadata.bind
798 if session.bind is None:
799 #TODO: figure out why this value gets cleared out...
800 session.bind = mon_metadata.bind
801 result = session.execute("select distinct(action_type) from actionrecord;")
803 types = [r[0] for r in result]
805 try: since = int(since)
809 acts = ActionRecord.query.filter(ActionRecord.action_type==t
810 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since))
811 results[t] = acts.count()
812 return dict(results=results)
814 @expose(template="monitorweb.templates.actionlist")
815 def actionlist(self, action_type='down_notice', since=7, loginbase=None):
817 try: since = int(since)
821 acts = ActionRecord.query.filter_by(loginbase=loginbase
822 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
823 ).order_by(ActionRecord.date_created.desc())
825 acts = ActionRecord.query.filter(ActionRecord.action_type==action_type
826 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
827 ).order_by(ActionRecord.date_created.desc())
828 query = [ a for a in acts ]
830 return dict(actions=query, action_type=action_type, since=since)