1
2
3
4
5
6
7
8
9
10
11
12
13
14 """
15 Data connector to backend of the event management system.
16 """
17
18 __version__ = "$Revision: 1.6 $"[11:-2]
19
20 import time
21 import types
22 import random
23 import simplejson
24 import re
25 random.seed()
26 import logging
27 log = logging.getLogger("zen.Events")
28
29 from AccessControl import ClassSecurityInfo
30 from AccessControl import getSecurityManager
31 from Globals import InitializeClass
32 from Globals import DTMLFile
33 from Acquisition import aq_base, aq_parent
34 import DateTime
35 from AccessControl import Permissions as permissions
36
37 from Products.ZenUtils.ObjectCache import ObjectCache
38 from Products.ZenUtils.Utils import extractPostContent
39
40 from interfaces import IEventList, IEventStatus, ISendEvents
41
42 from Products.AdvancedQuery import Eq, Or
43
44 from ZEvent import ZEvent
45 from EventDetail import EventDetail
46 from BetterEventDetail import BetterEventDetail
47 from EventCommand import EventCommand
48 from Exceptions import *
49
50 from Products.ZenModel.ZenModelRM import ZenModelRM
51 from Products.ZenModel.ZenossSecurity import ZEN_COMMON, ZEN_VIEW
52 from Products.ZenRelations.RelSchema import *
53 from Products.ZenUtils import Time
54 from Products.ZenUtils.FakeRequest import FakeRequest
55 from Products.ZenEvents.ZenEventClasses import Status_Ping, Status_Wmi_Conn
56 import StringIO
57 import csv
58 from sets import Set
59
60 from ZenEventClasses import Unknown
61
62 import time
63
64 from DbAccessBase import DbAccessBase
65
66
68 """
69 Prepares data from L{Products.ZenEvents.EventManagerBase.getEventSummary}
70 for rendering in the eventrainbow template macro.
71
72 Each cell of the old-style event rainbow needs a CSS class specified in
73 order to render its color, fill and border style. evtprep determines the
74 proper class and returns it, along with a string representation of the
75 number of live and acknowledged events.
76
77 >>> from Products.ZenEvents.EventManagerBase import evtprep
78 >>> evtprep(['zenevents_5_noack noack', 2, 2])
79 {'cssclass': 'zenevents_5_noack noack empty thin', 'data': '2/2'}
80 >>> evtprep(['zenevents_5_noack noack', 1, 2])
81 {'cssclass': 'zenevents_5_noack noack', 'data': '1/2'}
82
83 @param evts: A tuple of the form (Severity string, Number Acknowledged int,
84 Number Live int)
85 @type evts: tuple
86 @return: A dictionary of the form {'cssclass': Class string, 'data': Event
87 count representation string}
88
89 """
90 evtsdata = "%d/%d" % (evts[1],evts[2])
91 if evts[1]==evts[2] or evts[2]==0:
92 return {'cssclass':evts[0] + " empty thin",
93 'data':evtsdata}
94 else:
95 return {'cssclass':evts[0], 'data':evtsdata}
96
97
99 """
100 Data connector to backend of the event management system.
101 """
102
103
104
105
106 eventStateConversions = (
107 ('New', 0),
108 ('Acknowledged',1),
109 ('Suppressed', 2),
110
111 )
112
113 eventActions = ('status', 'history', 'drop')
114
115 severityConversions = (
116 ('Critical', 5),
117 ('Error', 4),
118 ('Warning', 3),
119 ('Info', 2),
120 ('Debug', 1),
121 ('Clear', 0),
122 )
123 severities = dict([(b, a) for a, b in severityConversions])
124
125 priorityConversions = (
126 ('None', -1),
127 ('Emergency', 0),
128 ('Alert', 1),
129 ('Critical', 2),
130 ('Error', 3),
131 ('Warning', 4),
132 ('Notice', 6),
133 ('Info', 8),
134 ('Debug', 10),
135 )
136 priorities = dict([(b, a) for a, b in priorityConversions])
137
138 statusTable = "status"
139 detailTable = "detail"
140 logTable = "log"
141 lastTimeField = "lastTime"
142 firstTimeField = "firstTime"
143 deviceField = "device"
144 componentField = "component"
145 eventClassField = "eventClass"
146 severityField = "severity"
147 stateField = "eventState"
148 countField = "count"
149 prodStateField = "prodState"
150 DeviceGroupField = "DeviceGroups"
151 SystemField = "Systems"
152
153 DeviceWhere = "\"device = '%s'\" % me.getDmdKey()"
154 DeviceResultFields = ("component", "eventClass", "summary", "firstTime",
155 "lastTime", "count" )
156 ComponentWhere = ("\"(device = '%s' and component = '%s')\""
157 " % (me.device().getDmdKey(), me.getDmdKey())")
158 ComponentResultFields = ("eventClass", "summary", "firstTime",
159 "lastTime", "count" )
160 IpAddressWhere = "\"ipAddress='%s'\" % (me.getId())"
161 EventClassWhere = "\"eventClass like '%s%%'\" % me.getDmdKey()"
162 EventClassInstWhere = """\"eventClass = '%s' and eventClassKey = '%s'\" % (\
163 me.getEventClass(), me.eventClassKey)"""
164 DeviceClassWhere = "\"DeviceClass like '%s%%'\" % me.getDmdKey()"
165 LocationWhere = "\"Location like '%s%%'\" % me.getDmdKey()"
166 SystemWhere = "\"Systems like '%%|%s%%'\" % me.getDmdKey()"
167 DeviceGroupWhere = "\"DeviceGroups like '%%|%s%%'\" % me.getDmdKey()"
168
169 defaultResultFields = ("device", "component", "eventClass", "summary",
170 "firstTime", "lastTime", "count" )
171
172 defaultFields = ('eventState', 'severity', 'evid')
173
174 defaultEventId = ('device', 'component', 'eventClass',
175 'eventKey', 'severity')
176
177 requiredEventFields = ('device', 'summary', 'severity')
178
179 refreshConversionsForm = DTMLFile('dtml/refreshNcoProduct', globals())
180
181 defaultAvailabilityDays = 7
182 defaultPriority = 3
183 eventAgingHours = 4
184 eventAgingSeverity = 4
185
186 _properties = (
187 {'id':'backend', 'type':'string','mode':'r', },
188 {'id':'username', 'type':'string', 'mode':'w'},
189 {'id':'password', 'type':'string', 'mode':'w'},
190 {'id':'host', 'type':'string', 'mode':'w'},
191 {'id':'database', 'type':'string', 'mode':'w'},
192 {'id':'port', 'type':'int', 'mode':'w'},
193 {'id':'defaultWhere', 'type':'text', 'mode':'w'},
194 {'id':'defaultOrderby', 'type':'text', 'mode':'w'},
195 {'id':'defaultResultFields', 'type':'lines', 'mode':'w'},
196 {'id':'statusTable', 'type':'string', 'mode':'w'},
197 {'id':'detailTable', 'type':'string', 'mode':'w'},
198 {'id':'logTable', 'type':'string', 'mode':'w'},
199 {'id':'lastTimeField', 'type':'string', 'mode':'w'},
200 {'id':'firstTimeField', 'type':'string', 'mode':'w'},
201 {'id':'deviceField', 'type':'string', 'mode':'w'},
202 {'id':'componentField', 'type':'string', 'mode':'w'},
203 {'id':'severityField', 'type':'string', 'mode':'w'},
204 {'id':'countField', 'type':'string', 'mode':'w'},
205 {'id':'DeviceGroupField', 'type':'string', 'mode':'w'},
206 {'id':'SystemField', 'type':'string', 'mode':'w'},
207 {'id':'DeviceWhere', 'type':'string', 'mode':'w'},
208 {'id':'DeviceResultFields', 'type':'lines', 'mode':'w'},
209 {'id':'ComponentResultFields', 'type':'lines', 'mode':'w'},
210 {'id':'EventClassWhere', 'type':'string', 'mode':'w'},
211 {'id':'EventClassInstWhere', 'type':'string', 'mode':'w'},
212 {'id':'DeviceClassWhere', 'type':'string', 'mode':'w'},
213 {'id':'LocationWhere', 'type':'string', 'mode':'w'},
214 {'id':'SystemWhere', 'type':'string', 'mode':'w'},
215 {'id':'DeviceGroupWhere', 'type':'string', 'mode':'w'},
216 {'id':'requiredEventFields', 'type':'lines', 'mode':'w'},
217 {'id':'defaultEventId', 'type':'lines', 'mode':'w'},
218 {'id':'defaultFields', 'type':'lines', 'mode':'w'},
219 {'id':'timeout', 'type':'int', 'mode':'w'},
220 {'id':'clearthresh', 'type':'int', 'mode':'w'},
221 {'id':'defaultAvailabilityDays', 'type':'int', 'mode':'w'},
222 {'id':'defaultPriority', 'type':'int', 'mode':'w'},
223 {'id':'eventAgingHours', 'type':'int', 'mode':'w'},
224 {'id':'eventAgingSeverity', 'type':'int', 'mode':'w'},
225 )
226
227 _relations = (
228 ("commands", ToManyCont(ToOne, "Products.ZenEvents.EventCommand", "eventManager")),
229 )
230
231 factory_type_information = (
232 {
233 'immediate_view' : 'editEventManager',
234 'actions' :
235 (
236 { 'id' : 'edit'
237 , 'name' : 'Edit'
238 , 'action' : 'editEventManager'
239 , 'permissions' : ( "Manage DMD", )
240 },
241 { 'id' : 'edit'
242 , 'name' : 'Fields'
243 , 'action' : 'editEventManagerFields'
244 , 'permissions' : ( "Manage DMD", )
245 },
246 { 'id' : 'history_edit'
247 , 'name' : 'History Fields'
248 , 'action' : 'editEventManagerHistoryFields'
249 , 'permissions' : ( "Manage DMD", )
250 },
251 { 'id' : 'commands'
252 , 'name' : 'Commands'
253 , 'action' : 'listEventCommands'
254 , 'permissions' : ( "Manage DMD", )
255 },
256 { 'id' : 'changes'
257 , 'name' : 'Modifications'
258 , 'action' : 'viewHistory'
259 , 'permissions' : ( permissions.view, )
260 },
261 )
262 },
263 )
264
265 security = ClassSecurityInfo()
266
267
268 - def __init__(self, id, title='', hostname='localhost', username='root',
269 password='', database='events', port=3306,
270 defaultWhere='',defaultOrderby='',defaultResultFields=[]):
271 """
272 Sets up event database access and initializes the cache.
273
274 @param id: A unique id
275 @type id: string
276 @param title: A title
277 @type title: string
278 @param hostname: The hostname of the events database server
279 @type hostname: string
280 @param username: The name of a user with permissions to access the
281 events database
282 @type username: string
283 @param password: The password of the user
284 @type password: string
285 @param database: The name of the events database
286 @type database: string
287 @param port: The port on which the database server is listening
288 @type port: int
289 @param defaultWhere: The default where clause to use when building
290 queries
291 @type defaultWhere: string
292 @param defaultOrderby: The default order by clause to use when building
293 queries
294 @type defaultOrderby: string
295 @param defaultResultFields: DEPRECATED. Currently unused.
296 @type defaultResultFields: list
297
298 """
299 self.id = id
300 self.title = title
301 self.username=username
302 self.password=password
303 self.database=database
304 self.host=hostname
305 self.port=port
306 DbAccessBase.__init__(self)
307
308 self.defaultWhere = defaultWhere
309 self.defaultOrderby="%s desc, %s desc" % (
310 self.severityField, self.lastTimeField)
311
312 self._schema = {}
313 self._fieldlist = []
314 self._colors = ()
315 self._ackedcolors = ()
316 ObjectCache.__init__(self)
317 self.initCache()
318
319
320
321
322
323
325 """
326 A wrapper for L{lookupManagedEntityResultFields} accepting an object
327 with an C{event_key} attribute.
328
329 >>> class dummy(object):
330 ... event_key = 'Device'
331 ...
332 >>> d = dummy()
333 >>> f = dmd.ZenEventManager.getEventResultFields(d)
334 >>> f==dmd.ZenEventManager.DeviceResultFields
335 True
336 >>> d.event_key = 'Robot'
337 >>> f = dmd.ZenEventManager.getEventResultFields(d)
338 >>> f==dmd.ZenEventManager.defaultResultFields
339 True
340
341 @param context: An object with an C{event_key} attribute.
342 @type context: L{ManagedEntity}
343 @return: A sequence of strings representing columns in the database.
344 @rtype: tuple
345 """
346 return self.lookupManagedEntityResultFields(getattr(context,
347 'event_key', 'Default'))
348
369
370
371 - def getEventBatchME(self, me, selectstatus=None, resultFields=[],
372 where="", orderby="", severity=None, state=2,
373 startdate=None, enddate=None, offset=0, rows=0,
374 getTotalCount=False, filter="", goodevids=[],
375 badevids=[], **kwargs):
376 """
377 Returns a batch of events based on criteria from checked rows on the
378 event console.
379
380 The event console can show thousands of events, and we want to support a
381 "Select All" feature; enter this method. It builds a query based on the
382 select status from the console ("All", "None", "Acknowledged",
383 "Unacknowledged") and any checkboxes that have been modified manually.
384
385 @param me: The managed entity for which to query events.
386 @type me: L{ManagedEntity}
387 @param resultFields: The columns to return from the database.
388 @type resultFields: list
389 @param where: DEPRECATED The base where clause to modify (ignored).
390 @type where: string
391 @param orderby: The "ORDER BY" string governing sort order.
392 @type orderby: string
393 @param severity: The minimum severity for which to query.
394 @type severity: int
395 @param state: The minimum state for which to query.
396 @type state: int
397 @param startdate: The early date limit
398 @type startdate: string, DateTime
399 @param enddate: The late date limit
400 @type enddate: string, DateTime
401 @param offset: The row at which to begin returning
402 @type offset: int
403 @param rows: DEPRECATED The number of rows to return (ignored).
404 @type rows: int
405 @param getTotalCount: Whether or not to return a count of the total
406 number of rows
407 @type getTotalCount: bool
408 @param filter: A glob by which to filter events
409 @type filter: string
410 @param goodevids: Ids of events that specifically should be included
411 @type goodevids: list
412 @param badevids: Ids of events that specifically should not be included
413 @type badevids: list
414 @return: Ids of matching events
415 @rtype: list
416 @todo: Remove unused parameters from the method definition
417 """
418 where = self.lookupManagedEntityWhere(me)
419 badevidsstr, goodevidsstr = '',''
420 if not isinstance(goodevids, (list, tuple)): goodevids = [goodevids]
421 if not isinstance(badevids, (list, tuple)): badevids = [badevids]
422 if badevids: badevidsstr = " and evid not in ('%s')" %(
423 "','".join(badevids))
424 if goodevids: goodevidsstr = " and evid in ('%s')" %(
425 "','".join(goodevids))
426 if selectstatus=='all':
427 where += badevidsstr
428 elif selectstatus=='none':
429 where += goodevidsstr or ' and 0'
430 elif selectstatus=='acked':
431 oper = bool(goodevidsstr) and ' or' or ' and'
432 where += goodevidsstr + oper + " (eventstate=1 %s) " % badevidsstr
433 elif selectstatus=='unacked':
434 oper = bool(goodevidsstr) and ' or' or 'and'
435 where += goodevidsstr + oper + " (eventstate=0 %s) " % badevidsstr
436 try:
437 resultFields = kwargs['resultFields']; del kwargs['resultFields']
438 except KeyError:
439 resultFields = self.lookupManagedEntityResultFields(me.event_key)
440 events = self.getEventList(
441 filter=filter,
442 offset=offset,
443 getTotalCount=False,
444 startdate=startdate,
445 enddate=enddate, severity=severity,
446 state=state, orderby=orderby,
447 resultFields=resultFields,
448 where=where,**kwargs)
449 return [ev.evid for ev in events]
450
451
452 - def getEventList(self, resultFields=[], where="", orderby="",
453 severity=None, state=2, startdate=None, enddate=None, offset=0,
454 rows=0, getTotalCount=False, filter="", **kwargs):
455 """
456 Fetch a list of events from the database matching certain criteria.
457
458 @param resultFields: The columns to return from the database.
459 @type resultFields: list
460 @param where: The base where clause to modify.
461 @type where: string
462 @param orderby: The "ORDER BY" string governing sort order.
463 @type orderby: string
464 @param severity: The minimum severity for which to query.
465 @type severity: int
466 @param state: The minimum state for which to query.
467 @type state: int
468 @param startdate: The early date limit
469 @type startdate: string, DateTime
470 @param enddate: The late date limit
471 @type enddate: string, DateTime
472 @param offset: The row at which to begin returning
473 @type offset: int
474 @param rows: The number of rows to return.
475 @type rows: int
476 @param getTotalCount: Whether or not to return a count of the total
477 number of rows
478 @type getTotalCount: bool
479 @param filter: A glob by which to filter events
480 @type filter: string
481 @return: Matching events as L{ZEvent}s.
482 @rtype: list
483 @todo: Remove unused parameters from the method definition
484 """
485 try:
486 if not resultFields:
487 resultFields = self.defaultResultFields
488 resultFields = list(resultFields)
489 resultFields.extend(self.defaultFields)
490 calcfoundrows = ''
491 if getTotalCount:
492 calcfoundrows = 'SQL_CALC_FOUND_ROWS'
493 select = ["select ", calcfoundrows, ','.join(resultFields),
494 "from %s where" % self.statusTable ]
495 if not where:
496 where = self.defaultWhere
497 where = self._wand(where, "%s >= %s", self.severityField, severity)
498 where = self._wand(where, "%s <= %s", self.stateField, state)
499 if filter:
500 where += ' and (%s) ' % (' or '.join(['%s LIKE "%%%s%%"' % (
501 x, filter) for x in resultFields]))
502 if startdate:
503 startdate, enddate = self._setupDateRange(startdate, enddate)
504 where += " and %s >= '%s' and %s <= '%s'" % (
505 self.lastTimeField, startdate,
506 self.firstTimeField, enddate)
507 select.append(where)
508 if not orderby:
509 orderby = self.defaultOrderby
510 if orderby:
511 select.append("order by")
512 select.append(orderby)
513 if rows:
514 select.append("limit %s, %s" % (offset, rows))
515 select.append(';')
516 select = " ".join(select)
517 if getTotalCount:
518 try: retdata, totalCount = self.checkCache(select)
519 except TypeError:
520 retdata, totalCount = self.checkCache(select), 100
521 else: retdata = self.checkCache(select)
522 if not False:
523 conn = self.connect()
524 try:
525 curs = conn.cursor()
526 curs.execute(select)
527 retdata = []
528
529
530 for row in curs.fetchall():
531 row = map(self.convert, resultFields, row)
532 evt = ZEvent(self, resultFields, row)
533 retdata.append(evt)
534 if getTotalCount:
535 curs.execute("SELECT FOUND_ROWS()")
536 totalCount = curs.fetchone()[0]
537 finally: self.close(conn)
538 if getTotalCount: self.addToCache(select, (retdata, totalCount))
539 else: self.addToCache(select, retdata)
540 self.cleanCache()
541 if getTotalCount:
542 return retdata, totalCount
543 else: return retdata
544 except:
545 log.exception("Failure querying events")
546 raise
547
548
550 """
551 Return the CSS class, number of acknowledged events, and number of
552 unacknowledged events, per severity, for a C{ManagedEntity}.
553
554 @param me: The object of the inquiry.
555 @type me: L{ManagedEntity}
556 @param severity: The minimum severity for which to retrieve events
557 @type severity: int
558 @param state: The minimum state for which to retrieve events
559 @type state: int
560 @param prodState: The minimum production state for which to retrieve
561 events
562 @type prodState: int
563 @return: List of lists of the form [class, acked count, unacked count].
564 @rtype: list
565 """
566 try:
567 where = self.lookupManagedEntityWhere(me)
568 return self.getEventSummary(where, severity, state, prodState)
569 except:
570 log.exception("event summary for %s failed" % me.getDmdKey())
571 raise
572
574 """
575 Get HTML code displaying the maximum event severity and the number of
576 events of that severity on a particular L{ManagedEntity} in a pleasing
577 pill-shaped container. Optionally return pills for lesser severities as
578 well. Optionally return a green pill if there are no events (normally no
579 events in a severity will not yield a result).
580
581 @param me: The object regarding which event data should be queried.
582 @type me: L{ManagedEntity}
583 @param number: The number of pills to return
584 @type number: int
585 @param showGreen: Whether to return an empty green pill if all is well
586 @type showGreen: bool
587 @return: HTML strings ready for template inclusion
588 @rtype: list
589 """
590 try:
591 evsum = self.getEventSummaryME(me, 0)
592 summary =[x[2] for x in evsum]
593 colors = "red orange yellow blue grey".split()
594 info = ["%s out of %s acknowledged" % (x[1],x[2])
595 for x in evsum]
596 results = zip(colors, [me.getPrimaryUrlPath()]*5, info, summary)
597 template = ('<div class="evpill-%s" onclick="location.href='
598 '\'%s/viewEvents\'" title="%s">%s</div>')
599 pills = []
600 for i, result in enumerate(results):
601 color, path, info, summary = result
602 if evsum[i][1]>=evsum[i][2]:
603 if color!='green': color += '-acked'
604 result = color, path, info, summary
605 if (result[3]):
606 pills.append(template % result)
607 if len(pills)==number: return pills
608 if (showGreen):
609 color = 'green'
610 summary = ' '
611 info = 'No events'
612 result = (color, path, info, summary)
613 pills.append(template % result)
614 return pills
615 except:
616 log.exception("event summary for %s failed" % me.getDmdKey())
617 return None
618
620 """
621 Return a list of categories of components on a device along with event
622 pills for the maximum severity event on each category in the form of a
623 JSON object ready for inclusion in a YUI data table. If a category
624 contains components with events, the component and its associated event
625 pill are returned as a separate (indented) row.
626
627 @param device: The device for which to gather component data
628 @type device: L{Device}
629 @return: A JSON-formatted string representation of the columns and rows
630 of the table
631 @rtype: string
632 """
633 mydict = {'columns':[], 'data':[]}
634 mydict['columns'] = ['Component Type', 'Status']
635 getcolor = re.compile(r'class=\"evpill-(.*?)\"', re.S|re.I|re.M).search
636 devdata = []
637 query = { 'getParentDeviceName':device.id,}
638 brains = device.componentSearch(query)
639 metatypes = Set([str(x.meta_type) for x in brains])
640 resultdict = {}
641 for mt in metatypes: resultdict[mt] = {}
642 evpilltemplate = ('<img src="img/%s_dot.png" '
643 'width="15" height="15" '
644 'style="cursor:hand;cursor:pointer" '
645 'onclick="location.href'
646 '=\'%s/viewEvents\'"/>')
647 linktemplate = ("<a href='%s' class='prettylink'>"
648 "<div class='device-icon-container'>%s "
649 "</div>%s</a>")
650 colors = "green grey blue yellow orange red".split()
651 indent = " "*8
652 def getcompfrombrains(id):
653 for comp in brains:
654 compid = comp.getPrimaryId.split('/')[-1]
655 if compid==id: return comp.getPrimaryId, comp.meta_type
656 return None, None
657 for event in self.getEventListME(device):
658 if not len(event.component): continue
659 id, metatype = getcompfrombrains(event.component)
660 if id is None or metatype is None:
661 id, metatype = event.component, 'Other'
662 tally = resultdict.setdefault(metatype,
663 {'sev':event.severity,
664 'components': {id: (event.severity, 1, id)}})
665 tally.setdefault('sev', event.severity)
666 tally.setdefault('components', {id: (event.severity, 0, id)})
667 if tally['sev'] < event.severity: tally['sev'] = event.severity
668 comp = tally['components'].setdefault(id, (event.severity, 0, id))
669 comptotal = tally['components'][id][1]
670 compsev = tally['components'][id][0]
671 newsev = compsev
672 if event.severity > compsev: newsev = event.severity
673 tally['components'][id] = (newsev, comptotal+1, id)
674 r = resultdict
675 categorysort = [(r[x]['sev'],len(r[x]['components']), x,
676 r[x]['components'].values()) for x in r if r[x]]
677 categorysort.sort(); categorysort.reverse()
678 categorysort.extend([(0, 0, x, []) for x in r if not r[x]])
679 devurl = device.getPrimaryUrlPath()
680 for bunch in categorysort:
681 catsev, catnum, catname, comps = bunch
682 catlink = '%s/os' % devurl
683 catcolor = colors[catsev]
684 evpill = evpilltemplate % (catcolor,
685 device.getPrimaryUrlPath())
686 if catnum: evpill = ''
687 devdata.append((linktemplate % (catlink, '', catname), evpill))
688 comps.sort()
689 for comp in comps:
690 compsev, compnum, complink = comp
691 compcolor = colors[compsev]
692 if not complink.startswith('/zport'):
693 compname = complink
694 complink = '%s/os' % devurl
695 else:
696 compname = complink.split('/')[-1]
697 if not compname: continue
698 compname = "<strong>%s</strong>" % compname
699 devdata.append(
700 (linktemplate % (complink, '', indent+compname),
701 evpilltemplate % (compcolor,
702 device.getPrimaryUrlPath())
703 )
704 )
705 mydict['data'] = [{'Component Type':x[0],
706 'Status':x[1]} for x in devdata]
707 return simplejson.dumps(mydict)
708
710 """
711 Return an HTML link and event pill for each object passed as a JSON
712 object ready for inclusion in a YUI data table.
713
714 @param objects: The objects for which to create links and pills.
715 @type objects: list
716 @return: A JSON-formatted string representation of the columns and rows
717 of the table
718 @rtype: string
719 """
720 mydict = {'columns':[], 'data':[]}
721 mydict['columns'] = ['Object', 'Events']
722 getcolor = re.compile(r'class=\"evpill-(.*?)\"', re.S|re.I|re.M).search
723 colors = "red orange yellow blue grey green".split()
724 def pillcompare(a,b):
725 a, b = map(lambda x:getcolor(x[1]), (a, b))
726 def getindex(x):
727 try:
728 color = x.groups()[0]
729 smallcolor = x.groups()[0].replace('-acked','')
730 isacked = 'acked' in color
731 index = colors.index(x.groups()[0].replace('-acked',''))
732 if isacked: index += .5
733 return index
734 except: return 5
735 a, b = map(getindex, (a, b))
736 return cmp(a, b)
737 devdata = []
738 for obj in objects:
739 alink = obj.getPrettyLink()
740 pill = self.getEventPillME(obj, showGreen=True)
741 if type(pill)==type([]): pill = pill[0]
742 devdata.append([alink, pill])
743 devdata.sort(pillcompare)
744 mydict['data'] = [{'Object':x[0],'Events':x[1]} for x in devdata]
745 return simplejson.dumps(mydict)
746
748 """
749 A wrapper for L{getObjectsEventSummaryJSON} that accepts a list of
750 paths to Zope objects which it then attempts to resolve. If no list of
751 paths is given, it will try to read them from the POST data of the
752 REQUEST object.
753
754 @param entities: A list of paths that should be resolved into objects
755 and passed to L{getObjectsEventSummaryJSON}.
756 @type entities: list
757 @return: A JSON-formatted string representation of the columns and rows
758 of the table
759 @rtype: string
760 """
761 if not entities:
762 entities = simplejson.loads(extractPostContent(REQUEST),
763 encoding = 'ascii')
764 def getob(e):
765 e = str(e)
766 try:
767 if not e.startswith('/zport/dmd'):
768 bigdev = '/zport/dmd' + e
769 obj = self.dmd.unrestrictedTraverse(bigdev)
770 except KeyError:
771 obj = self.dmd.Devices.findDevice(e)
772 if self.has_permission("View", obj): return obj
773 entities = filter(lambda x:x is not None, map(getob, entities))
774 return self.getObjectsEventSummaryJSON(entities, REQUEST)
775
777 """
778 Return a list of tuples with number of events and the color of the
779 severity that the number represents.
780
781 This method should not be called directly, but overridden by subclasses.
782
783 @param where: The base where clause to modify.
784 @type where: string
785 @param severity: The minimum severity for which to retrieve events
786 @type severity: int
787 @param state: The minimum state for which to retrieve events
788 @type state: int
789 @param prodState: The minimum production state for which to retrieve
790 events
791 @type prodState: int
792 @return: List of lists of the form [class, acked count, unacked count].
793 @rtype: list
794 """
795 raise NotImplementedError
796
797
800 try:
801 event = self.dmd.ZenEventManager.getEventDetail(
802 evid, dedupid, better)
803 except ZenEventNotFound:
804 event = self.dmd.ZenEventHistory.getEventDetail(evid, dedupid,
805 better)
806 return event
807
809 """Return an EventDetail object for a particular event.
810 """
811 idfield = evid and "evid" or "dedupid"
812 if not evid: evid = dedupid
813 cachekey = '%s%s' % (idfield, evid)
814 event = self.checkCache(cachekey)
815 if event: return event
816 fields = self.getFieldList()
817 selectevent = "select "
818 selectevent += ", ".join(fields)
819 selectevent += " from %s where" % self.statusTable
820 selectevent += " %s = '%s'" % (idfield, evid)
821 if self.backend=="omnibus": selectevent += ";"
822 conn = self.connect()
823 try:
824 curs = conn.cursor()
825 curs.execute(selectevent)
826 evrow = curs.fetchone()
827 if not evrow:
828 raise (ZenEventNotFound,"Event evid %s not found" % evid)
829 evdata = map(self.convert, fields, evrow)
830 if better:
831 event = BetterEventDetail(self, fields, evdata)
832 else:
833 event = EventDetail(self, fields, evdata)
834 event = event.__of__(self)
835
836 selectdetail = "select name, value from %s where" % self.detailTable
837 selectdetail += " evid = '%s'" % event.evid
838 if self.backend=="omnibus": selectevent += ";"
839
840 curs.execute(selectdetail)
841 event._details = curs.fetchall()
842
843 selectlogs = "select userName, ctime, text"
844 selectlogs += " from %s where" % self.logTable
845 selectlogs += " evid = '%s' order by ctime desc" % event.evid
846 if self.backend=="omnibus": selectevent += ";"
847
848 curs.execute(selectlogs)
849 jrows = curs.fetchall()
850 logs = []
851 for row in jrows:
852 user = self.cleanstring(row[0])
853 date = self.dateString(row[1])
854 text = row[2]
855 logs.append((user, date, text))
856 event._logs = logs
857 finally: self.close(conn)
858
859 self.addToCache(cachekey, event)
860 self.cleanCache()
861 return event
862
877
878
880 """Return status based on a where clause defined for the me event_type.
881 No fancy caching done this might be a little slow if there are a lot
882 of events. Where clause is evaled
883 """
884 where = self.lookupManagedEntityWhere(me)
885 select = "select count(*) from %s where %s" % (self.statusTable, where)
886 statusCount = self.checkCache(select)
887 if not statusCount:
888 conn = self.connect()
889 try:
890 curs = conn.cursor()
891
892 curs.execute(select)
893 statusCount = curs.fetchone()[0]
894 finally: self.close(conn)
895
896 self.addToCache(select,statusCount)
897 return statusCount
898
899
902 """see IEventStatus
903 """
904 orgfield = self.lookupManagedEntityField(org.event_key)
905 select = "select %s from %s where " % (orgfield, self.statusTable)
906 where = self._wand(where, "%s = '%s'", self.eventClassField,statusclass)
907 where = self._wand(where, "%s >= %s", self.severityField, severity)
908 where = self._wand(where, "%s <= %s", self.stateField, state)
909 select += where
910
911 statusCache = self.checkCache(select)
912 if not statusCache:
913 conn = self.connect()
914 try:
915 curs = conn.cursor()
916 curs.execute(select)
917 statusCache=[]
918 orgdict={}
919 for row in curs.fetchall():
920 orgfield = self.cleanstring(row[0])
921 if not orgfield: continue
922 if orgfield.startswith("|"): orgfield = orgfield[1:]
923 for orgname in orgfield.split("|"):
924 orgdict.setdefault(orgname, 0)
925 orgdict[orgname] += 1
926 statusCache = orgdict.items()
927 self.addToCache(select,statusCache)
928 finally: self.close(conn)
929 countevts = 0
930 for key, value in statusCache:
931 if key.startswith(org.getOrganizerName()):
932 countevts += value
933 return countevts
934
935
938 """Return list of tuples (org, count) for all organizers with events.
939 """
940 orgfield = self.lookupManagedEntityField(event_key)
941 select = "select %s, count from %s where " % (orgfield,self.statusTable)
942 where = self._wand(where, "%s >= %s", self.severityField, severity)
943 where = self._wand(where, "%s <= %s", self.stateField, state)
944 where = self._wand(where,"%s like '%s'",self.eventClassField,"/Status%")
945 select += where
946 statusCache = self.checkCache(select)
947 if not statusCache:
948 conn = self.connect()
949 try:
950 curs = conn.cursor()
951 curs.execute(select)
952 statusCache=[]
953 orgdict={}
954 for row in curs.fetchall():
955 orgfield = self.cleanstring(row[0])
956 if not orgfield: continue
957 if orgfield.startswith("|"): orgfield = orgfield[1:]
958 for orgname in orgfield.split("|"):
959 if not orgname: continue
960 count, total = orgdict.setdefault(orgname, (0,0))
961 count+=1
962 total+=row[1]
963 orgdict[orgname] = (count,total)
964 statusCache = [ [n, c[0], int(c[1])] for n, c in orgdict.items() ]
965 statusCache.sort(lambda x,y: cmp(x[1],y[1]))
966 statusCache.reverse()
967 if limit:
968 statusCache = statusCache[:limit]
969 self.addToCache(select,statusCache)
970 finally: self.close(conn)
971 return statusCache
972
973
981
982
989
990
996
997
999 """Return list of tuples (device, count, total) of events for
1000 all devices with events.
1001 """
1002 where = self._wand(where, "%s >= %s", self.severityField, severity)
1003 where = self._wand(where, "%s <= %s", self.stateField, state)
1004 select = """select distinct device, count(device) as evcount,
1005 sum(count) from status where %s group by device
1006 having evcount > %s""" % (where, mincount)
1007 statusCache = self.checkCache(select)
1008 if not statusCache:
1009 try:
1010 conn = self.connect()
1011 try:
1012 curs = conn.cursor()
1013 curs.execute(select)
1014 statusCache = [ [d,int(c),int(s)] for d,c,s in curs.fetchall() ]
1015
1016 statusCache.sort(lambda x,y: cmp(x[1],y[1]))
1017 statusCache.reverse()
1018 if limit:
1019 statusCache = statusCache[:limit]
1020 finally: self.close(conn)
1021 except:
1022 log.exception(select)
1023 raise
1024 return statusCache
1025
1026
1029 """see IEventStatus
1030 """
1031 if countField == None: countField = self.countField
1032 select = "select %s, %s from %s where " % (
1033 self.deviceField, self.countField, self.statusTable)
1034 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1035 where = self._wand(where, "%s >= %s", self.severityField, severity)
1036 where = self._wand(where, "%s <= %s", self.stateField, state)
1037 select += where
1038
1039 statusCache = self.checkCache(select)
1040 if not statusCache:
1041 try:
1042 conn = self.connect()
1043 try:
1044 curs = conn.cursor()
1045 curs.execute(select)
1046 statusCache = {}
1047 for dev, count in curs.fetchall():
1048 dev = self.cleanstring(dev)
1049 statusCache[dev] = count
1050 self.addToCache(select,statusCache)
1051 finally: self.close(conn)
1052 except:
1053 log.exception("status failed for device %s", device)
1054 return -1
1055 return statusCache.get(device, 0)
1056
1057
1060
1061
1064
1065
1067 import Availability
1068 for name in "device", "component", "eventClass", "systems":
1069 if hasattr(state, name):
1070 kw.setdefault(name, getattr(state, name))
1071 try:
1072 kw.setdefault('severity',
1073 dict(self.severityConversions)[state.severity])
1074 except (ValueError, KeyError):
1075 pass
1076 for name in "startDate", "endDate":
1077 if hasattr(state, name):
1078 kw.setdefault(name, Time.ParseUSDate(getattr(state, name)))
1079 kw.setdefault('startDate',
1080 time.time() - 60*60*24*self.defaultAvailabilityDays)
1081 return Availability.query(self.dmd, **kw)
1082
1083
1085 """Return all heartbeat issues list of tuples (device, component, secs)
1086 """
1087 sel = """select device, component, lastTime from heartbeat """
1088 if failures:
1089 sel += "where DATE_ADD(lastTime, INTERVAL timeout SECOND) <= NOW();"
1090
1091 statusCache = self.checkCache(sel)
1092 cleanup = lambda : None
1093 if not statusCache:
1094 statusCache = []
1095 conn = self.connect()
1096 try:
1097 curs = conn.cursor()
1098 curs.execute(sel)
1099 res = list(curs.fetchall())
1100 res.sort(lambda x,y: cmp(x[2],y[2]))
1101 devclass = self.getDmdRoot("Devices")
1102 for devname, comp, dtime in res:
1103 dtime = "%d" % int(time.time()-dtime.timeTime())
1104 dev = devclass.findDevice(devname)
1105 if dev and not simple:
1106 alink = "<a href='%s'>%s</a>" % (
1107 dev.getPrimaryUrlPath(), dev.id)
1108 else: alink = devname
1109 statusCache.append([alink, comp, dtime, devname])
1110 if limit:
1111 statusCache = statusCache[:limit]
1112 cleanup()
1113 finally: self.close(conn)
1114 return statusCache
1115
1116
1118 beats = self.getHeartbeat(failures, simple, limit, db)
1119 return [{'alink':b[0], 'comp':b[1], 'dtime':b[2], 'devId':b[3]}
1120 for b in beats]
1121
1122
1129 "Fetch the counts on all components matching statClass"
1130 if countField == None: countField = self.countField
1131 select = "select %s, %s, %s from %s where "\
1132 % (self.deviceField, self.componentField, countField,
1133 self.statusTable)
1134 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1135 where = self._wand(where, "%s >= %s", self.severityField, severity)
1136 where = self._wand(where, "%s <= %s", self.stateField, state)
1137 select += where
1138 conn = self.connect()
1139 try:
1140 curs = conn.cursor()
1141 curs.execute(select)
1142 result = {}
1143 for dev, comp, count in curs.fetchall():
1144 dev = self.cleanstring(dev)
1145 comp = self.cleanstring(comp)
1146 result[dev,comp] = count
1147 return result
1148 finally:
1149 self.close(conn)
1150
1151
1168
1169
1172 """see IEventStatus
1173 """
1174 if countField == None: countField = self.countField
1175 select = "select %s, %s, %s from %s where "\
1176 % (self.deviceField, self.componentField, countField,
1177 self.statusTable)
1178 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1179 where = self._wand(where, "%s >= %s", self.severityField, severity)
1180 where = self._wand(where, "%s <= %s", self.stateField, state)
1181 select += where
1182 statusCache = self.checkCache(select)
1183 if not statusCache:
1184 conn = self.connect()
1185 try:
1186 curs = conn.cursor()
1187 curs.execute(select)
1188 statusCache ={}
1189 for dev, comp, count in curs.fetchall():
1190 dev = self.cleanstring(dev)
1191 comp = self.cleanstring(comp)
1192 statusCache[dev+comp] = count
1193 self.addToCache(select,statusCache)
1194 finally: self.close(conn)
1195 return statusCache.get(device+component, 0)
1196
1197
1207
1208
1210 """Return a list of userids that correspond to the events in where.
1211 select distinct ownerid from status where
1212 device="win2k.confmon.loc" and eventState > 2
1213 """
1214 select ="select distinct ownerid from status where "
1215 where = self._wand(where, "%s >= %s", self.severityField, severity)
1216 where = self._wand(where, "%s <= %s", self.stateField, state)
1217 select += where
1218
1219 statusCache = self.checkCache(select)
1220 if statusCache: return statusCache
1221 conn = self.connect()
1222 try:
1223 curs = conn.cursor()
1224 curs.execute(select)
1225 statusCache = [ uid[0] for uid in curs.fetchall() if uid[0] ]
1226 self.addToCache(select,statusCache)
1227 finally: self.close(conn)
1228 return statusCache
1229
1230
1232 """Lookup and build where clause for managed entity.
1233 """
1234 key = me.event_key + "Where"
1235 wheretmpl = getattr(aq_base(self), key, False)
1236 if not wheretmpl:
1237 raise ValueError("no where found for event_key %s" % me.event_key)
1238 return eval(wheretmpl,{'me':me})
1239
1240
1242 """Lookup database field for managed entity default is event_key.
1243 """
1244 key = event_key + "Field"
1245 return getattr(aq_base(self), key, event_key)
1246
1247
1249 """
1250 Gets the column names that should be requested in an event query for
1251 this entity type.
1252
1253 Returns a set of result fields predefined for this entity type. If
1254 none have been defined, returns the default result fields.
1255
1256 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Device')
1257 >>> f==dmd.ZenEventManager.DeviceResultFields
1258 True
1259 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Robot')
1260 >>> f==dmd.ZenEventManager.defaultResultFields
1261 True
1262
1263 @param event_key: The event key of a managed entity.
1264 @type event_key: string
1265 @return: A tuple of strings representing columns in the database.
1266 """
1267 key = event_key + "ResultFields"
1268 return getattr(aq_base(self), key, self.defaultResultFields)
1269
1270
1271 - def _wand(self, where, fmt, field, value):
1272 """
1273 >>> dmd.ZenEventManager._wand('where 1=1', '%s=%s', 'a', 'b')
1274 'where 1=1 and a=b'
1275 >>> dmd.ZenEventManager._wand('where a=5', '%s=%s', 'a', 'b')
1276 'where a=5'
1277 >>> dmd.ZenEventManager._wand('where b=a', '%s=%s', 'a', 'b')
1278 'where b=a'
1279 """
1280 if value != None and where.find(field) == -1:
1281 if where: where += " and "
1282 where += fmt % (field, value)
1283 return where
1284
1285
1288 """
1289 Make a start and end date range that is at least one day long.
1290 returns a start and end date as a proper database element.
1291 """
1292 if type(enddate) == types.StringType:
1293 enddate = DateTime.DateTime(enddate, datefmt='us')
1294 enddate = enddate.latestTime()
1295 if type(startdate) == types.StringType:
1296 startdate = DateTime.DateTime(startdate, datefmt='us')
1297 startdate = startdate.earliestTime()
1298 startdate = self.dateDB(startdate)
1299 enddate = self.dateDB(enddate)
1300 return startdate, enddate
1301
1302
1303 security.declareProtected(ZEN_COMMON,'getDashboardInfo')
1322
1323
1325 """ Get devices with issues in a form suitable
1326 for a portlet on the dashboard.
1327 """
1328 mydict = {'columns':[], 'data':[]}
1329 mydict['columns'] = ['Device', 'Events']
1330 deviceinfo = self.getDeviceDashboard()
1331 for alink, pill in deviceinfo:
1332 mydict['data'].append({'Device':alink,
1333 'Events':pill})
1334 return simplejson.dumps(mydict)
1335
1337 """ Get heartbeat issues in a form suitable
1338 for a portlet on the dashboard.
1339 """
1340 mydict = {'columns':[], 'data':[]}
1341 mydict['columns'] = ['Device', 'Daemon', 'Seconds']
1342 heartbeats = self.getHeartbeat()
1343 for Device, Daemon, Seconds, dummy in heartbeats:
1344 mydict['data'].append({'Device':Device,
1345 'Daemon':Daemon, 'Seconds':Seconds})
1346 return simplejson.dumps(mydict)
1347
1348
1350 """return device info for bad device to dashboard"""
1351 devices = [d[0] for d in self.getDeviceIssues(
1352 severity=4, state=1, limit=100)]
1353 devdata = []
1354 devclass = self.getDmdRoot("Devices")
1355 getcolor = re.compile(r'class=\"evpill-(.*?)\"', re.S|re.I|re.M).search
1356 colors = "red orange yellow blue grey green".split()
1357 def pillcompare(a,b):
1358 a, b = map(lambda x:getcolor(x[1]), (a, b))
1359 def getindex(x):
1360 try:
1361 color = x.groups()[0]
1362 smallcolor = x.groups()[0].replace('-acked','')
1363 isacked = 'acked' in color
1364 index = colors.index(x.groups()[0].replace('-acked',''))
1365 if isacked: index += .5
1366 return index
1367 except: return 5
1368 a, b = map(getindex, (a, b))
1369 return cmp(a, b)
1370 for devname in devices:
1371 dev = devclass.findDevice(devname)
1372 if dev:
1373 if (not self.checkRemotePerm(ZEN_VIEW, dev)
1374 or dev.productionState < self.prodStateDashboardThresh
1375 or dev.priority < self.priorityDashboardThresh):
1376 continue
1377 if simple: alink = devname
1378 else: alink = dev.getPrettyLink()
1379 try:
1380 pill = self.getEventPillME(dev)[0]
1381 except IndexError:
1382 pill = ''
1383 evts = [alink,pill]
1384 devdata.append(evts)
1385 devdata.sort(pillcompare)
1386 return devdata
1387
1388
1405
1406
1408 """
1409 Return a map of device to production state in a format suitable for a
1410 YUI data table.
1411 """
1412 if type(prodStates)==type(''): prodStates = [prodStates]
1413 orderby, orderdir = 'id', 'asc'
1414 catalog = getattr(self.dmd.Devices, self.dmd.Devices.default_catalog)
1415 queries = []
1416 for state in prodStates:
1417 queries.append(Eq('getProdState', state))
1418 query = Or(*queries)
1419 objects = catalog.evalAdvancedQuery(query, ((orderby, orderdir),))
1420 devs = [x.getObject() for x in objects][:100]
1421 mydict = {'columns':['Device', 'Prod State'], 'data':[]}
1422 for dev in devs:
1423 if not self.checkRemotePerm(ZEN_VIEW, dev): continue
1424 mydict['data'].append({
1425 'Device' : dev.getPrettyLink(),
1426 'Prod State' : dev.getProdState()
1427 })
1428 return simplejson.dumps(mydict)
1429
1430
1436
1438 '''Build summary of serveral zope servers'''
1439 import urllib, re
1440 user = 'admin'; pw = 'zenoss'
1441 servernames = ['zenoss', 'tilde']
1442 dashurl = "<a href='http://%s:8080/zport/dmd/'>%s</a>"
1443 sumurl = 'http://%s:%s@%s:8080/zport/dmd/Systems/getEventSummary'
1444 infourl = 'http://%s:%s@%s:8080/zport/dmd/ZenEventManager/getDashboardInfo'
1445 info = {'deviceevents': [], 'systemevents': [], 'heartbeat': []}
1446
1447 def getData(user, pw, urlfmt):
1448 data = ''
1449 try:
1450 url = urlfmt % (user, pw, s)
1451 data = urllib.urlopen(url).read()
1452 if re.search('zenevents_5_noack', data):
1453 return data
1454 except IOError: pass
1455
1456 zenossdata = []
1457 for s in servernames:
1458 data = getData(user, pw, sumurl)
1459 if not data: continue
1460 evts = [ dashurl % (s, s) ]
1461 evts.extend(map(evtprep, eval(data)))
1462 zenossdata.append(evts)
1463 zenossdata.sort()
1464 info['systemevents'] = zenossdata
1465 for s in servernames:
1466 data = getData(user, pw, infourl)
1467 if not data: continue
1468 data = eval(data)
1469 info['deviceevents'].extend(data['deviceevents'])
1470 info['heartbeat'].extend(data['heartbeat'])
1471
1472 if REQUEST:
1473 REQUEST.RESPONSE.setHeader('Cache-Control', 'no-cache')
1474 REQUEST.RESPONSE.setHeader('Expires', '-1')
1475 REQUEST.RESPONSE.setHeader("Pragma", "no-cache")
1476 return info
1477
1478 security.declareProtected('View','getJSONEventsInfo')
1479 - def getJSONEventsInfo(self, context, offset=0, count=50,
1480 getTotalCount=True,
1481 startdate=None, enddate=None,
1482 filter='', severity=2, state=1,
1483 orderby='', **kwargs):
1484 """ Event data in JSON format.
1485 """
1486 if hasattr(context, 'getResultFields'):
1487 fields = context.getResultFields()
1488 else:
1489 if hasattr(context, 'event_key'): base = context
1490 else: base = self.dmd.Events
1491 fields = self.lookupManagedEntityResultFields(base.event_key)
1492 data, totalCount = self.getEventListME(context,
1493 offset=offset, rows=count, resultFields=fields,
1494 getTotalCount=getTotalCount, filter=filter, severity=severity,
1495 state=state, orderby=orderby, startdate=startdate, enddate=enddate)
1496 results = [x.getDataForJSON(fields) + [x.getCssClass()] for x in data]
1497 return simplejson.dumps((results, totalCount))
1498
1499
1500 security.declareProtected('View','getJSONFields')
1513
1514
1516 conn = self.connect()
1517 try:
1518 curs = conn.cursor()
1519 selstatement = ("SELECT AVG(CHAR_LENGTH(mycol))+20 FROM (SELECT %s AS "
1520 "mycol FROM %s WHERE %s IS NOT NULL LIMIT 500) AS "
1521 "a;") % (fieldname, self.statusTable, fieldname)
1522 curs.execute(selstatement)
1523 avglen = curs.fetchone()
1524 finally: self.close(conn)
1525 try: return float(avglen[0])
1526 except TypeError: return 10.
1527
1528
1529
1530
1531
1532
1533 security.declareProtected('Send Events', 'sendEvents')
1535 """Send a group of events to the backend.
1536 """
1537 count = 0
1538 for event in events:
1539 try:
1540 self.sendEvent(event)
1541 count += 1
1542 except Exception, ex:
1543 log.exception(ex)
1544 return count
1545
1546
1547 security.declareProtected('Send Events', 'sendEvent')
1552
1553
1554
1555
1556
1557
1571
1572
1573 security.declareProtected("View", "getFieldList")
1575 """Return a list of all fields in the status table of the backend.
1576 """
1577 if not getattr(self, '_fieldlist', None):
1578 self.loadSchema()
1579 return self._fieldlist
1580
1585
1587 """Return a list of possible event actions.
1588 """
1589 return self.eventActions
1590
1591 security.declareProtected(ZEN_COMMON,'getSeverities')
1593 """Return a list of tuples of severities [('Warning', 3), ...]
1594 """
1595 return self.severityConversions
1596
1598 """Return a string representation of the severity.
1599 """
1600 try:
1601 return self.severities[severity]
1602 except KeyError:
1603 return "Unknown"
1604
1606 """Return a list of tuples of priorities [('Warning', 3), ...]
1607 """
1608 return self.priorityConversions
1609
1617
1622
1623
1625 ''' Return the img source for a status number
1626 '''
1627 if status < 0:
1628 src = 'grey'
1629 elif status == 0:
1630 src = 'green'
1631 elif status == 1:
1632 src = 'yellow'
1633 elif status == 2:
1634 src = 'yellow'
1635 else:
1636 src = 'red'
1637 return '/zport/dmd/img/%s_dot.png' % src
1638
1639
1641 """return the css class name to be used for this event.
1642 """
1643 value = severity < 0 and "unknown" or severity
1644 acked = acked and "acked" or "noack"
1645 return "zenevents_%s_%s %s" % (value, acked, acked)
1646
1647
1649 """Check to see if a column is of type date.
1650 """
1651 if not self._schema:
1652 self.getFieldList()
1653 return self._schema.get(colName, False)
1654
1655
1662
1663
1664
1673
1674
1676 """Prepare string values for db by escaping special characters.
1677 """
1678 raise NotImplementedError
1679
1680
1682 """Load schema from database. If field is a date set value to true."""
1683 schema = {}
1684 fieldlist = []
1685 sql = "describe %s;" % self.statusTable
1686 conn = self.connect()
1687 try:
1688 curs = conn.cursor()
1689 curs.execute(sql)
1690 for row in curs.fetchall():
1691 fieldlist.append(row[0])
1692 col = self.cleanstring(row[0])
1693 if self.backend == "omnibus":
1694 type = row[1] in (1, 4, 7, 8)
1695 elif self.backend == "mysql":
1696 type = row[1] in ("datetime", "timestamp", "double")
1697 schema[col] = type
1698 if schema: self._schema = schema
1699 self._fieldlist = fieldlist
1700 finally: self.close(conn)
1701
1702
1704 """Are there event controls on this event list.
1705 """
1706 if self.isManager() and self.statusTable in ["status","history"]:
1707 return 1
1708 return 0
1709
1710 - def updateEvents(self, stmt, whereClause, reason,
1711 table="status", toLog=True):
1712 userId = getSecurityManager().getUser().getId()
1713 insert = 'INSERT INTO log (evid, userName, text) ' + \
1714 'SELECT evid, "%s", "%s" ' % (userId, reason) + \
1715 'FROM %s ' % table + whereClause
1716 query = stmt + ' ' + whereClause
1717 conn = self.connect()
1718 try:
1719 curs = conn.cursor()
1720 if toLog: curs.execute(insert)
1721 curs.execute(query)
1722 finally: self.close(conn)
1723 self.clearCache()
1724 self.manage_clearCache()
1725
1726 security.declareProtected('Manage Events','manage_addEvent')
1728 ''' Create an event from user supplied data
1729 '''
1730 eventDict = dict(
1731 summary = REQUEST.get('summary', ''),
1732 message = REQUEST.get('message', ''),
1733 device = REQUEST.get('device', ''),
1734 component = REQUEST.get('component', ''),
1735 severity = REQUEST.get('severity', ''),
1736 eventClassKey = REQUEST.get('eventClassKey', ''),
1737 )
1738
1739
1740 if REQUEST.get('eclass', None):
1741 eventDict['eventClass'] = REQUEST['eclass']
1742
1743 if not eventDict['device'] and not eventDict['component']:
1744 if REQUEST:
1745 REQUEST['message'] = 'You must specify a device and/or a component.'
1746 return self.callZenScreen(REQUEST)
1747 else:
1748 return
1749 evid = self.sendEvent(eventDict)
1750 if REQUEST:
1751 REQUEST['RESPONSE'].redirect('/zport/dmd/Events/viewEvents')
1752
1753
1755 self.updateEvents('DELETE FROM status', whereClause, reason)
1756
1757 security.declareProtected('Manage Events','manage_deleteEvents')
1759 "Delete the given event ids"
1760 if type(evids) == type(''):
1761 evids = [evids]
1762 num = len(evids)
1763 if evids:
1764 evids = ",".join([ "'%s'" % evid for evid in evids])
1765 whereClause = ' where evid in (%s)' % evids
1766 self.deleteEvents(whereClause, 'Deleted by user')
1767 if REQUEST:
1768 REQUEST['message'] = 'Moved %s event%s to History.' % (
1769 num, (num != 1 and 's') or '')
1770 return self.callZenScreen(REQUEST)
1771
1773 fields = ','.join( self.getFieldList() )
1774
1775 fields = fields.replace('clearid','NULL')
1776 self.updateEvents( 'INSERT status ' + \
1777 'SELECT %s FROM history' % fields,
1778 whereClause + \
1779 ' ON DUPLICATE KEY UPDATE status.count=status.count+history.count',
1780 reason, 'history', toLog=False)
1781 self.updateEvents( 'DELETE FROM history', whereClause, \
1782 reason, 'history')
1783
1784 security.declareProtected('Manage Events','manage_undeleteEvents')
1786 "Move the given event ids into status and delete from history"
1787 if type(evids) == type(''):
1788 evids = [evids]
1789 num = len(evids)
1790 if evids:
1791 evids = ",".join([ "'%s'" % evid for evid in evids])
1792 whereClause = ' where evid in (%s)' % evids
1793 self.undeleteEvents(whereClause, 'Undeleted by user')
1794 if REQUEST:
1795 REQUEST['message'] = "%s events undeleted." % num
1796 return self.callZenScreen(REQUEST)
1797
1798 security.declareProtected('Manage Events','manage_deleteAllEvents')
1800 "Delete the events for a given Device (used for deleting the device"
1801 whereClause = 'where device = "%s"' % devname
1802 self.deleteEvents(whereClause, 'Device deleted')
1803 if REQUEST:
1804 REQUEST['message'] = 'Deleted all events for %s' % devname
1805 return self.callZenScreen(REQUEST)
1806
1807
1808 security.declareProtected('Manage Events','manage_deleteHeartbeat')
1820
1821
1822 security.declareProtected('Manage Events','manage_ackEvents')
1824 "Ack the given event ids"
1825 if type(evids) == type(''):
1826 evids = [evids]
1827 request = FakeRequest()
1828 self.manage_setEventStates(1 , evids, request)
1829 if REQUEST:
1830 dest = '/zport/dmd/Events/viewEvents'
1831 if request.get('message', ''):
1832 dest += '?message=%s' % request['message']
1833 if not getattr(REQUEST, 'dontRedirect', False):
1834 REQUEST['RESPONSE'].redirect(dest)
1835
1836
1837 security.declareProtected('Manage Events','manage_setEventStates')
1839 reason = None
1840 if eventState and evids:
1841 eventState = int(eventState)
1842 userid = ""
1843 if eventState > 0: userid = getSecurityManager().getUser()
1844 update = "update status set eventState=%s, ownerid='%s' " % (
1845 eventState, userid)
1846 whereClause = "where evid in ("
1847 whereClause += ",".join([ "'%s'" % evid for evid in evids]) + ")"
1848 reason = 'Event state changed to '
1849 try:
1850 reason += self.eventStateConversions[eventState][0]
1851 except KeyError:
1852 reason += 'unknown (%d)' % eventState
1853 self.updateEvents(update, whereClause, reason)
1854 if REQUEST:
1855 if reason:
1856 REQUEST['message'] = reason
1857 else:
1858 REQUEST['message'] = 'no reason'
1859 return self.callZenScreen(REQUEST)
1860
1861
1862 security.declareProtected('Manage Events','manage_setEventStates')
1865 """Create an event map from an event or list of events.
1866 """
1867 evclass = None
1868 evmap = None
1869 numCreated = 0
1870 numNotUnknown = 0
1871 numNoKey = 0
1872 if eventClass and evids:
1873 evclass = self.getDmdRoot("Events").getOrganizer(eventClass)
1874 sel = """select eventClassKey, eventClass, message
1875 from %s where evid in ('%s')"""
1876 sel = sel % (self.statusTable, "','".join(evids))
1877 conn = self.connect()
1878 try:
1879 curs = conn.cursor()
1880 curs.execute(sel);
1881 for row in curs.fetchall():
1882 evclasskey, curevclass, msg = row
1883 if curevclass != Unknown:
1884 numNotUnknown += 1
1885 continue
1886 if not evclasskey:
1887 numNoKey += 1
1888 continue
1889 evmap = evclass.createInstance(evclasskey)
1890 evmap.eventClassKey = evclasskey
1891 evmap.example = msg
1892 numCreated += 1
1893 finally: self.close(conn)
1894 elif REQUEST:
1895 if not evids:
1896 REQUEST['message'] = 'No events selected.'
1897 elif not eventClass:
1898 REQUEST['message'] = 'No event class selected.'
1899
1900 if REQUEST:
1901 msg = REQUEST.get('message', '')
1902 if numNotUnknown:
1903 msg += ((msg and ' ') +
1904 '%s event%s %s not class /Unknown.' % (
1905 numNotUnknown,
1906 (numNotUnknown != 1 and 's') or '',
1907 (numNotUnknown != 1 and 'are') or 'is'))
1908 if numNoKey:
1909 msg += ((msg and ' ') +
1910 '%s event%s %s not have an event class key.' % (
1911 numNoKey,
1912 (numNoKey != 1 and 's') or '',
1913 (numNoKey != 1 and 'do') or 'does'))
1914 msg += (msg and ' ') + 'Created %s event mapping%s.' % (
1915 numCreated,
1916 (numCreated != 1 and 's') or '')
1917 REQUEST['message'] = msg
1918
1919
1920 if getattr(REQUEST, 'dontRender', False):
1921 return ''
1922 if len(evids) == 1 and evmap: return evmap()
1923 elif evclass and evmap: return evclass()
1924
1925
1926 security.declareProtected('Manage EventManager','manage_refreshConversions')
1928 """get the conversion information from the omnibus server"""
1929 assert(self == self.dmd.ZenEventManager)
1930 self.loadSchema()
1931 self.dmd.ZenEventHistory.loadSchema()
1932 if REQUEST:
1933 REQUEST['message'] = 'Event schema refreshed'
1934 return self.callZenScreen(REQUEST)
1935
1936
1937 security.declareProtected('Manage EventManager','manage_editCache')
1939 """Reset cache values"""
1940 self.timeout = int(timeout)
1941 self.clearthresh = int(clearthresh)
1942 if REQUEST:
1943 message = "Cache parameters set"
1944 return self.editCache(self, REQUEST, manage_tabs_message=message)
1945
1946
1947 security.declareProtected('Manage EventManager','manage_clearCache')
1956
1957
1958 security.declareProtected('Manage EventManager','manage_editEventManager')
1960 ''' Call zmanage_editProperties then take care of saving a few
1961 values to ZenEventHistory
1962 '''
1963 assert(self == self.dmd.ZenEventManager)
1964 self.zmanage_editProperties(REQUEST)
1965 self.dmd.ZenEventHistory.timeout = REQUEST['history_timeout']
1966 self.dmd.ZenEventHistory.clearthresh = REQUEST['history_clearthresh']
1967 self.dmd.ZenEventHistory.username = self.dmd.ZenEventManager.username
1968 self.dmd.ZenEventHistory.password = self.dmd.ZenEventManager.password
1969 self.dmd.ZenEventHistory.database = self.dmd.ZenEventManager.database
1970 self.dmd.ZenEventHistory.host = self.dmd.ZenEventManager.host
1971 self.dmd.ZenEventHistory.port = self.dmd.ZenEventManager.port
1972 if REQUEST: return self.callZenScreen(REQUEST)
1973
1974
1975 security.declareProtected('Manage EventManager','manage_clearHeartbeats')
1987
1988 security.declareProtected('Manage EventManager','zmanage_editProperties')
1990 ''' Need to handle editing of history event fields differently
1991 '''
1992 assert(self == self.dmd.ZenEventManager)
1993 screenName = REQUEST.get('zenScreenName', '')
1994 if screenName == 'editEventManagerHistoryFields':
1995 obj = self.dmd.ZenEventHistory
1996 else:
1997 obj = self
1998 if screenName == 'editEventManager':
1999
2000
2001 if REQUEST.has_key('mysql_pass'):
2002 REQUEST.form['password'] = REQUEST['mysql_pass']
2003 ZenModelRM.zmanage_editProperties(obj, REQUEST)
2004 if REQUEST: return self.callZenScreen(REQUEST)
2005
2006 security.declareProtected('Manage EventManager', 'manage_addLogMessage')
2008 'Add a log message to an event'
2009 if not evid:
2010 return
2011 userId = getSecurityManager().getUser().getId()
2012 conn = self.connect()
2013 try:
2014 curs = conn.cursor()
2015 insert = 'INSERT INTO log (evid, userName, text) '
2016 insert += 'VALUES ("%s", "%s", "%s")' % (evid,
2017 userId,
2018 conn.escape_string(message))
2019 curs.execute(insert)
2020 finally: self.close(conn)
2021 self.clearCache('evid' + evid)
2022 if REQUEST: return self.callZenScreen(REQUEST)
2023
2024 security.declareProtected('Manage EventManager', 'manage_addCommand')
2030
2031 security.declareProtected('Manage EventManager', 'manage_deleteCommands')
2037
2038
2039
2040
2041
2042
2044 """globally unique id based on timestamp, fqdn, and random number.
2045 """
2046 d=datetime.datetime.utcnow().strftime("%Y%m%d%H%M%S")
2047 r = "%04d" % random.randint(0, 1000)
2048 return d+str(d.microsecond)+r+self.FQDNID
2049
2050
2052 """Install skins into portal.
2053 """
2054 from Products.CMFCore.utils import getToolByName
2055 from Products.CMFCore.DirectoryView import addDirectoryViews
2056 from cStringIO import StringIO
2057 import string
2058
2059 out = StringIO()
2060 skinstool = getToolByName(self, 'portal_skins')
2061 if 'zenevents' not in skinstool.objectIds():
2062 addDirectoryViews(skinstool, 'skins', globals())
2063 out.write("Added 'zenevents' directory view to portal_skins\n")
2064 skins = skinstool.getSkinSelections()
2065 for skin in skins:
2066 path = skinstool.getSkinPath(skin)
2067 path = map(string.strip, string.split(path,','))
2068 if 'zenevents' not in path:
2069 try: path.insert(path.index('zenmodel'), 'zenevents')
2070 except ValueError:
2071 path.append('zenevents')
2072 path = string.join(path, ', ')
2073 skinstool.addSkinSelection(skin, path)
2074 out.write("Added 'zenevents' to %s skin\n" % skin)
2075 else:
2076 out.write(
2077 "Skipping %s skin, 'zenevents' is already set up\n" % skin)
2078 return out.getvalue()
2079