a1746c86049703ec859267f2762994c83fbe5956
[monitor.git] / web / MonitorWeb / monitorweb / controllers.py
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
5 import cherrypy
6 # from monitorweb import model
7 # import logging
8 # log = logging.getLogger("monitorweb.controllers")
9 import re
10 from monitor.database.info.model import *
11 #from monitor.database.zabbixapi.model import *
12 #from monitor.database.dborm import zab_session as session
13 #from monitor.database.dborm import zab_metadata as metadata
14 from monitor_xmlrpc import MonitorXmlrpcServer
15
16 from monitor import reboot
17 from monitor import scanapi
18 import time
19
20 from monitor.wrapper.plccache import plcdb_hn2lb as site_hn2lb
21
22 from monitorweb.templates.links import *
23
24
25 # make it easier group objects without invoking the elixir auto-write feature.
26 class aggregate: pass
27
28
29 def query_to_dict(query):
30         """ take a url query string and chop it up """
31         val = {}
32         query_fields = query.split('&')
33         for f in query_fields:
34                 (k,v) = urllib.splitvalue(f)
35                 val[k] = v
36
37         return val
38
39 def format_ports(data, pcumodel=None):
40         retval = []
41         filtered_length=0
42
43         if pcumodel:
44                 supported_ports=reboot.model_to_object(pcumodel).supported_ports
45         else:
46                 # ports of a production node
47                 supported_ports=[22,80,806]
48
49         if data and len(data.keys()) > 0 :
50                 for port in supported_ports:
51                         try:
52                                 state = data[str(port)]
53                         except:
54                                 state = "unknown"
55
56                         if state == "filtered":
57                                 filtered_length += 1
58                                 
59                         retval.append( (port, state) )
60
61         if retval == []: 
62                 retval = [( "Closed/Filtered", "state" )]
63
64         if filtered_length == len(supported_ports):
65                 retval = [( "All Filtered", "state" )]
66
67         return retval
68
69 def format_pcu_shortstatus(pcu):
70         status = "error"
71         if pcu:
72                 if pcu.reboot_trial_status == str(0):
73                         status = "Ok"
74                 elif pcu.reboot_trial_status == "NetDown" or pcu.reboot_trial_status == "Not_Run":
75                         status = pcu.reboot_trial_status
76                 else:
77                         status = "error"
78
79         return status
80
81 def prep_pcu_for_display(pcu):
82         agg = aggregate()
83         agg.pcu = pcu 
84                 
85         try:
86                 agg.loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
87         except:
88                 agg.loginbase = "unknown"
89
90         agg.ports = format_ports(pcu.port_status, pcu.plc_pcu_stats['model'])
91         agg.status = format_pcu_shortstatus(pcu)
92
93         #print pcu.entry_complete
94         agg.entry_complete_str = pcu.entry_complete
95         #pcu.entry_complete_str += "".join([ f[0] for f in pcu.entry_complete.split() ])
96         if pcu.dns_status == "NOHOSTNAME":
97                 agg.dns_short_status = 'NoHost'
98         elif pcu.dns_status == "DNS-OK":
99                 agg.dns_short_status = 'Ok'
100         elif pcu.dns_status == "DNS-NOENTRY":
101                 agg.dns_short_status = 'NoEntry'
102         elif pcu.dns_status == "NO-DNS-OR-IP":
103                 agg.dns_short_status = 'NoHostOrIP'
104         elif pcu.dns_status == "DNS-MISMATCH":
105                 agg.dns_short_status = 'Mismatch'
106         return agg
107
108 class NodeWidget(widgets.Widget):
109         pass
110
111 def prep_node_for_display(node):
112         agg = aggregate()
113         agg.node = node
114
115         if node.plc_pcuid:
116                 pcu = FindbadPCURecord.get_latest_by(plc_pcuid=node.plc_pcuid)
117                 if pcu:
118                         agg.pcu_status = pcu.reboot_trial_status
119                         agg.pcu_short_status = format_pcu_shortstatus(pcu)
120                         agg.pcu = prep_pcu_for_display(pcu)
121                 else:
122                         agg.pcu_short_status = "none"
123                         agg.pcu_status = "nodata"
124                         agg.pcu = None
125
126         else:
127                 agg.pcu_status = "nopcu"
128                 agg.pcu_short_status = "none"
129                 agg.pcu = None
130
131
132         if node.kernel_version:
133                 agg.kernel = node.kernel_version.split()[2]
134         else:
135                 agg.kernel = ""
136
137         try:
138                 agg.loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
139         except:
140                 agg.loginbase = "unknown"
141
142         if agg.loginbase:
143                 agg.site = HistorySiteRecord.by_loginbase(agg.loginbase)
144                 if agg.site is None:
145                         # TODO: need a cleaner fix for this...
146                         agg.site = HistorySiteRecord.by_loginbase("pl")
147                         if not agg.site:
148                                 agg.site = HistorySiteRecord.by_loginbase("ple")
149
150         agg.history = HistoryNodeRecord.by_hostname(node.hostname)
151
152         agg.ports = format_ports(node.port_status)
153
154         try:
155                 exists = node.plc_node_stats['last_contact']
156         except:
157                 # TODO: this should not assign to the fb object!
158                 node.plc_node_stats = {'last_contact' : None}
159         
160         return agg
161
162
163 class Root(controllers.RootController, MonitorXmlrpcServer):
164         @expose(template="monitorweb.templates.welcome")
165         def index(self):
166                 # log.debug("Happy TurboGears Controller Responding For Duty")
167                 flash("Welcome To MyOps!")
168                 return dict(now=time.ctime())
169
170         @expose(template="monitorweb.templates.nodelist")
171         def node(self, filter='boot'):
172                 print "NODE------------------"
173                 print "befor-len: ", len( [ i for i in session] )
174                 session.flush(); session.clear()
175                 print "after-len: ", len( [ i for i in session] )
176                 fbquery = FindbadNodeRecord.get_all_latest()
177                 query = []
178                 filtercount = {'down' : 0, 'boot': 0, 'debug' : 0, 'diagnose' : 0, 'disabled': 0, 
179                                                 'neverboot' : 0, 'pending' : 0, 'all' : 0, None : 0}
180                 for node in fbquery:
181                         # NOTE: reformat some fields.
182                         agg = prep_node_for_display(node)
183
184                         #node.history.status
185                         #print node.hostname
186
187                         if not agg.history:
188                                 continue
189
190                         if agg.history.status in ['down', 'offline']:
191                                 if node.plc_node_stats and node.plc_node_stats['last_contact'] != None:
192                                         filtercount['down'] += 1
193                                 else:
194                                         filtercount['neverboot'] += 1
195                         elif agg.history.status in ['good', 'online']:
196                                 filtercount['boot'] += 1
197                         elif agg.history.status in ['debug', 'monitordebug']:
198                                 filtercount['debug'] += 1
199                         else:
200                                 # TODO: need a better fix. filtercount
201                                 # doesn't maps to GetBootStates() on
202                                 # 4.3 so this one fails quite often.
203                                 if filtercount.has_key(agg.history.status):
204                                         filtercount[agg.history.status] += 1
205                                 
206
207                         # NOTE: apply filter
208                         if filter == "neverboot":
209                                 if not node.plc_node_stats or node.plc_node_stats['last_contact'] == None:
210                                         query.append(agg)
211                         elif filter == "all":
212                                 query.append(agg)
213                         elif filter == agg.history.status:
214                                 query.append(agg)
215                         elif filter == 'boot':
216                                 query.append(agg)
217
218                                 
219                 widget = NodeWidget(template='monitorweb.templates.node_template')
220                 return dict(now=time.ctime(), query=query, fc=filtercount, nodewidget=widget)
221         
222         def nodeaction_handler(self, tg_exceptions=None):
223                 """Handle any kind of error."""
224                 print "NODEACTION_HANDLER------------------"
225
226                 if 'pcuid' in request.params:
227                         pcuid = request.params['pcuid']
228                 else:
229                         refurl = request.headers.get("Referer",link("pcu"))
230                         print refurl
231
232                         # TODO: do this more intelligently...
233                         uri_fields = urllib.splitquery(refurl)
234                         if uri_fields[1] is not None:
235                                 val = query_to_dict(uri_fields[1])
236                                 if 'pcuid' in val:
237                                         pcuid = val['pcuid']
238                                 elif 'hostname' in val:
239                                         pcuid = FindbadNodeRecord.get_latest_by(hostname=val['hostname']).plc_pcuid
240                                 else:
241                                         pcuid=None
242                         else:
243                                 pcuid=None
244
245                 cherry_trail = cherrypy._cputil.get_object_trail()
246                 for i in cherry_trail:
247                         print "trail: ", i
248
249                 print pcuid
250                 return self.pcuview(None, pcuid, **dict(exceptions=tg_exceptions))
251
252         def nodeaction(self, **data):
253                 print "NODEACTION------------------"
254                 for item in data.keys():
255                         print "%s %s" % ( item, data[item] )
256
257                 if 'hostname' in data:
258                         hostname = data['hostname']
259                 else:
260                         flash("No hostname given in submitted data")
261                         return
262
263                 if 'submit' in data or 'type' in data:
264                         try:
265                                 action = data['submit']
266                         except:
267                                 action = data['type']
268                 else:
269                         flash("No submit action given in submitted data")
270                         return
271
272                 if action == "Reboot":
273                         print "REBOOT: %s" % hostname
274                         ret = reboot.reboot_str(str(hostname))
275                         print ret
276                         if ret: raise RuntimeError("Error using PCU: " + str(ret))
277                         flash("Reboot appeared to work.  Allow at most 5 minutes.  Then run ExternalScan to check current status.")
278
279                 elif action == "ExternalScan":
280                         scanapi.externalprobe(str(hostname))
281                         flash("External Scan Successful!")
282                 elif action == "InternalScan":
283                         scanapi.internalprobe(str(hostname))
284                         flash("Internal Scan Successful!")
285                 else:
286                         # unknown action
287                         raise RuntimeError("Unknown action given")
288                 return
289
290         # TODO: add form validation
291         @expose(template="monitorweb.templates.pcuview")
292         @exception_handler(nodeaction_handler,"isinstance(tg_exceptions,RuntimeError)")
293         def pcuview(self, loginbase=None, pcuid=None, hostname=None, since=20, **data):
294                 print "PCUVIEW------------------"
295                 print "befor-len: ", len( [ i for i in session] )
296                 session.flush(); session.clear()
297                 print "after-len: ", len( [ i for i in session] )
298                 sitequery=[]
299                 pcuquery=[]
300                 nodequery=[]
301                 actions=[]
302                 exceptions = None
303
304                 try: since = int(since)
305                 except: since = 7
306
307                 for key in data:
308                         print key, data[key]
309
310                 if 'submit' in data.keys() or 'type' in data.keys():
311                         if hostname: data['hostname'] = hostname
312                         self.nodeaction(**data)
313                 if 'exceptions' in data:
314                         exceptions = data['exceptions']
315
316                 if pcuid:
317                         print "pcuid: %s" % pcuid
318                         pcu = FindbadPCURecord.get_latest_by(plc_pcuid=pcuid)
319                         loginbase = PlcSite.query.get(pcu.plc_pcu_stats['site_id']).plc_site_stats['login_base']
320
321                 if hostname:
322                         node = FindbadNodeRecord.get_latest_by(hostname=hostname)
323                         loginbase = PlcSite.query.get(node.plc_node_stats['site_id']).plc_site_stats['login_base']
324
325                 if loginbase:
326                         actions = ActionRecord.query.filter_by(loginbase=loginbase
327                                                         ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
328                                                         ).order_by(ActionRecord.date_created.desc())
329                         actions = [ a for a in actions ]
330                         sitequery = [HistorySiteRecord.by_loginbase(loginbase)]
331                         pcus = {}
332                         for node in FindbadNodeRecord.query.filter_by(loginbase=loginbase):
333                                         # NOTE: reformat some fields.
334                                         agg = prep_node_for_display(node)
335                                         nodequery += [agg]
336                                         if agg.pcu: #.pcu.plc_pcuid:    # not None
337                                                 #pcu = FindbadPCURecord.get_latest_by(plc_pcuid=agg.plc_pcuid)
338                                                 #prep_pcu_for_display(pcu)
339                                                 pcus[agg.pcu.pcu.plc_pcuid] = agg.pcu
340
341                         for pcuid_key in pcus:
342                                 pcuquery += [pcus[pcuid_key]]
343
344                 return dict(sitequery=sitequery, pcuquery=pcuquery, nodequery=nodequery, actions=actions, since=since, exceptions=exceptions)
345
346         @expose(template="monitorweb.templates.pcuhistory")
347         def pcuhistory(self, pcu_id=None):
348                 query = []
349                 if pcu_id:
350                         fbnode = HistoryPCURecord.get_by(plc_pcuid=pcu_id)
351                         l = fbnode.versions[-100:]
352                         l.reverse()
353                         for pcu in l:
354                                 #prep_node_for_display(node)
355                                 query.append(pcu)
356
357                 return dict(query=query, pcu_id=pcu_id)
358
359         @expose(template="monitorweb.templates.nodehistory")
360         def nodehistory(self, hostname=None):
361                 query = []
362                 if hostname:
363                         #fbnode = FindbadNodeRecord.get_by(hostname=hostname)
364                         ## TODO: add links for earlier history if desired.
365                         #l = fbnode.versions[-100:]
366                         #l.reverse()
367                         #for node in l:
368                         #       prep_node_for_display(node)
369                         #       query.append(node)
370
371                         fbnode = HistoryNodeRecord.get_by(hostname=hostname)
372                         l = fbnode.versions[-100:]
373                         l.reverse()
374                         for node in l:
375                                 #prep_node_for_display(node)
376                                 query.append(node)
377
378                 return dict(query=query, hostname=hostname)
379
380         @expose(template="monitorweb.templates.sitehistory")
381         def sitehistory(self, loginbase=None):
382                 query = []
383                 if loginbase:
384                         fbsite = HistorySiteRecord.get_by(loginbase=loginbase)
385                         # TODO: add links for earlier history if desired.
386                         l = fbsite.versions[-100:]
387                         l.reverse()
388                         for site in l:
389                                 query.append(site)
390                 return dict(query=query, loginbase=loginbase)
391
392
393         @expose(template="monitorweb.templates.pculist")
394         def pcu(self, filter='all'):
395                 print "PCUVIEW------------------"
396                 print "befor-len: ", len( [ i for i in session] )
397                 session.flush(); session.clear()
398                 print "after-len: ", len( [ i for i in session] )
399                 fbquery = FindbadPCURecord.get_all_latest()
400                 query = []
401                 filtercount = {'ok' : 0, 'NetDown': 0, 'Not_Run' : 0, 'pending' : 0, 'all' : 0}
402                 for node in fbquery:
403
404                         # NOTE: count filter
405                         if node.reboot_trial_status == str(0):
406                                 filtercount['ok'] += 1
407                         elif node.reboot_trial_status == 'NetDown' or node.reboot_trial_status == 'Not_Run':
408                                 filtercount[node.reboot_trial_status] += 1
409                         else:
410                                 filtercount['pending'] += 1
411
412                         pcuagg = prep_pcu_for_display(node)
413
414                         # NOTE: apply filter
415                         if filter == "all":
416                                 query.append(pcuagg)
417                         elif filter == "ok" and node.reboot_trial_status == str(0):
418                                 query.append(pcuagg)
419                         elif filter == node.reboot_trial_status:
420                                 query.append(pcuagg)
421                         elif filter == "pending":
422                                 # TODO: look in message logs...
423                                 if node.reboot_trial_status != str(0) and \
424                                         node.reboot_trial_status != 'NetDown' and \
425                                         node.reboot_trial_status != 'Not_Run':
426
427                                         query.append(pcuagg)
428                                 
429                 return dict(query=query, fc=filtercount)
430
431         @expose(template="monitorweb.templates.sitelist")
432         def site(self, filter='all'):
433                 print "SITE------------------"
434                 print "befor-len: ", len( [ i for i in session] )
435                 session.flush(); session.clear()
436                 print "after-len: ", len( [ i for i in session] )
437                 filtercount = {'good' : 0, 'down': 0, 'online':0, 'offline' : 0, 'new' : 0, 'pending' : 0, 'all' : 0}
438                 fbquery = HistorySiteRecord.query.all()
439                 query = []
440                 for site in fbquery:
441                         # count filter
442                         filtercount['all'] += 1
443                         if site.new and site.slices_used == 0 and not site.enabled:
444                                 filtercount['new'] += 1
445                         elif not site.enabled:
446                                 filtercount['pending'] += 1
447                         elif site.status in ['good', 'online']:
448                                 filtercount['good'] += 1
449                         elif site.status in ['down', 'offline']:
450                                 filtercount['down'] += 1
451
452                         # apply filter
453                         if filter == "all":
454                                 query.append(site)
455                         elif filter == 'new' and site.new and site.slices_used == 0 and not site.enabled:
456                                 query.append(site)
457                         elif filter == "pending" and not site.enabled:
458                                 query.append(site)
459                         elif filter == 'good' and site.status in ['good', 'online']:
460                                 query.append(site)
461                         elif filter == 'down' and site.status in ['down', 'offline']:
462                                 query.append(site)
463                                 
464                 return dict(query=query, fc=filtercount)
465
466         @expose(template="monitorweb.templates.actionsummary")
467         def actionsummary(self, since=7):
468                 from monitor.wrapper.emailTxt import mailtxt
469
470                 types = filter(lambda x: 'notice' in x, dir(mailtxt))
471                 results = {}
472
473                 try: since = int(since)
474                 except: since = 7
475
476                 for  t in types:
477                         acts = ActionRecord.query.filter(ActionRecord.action_type==t
478                                         ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since))
479                         results[t] = acts.count()
480                 return dict(results=results)
481
482         @expose(template="monitorweb.templates.actionlist")
483         def actionlist(self, action_type='down_notice', since=7, loginbase=None):
484
485                 try: since = int(since)
486                 except: since = 7
487
488                 if loginbase:
489                         acts = ActionRecord.query.filter_by(loginbase=loginbase
490                                 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
491                                 ).order_by(ActionRecord.date_created.desc())
492                 else:
493                         acts = ActionRecord.query.filter(ActionRecord.action_type==action_type
494                                 ).filter(ActionRecord.date_created >= datetime.now() - timedelta(since)
495                                 ).order_by(ActionRecord.date_created.desc())
496                 query = [ a for a in acts ]
497                 
498                 return dict(actions=query, action_type=action_type, since=since)