1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__ = """EventManagerBase
15 Data connector to backend of the event management system.
16 """
17
18 import time
19 import types
20 import random
21 import re
22 random.seed()
23 import logging
24 log = logging.getLogger("zen.Events")
25
26 from AccessControl import ClassSecurityInfo
27 from AccessControl import getSecurityManager
28 from Globals import DTMLFile
29 from Acquisition import aq_base
30 import DateTime
31 from Products.ZenModel.ZenossSecurity import *
32
33 from Products.ZenUtils.ObjectCache import ObjectCache
34 from Products.ZenUtils.jsonutils import unjson
35
36 from ZEvent import ZEvent
37 from EventDetail import EventDetail
38 from BetterEventDetail import BetterEventDetail
39 from EventCommand import EventCommand
40 from Products.ZenEvents.Exceptions import *
41 from Products.AdvancedQuery import MatchRegexp, In, And
42
43 from Products.ZenModel.ZenModelRM import ZenModelRM
44 from Products.ZenModel.ZenossSecurity import *
45 from Products.ZenRelations.RelSchema import *
46 from Products.ZenUtils import Time
47 from Products.ZenUtils.FakeRequest import FakeRequest
48 from Products.ZenEvents.ZenEventClasses import Status_Ping, Status_Wmi_Conn
49 from Products.ZenWidgets import messaging
50 from Products.ZenUI3.browser.eventconsole.columns import COLUMN_CONFIG
51
52 from ZenEventClasses import Unknown
53
54 from DbAccessBase import DbAccessBase
55
56 from Products.ZenUtils.Utils import unused
57
58 __pychecker__="maxargs=16"
59
61 """
62 Prepares data from L{Products.ZenEvents.EventManagerBase.getEventSummary}
63 for rendering in the eventrainbow template macro.
64
65 Each cell of the old-style event rainbow needs a CSS class specified in
66 order to render its color, fill and border style. evtprep determines the
67 proper class and returns it, along with a string representation of the
68 number of live and acknowledged events.
69
70 >>> from Products.ZenEvents.EventManagerBase import evtprep
71 >>> evtprep(['zenevents_5_noack noack', 2, 2])
72 {'cssclass': 'zenevents_5_noack noack empty thin', 'data': '2/2'}
73 >>> evtprep(['zenevents_5_noack noack', 1, 2])
74 {'cssclass': 'zenevents_5_noack noack', 'data': '1/2'}
75
76 @param evts: A tuple of the form (Severity string, Number Acknowledged int,
77 Number Live int)
78 @type evts: tuple
79 @return: A dictionary of the form {'cssclass': Class string, 'data': Event
80 count representation string}
81
82 """
83 evtsdata = "%d/%d" % (evts[1],evts[2])
84 if evts[1]==evts[2] or evts[2]==0:
85 return {'cssclass':evts[0] + " empty thin",
86 'data':evtsdata}
87 else:
88 return {'cssclass':evts[0], 'data':evtsdata}
89
90
92 """
93 Data connector to backend of the event management system.
94 """
95
96
97
98
99 eventStateConversions = (
100 ('New', 0),
101 ('Acknowledged',1),
102 ('Suppressed', 2),
103
104 )
105
106 eventActions = ('status', 'history', 'drop')
107
108 severityConversions = (
109 ('Critical', 5),
110 ('Error', 4),
111 ('Warning', 3),
112 ('Info', 2),
113 ('Debug', 1),
114 ('Clear', 0),
115 )
116 severities = dict([(b, a) for a, b in severityConversions])
117
118 priorityConversions = (
119 ('None', -1),
120 ('Emergency', 0),
121 ('Alert', 1),
122 ('Critical', 2),
123 ('Error', 3),
124 ('Warning', 4),
125 ('Notice', 6),
126 ('Info', 8),
127 ('Debug', 10),
128 )
129 priorities = dict([(b, a) for a, b in priorityConversions])
130
131 statusTable = "status"
132 detailTable = "detail"
133 logTable = "log"
134 lastTimeField = "lastTime"
135 firstTimeField = "firstTime"
136 deviceField = "device"
137 componentField = "component"
138 eventClassField = "eventClass"
139 severityField = "severity"
140 stateField = "eventState"
141 countField = "count"
142 prodStateField = "prodState"
143 DeviceGroupField = "DeviceGroups"
144 SystemField = "Systems"
145
146 DeviceWhere = "\"device = '%s'\" % me.getDmdKey()"
147 DeviceResultFields = ("eventState", "severity", "component", "eventClass",
148 "summary", "firstTime", "lastTime", "count" )
149 ComponentWhere = ("\"(device = '%s' and component = '%s')\""
150 " % (me.device().getDmdKey(), me.name())")
151 ComponentResultFields = ("eventState", "severity", "eventClass", "summary",
152 "firstTime", "lastTime", "count" )
153 IpAddressWhere = "\"ipAddress='%s'\" % (me.getId())"
154 EventClassWhere = "\"eventClass like '%s%%'\" % me.getDmdKey()"
155 EventClassInstWhere = """\"eventClass = '%s' and eventClassKey = '%s'\" % (\
156 me.getEventClass(), me.eventClassKey)"""
157 DeviceClassWhere = "\"(DeviceClass = '%s' or DeviceClass like '%s/%%') \" % \
158 ( me.getDmdKey(), me.getDmdKey() )"
159
160 LocationWhere = "\"Location like '%s%%'\" % me.getDmdKey()"
161 SystemWhere = "\"Systems like '%%|%s%%'\" % me.getDmdKey()"
162 DeviceGroupWhere = "\"DeviceGroups like '%%|%s%%'\" % me.getDmdKey()"
163
164 defaultResultFields = ("eventState", "severity", "device", "component",
165 "eventClass", "summary", "firstTime", "lastTime",
166 "count" )
167
168 defaultFields = ('eventState', 'severity', 'evid')
169
170 defaultEventId = ('device', 'component', 'eventClass',
171 'eventKey', 'severity')
172
173 requiredEventFields = ('device', 'summary', 'severity')
174
175 refreshConversionsForm = DTMLFile('dtml/refreshNcoProduct', globals())
176
177 defaultAvailabilityDays = 7
178 defaultPriority = 3
179 eventAgingHours = 4
180 eventAgingSeverity = 4
181 historyMaxAgeDays = 0
182
183 _properties = (
184 {'id':'backend', 'type':'string','mode':'r', },
185 {'id':'username', 'type':'string', 'mode':'w'},
186 {'id':'password', 'type':'string', 'mode':'w'},
187 {'id':'host', 'type':'string', 'mode':'w'},
188 {'id':'database', 'type':'string', 'mode':'w'},
189 {'id':'port', 'type':'int', 'mode':'w'},
190 {'id':'defaultWhere', 'type':'text', 'mode':'w'},
191 {'id':'defaultOrderby', 'type':'text', 'mode':'w'},
192 {'id':'defaultResultFields', 'type':'lines', 'mode':'w'},
193 {'id':'statusTable', 'type':'string', 'mode':'w'},
194 {'id':'detailTable', 'type':'string', 'mode':'w'},
195 {'id':'logTable', 'type':'string', 'mode':'w'},
196 {'id':'lastTimeField', 'type':'string', 'mode':'w'},
197 {'id':'firstTimeField', 'type':'string', 'mode':'w'},
198 {'id':'deviceField', 'type':'string', 'mode':'w'},
199 {'id':'componentField', 'type':'string', 'mode':'w'},
200 {'id':'severityField', 'type':'string', 'mode':'w'},
201 {'id':'countField', 'type':'string', 'mode':'w'},
202 {'id':'DeviceGroupField', 'type':'string', 'mode':'w'},
203 {'id':'SystemField', 'type':'string', 'mode':'w'},
204 {'id':'DeviceWhere', 'type':'string', 'mode':'w'},
205 {'id':'DeviceResultFields', 'type':'lines', 'mode':'w'},
206 {'id':'ComponentResultFields', 'type':'lines', 'mode':'w'},
207 {'id':'EventClassWhere', 'type':'string', 'mode':'w'},
208 {'id':'EventClassInstWhere', 'type':'string', 'mode':'w'},
209 {'id':'DeviceClassWhere', 'type':'string', 'mode':'w'},
210 {'id':'LocationWhere', 'type':'string', 'mode':'w'},
211 {'id':'SystemWhere', 'type':'string', 'mode':'w'},
212 {'id':'DeviceGroupWhere', 'type':'string', 'mode':'w'},
213 {'id':'requiredEventFields', 'type':'lines', 'mode':'w'},
214 {'id':'defaultEventId', 'type':'lines', 'mode':'w'},
215 {'id':'defaultFields', 'type':'lines', 'mode':'w'},
216 {'id':'timeout', 'type':'int', 'mode':'w'},
217 {'id':'clearthresh', 'type':'int', 'mode':'w'},
218 {'id':'defaultAvailabilityDays', 'type':'int', 'mode':'w'},
219 {'id':'defaultPriority', 'type':'int', 'mode':'w'},
220 {'id':'eventAgingHours', 'type':'int', 'mode':'w'},
221 {'id':'eventAgingSeverity', 'type':'int', 'mode':'w'},
222 {'id':'historyMaxAgeDays', 'type':'int', 'mode':'w'},
223 )
224
225 _relations = (
226 ("commands", ToManyCont(ToOne, "Products.ZenEvents.EventCommand", "eventManager")),
227 )
228
229 factory_type_information = (
230 {
231 'immediate_view' : 'editEventManager',
232 'actions' :
233 (
234 { 'id' : 'edit'
235 , 'name' : 'Edit'
236 , 'action' : 'editEventManager'
237 , 'permissions' : ( "Manage DMD", )
238 },
239 { 'id' : 'edit'
240 , 'name' : 'Fields'
241 , 'action' : 'editEventManagerFields'
242 , 'permissions' : ( "Manage DMD", )
243 },
244 { 'id' : 'history_edit'
245 , 'name' : 'History Fields'
246 , 'action' : 'editEventManagerHistoryFields'
247 , 'permissions' : ( "Manage DMD", )
248 },
249 { 'id' : 'commands'
250 , 'name' : 'Commands'
251 , 'action' : 'listEventCommands'
252 , 'permissions' : ( "Manage DMD", )
253 },
254 { 'id' : 'changes'
255 , 'name' : 'Modifications'
256 , 'action' : 'viewNewHistory'
257 , 'permissions' : (ZEN_VIEW_MODIFICATIONS,)
258 },
259 )
260 },
261 )
262
263 security = ClassSecurityInfo()
264
265
266 - def __init__(self, id, title='', hostname='localhost', username='root',
267 password='', database='events', port=3306,
268 defaultWhere='',defaultOrderby='',defaultResultFields=[]):
269 """
270 Sets up event database access and initializes the cache.
271
272 @param id: A unique id
273 @type id: string
274 @param title: A title
275 @type title: string
276 @param hostname: The hostname of the events database server
277 @type hostname: string
278 @param username: The name of a user with permissions to access the
279 events database
280 @type username: string
281 @param password: The password of the user
282 @type password: string
283 @param database: The name of the events database
284 @type database: string
285 @param port: The port on which the database server is listening
286 @type port: int
287 @param defaultWhere: The default where clause to use when building
288 queries
289 @type defaultWhere: string
290 @param defaultOrderby: The default order by clause to use when building
291 queries
292 @type defaultOrderby: string
293 @param defaultResultFields: DEPRECATED. Currently unused.
294 @type defaultResultFields: list
295
296 """
297 unused(defaultOrderby, defaultResultFields)
298 self.id = id
299 self.title = title
300 self.username=username
301 self.password=password
302 self.database=database
303 self.host=hostname
304 self.port=port
305 DbAccessBase.__init__(self)
306
307 self.defaultWhere = defaultWhere
308 self.defaultOrderby="%s desc, %s desc" % (
309 self.severityField, self.lastTimeField)
310
311 self._schema = {}
312 self._fieldlist = []
313 self._colors = ()
314 self._ackedcolors = ()
315 ObjectCache.__init__(self)
316 self.initCache()
317
318
319
320
321
322
324 """
325 A wrapper for L{lookupManagedEntityResultFields} accepting an object
326 with an C{event_key} attribute.
327
328 >>> class dummy(object):
329 ... event_key = 'Device'
330 ...
331 >>> d = dummy()
332 >>> f = dmd.ZenEventManager.getEventResultFields(d)
333 >>> f==dmd.ZenEventManager.DeviceResultFields
334 True
335 >>> d.event_key = 'Robot'
336 >>> f = dmd.ZenEventManager.getEventResultFields(d)
337 >>> f==dmd.ZenEventManager.defaultResultFields
338 True
339
340 @param context: An object with an C{event_key} attribute.
341 @type context: L{ManagedEntity}
342 @return: A sequence of strings representing columns in the database.
343 @rtype: tuple
344 """
345 return self.lookupManagedEntityResultFields(getattr(context,
346 'event_key', 'Default'))
347
368
369
371 """
372 Create SQL representing conditions that match passed filters and append
373 it to a given where clause.
374
375 Expressions are built using various criteria, including the type of
376 filter widget representing the column and factors specific to the
377 column itself.
378
379 All C{textfield} columns except C{count} will be matched as regular
380 expressions.
381
382 C{count} may be passed as a comparison expression such as ">=2", "=8",
383 "<10", etc., or an integer, in which case it will be evaluated as
384 ">=n".
385
386 C{firstTime} and C{lastTime} are turned into seconds since the epoch,
387 then evaluated as '>=n' and '<=n', respectively.
388
389 C{multiselectmenu} columns (prodState, severity, eventState, priority)
390 arrive as a list of values to match, and become 'and (field=v1 or
391 field=v2...)' where field is the column and vn is the value.
392
393 @param where: The where clause to which to append further conditions
394 @type where: str
395 @param filters: Values for which to create filters (e.g.,
396 {'device':'^loc.*$', 'severity':[4, 5]})
397 @type filters: dict or JSON str representing dict
398 @param values: Clause will be parameterized and the values list will be
399 populated with the values for the query
400 @type values: list
401 """
402 queryValues = []
403 newwhere = ''
404 if filters is None: filters = {}
405 elif isinstance(filters, basestring):
406 filters = unjson(filters)
407 for k,v in filters.items():
408 ftype = COLUMN_CONFIG[k].get('filter', 'textfield')
409 if isinstance(ftype, dict):
410 ftype = ftype['xtype']
411 if k=='device':
412
413 deviceids = self._getDeviceIdsMatching(v)
414 if deviceids:
415 newwhere += " and (device REGEXP %%s OR device IN ('%s')) " % "','".join(deviceids)
416 else:
417 newwhere += " and (%s REGEXP %%s) " % (k,)
418 queryValues.append(v)
419 elif k=='component':
420 componentWhere = ""
421 componentids = self._getComponentIdsMatching(v)
422 if componentids:
423 componentWhere = " OR component IN ('%s') " % "','".join(componentids)
424
425
426 newwhere += ' and ((%s REGEXP %%s) %s) ' % (k,componentWhere)
427 queryValues.append(v)
428 elif k=='count':
429 m = re.match(r'^(?P<condition>\>=|\<=|\>|\<|=)?\s*(?P<value>[0-9]+)$', v.strip())
430 if m:
431 cond = m.group('condition') if m.group('condition') else '='
432 queryValues.append(m.group('value'))
433
434 newwhere += ' and count %s %%s' % cond
435 else:
436 raise ValueError('%s is not a valid count' % v)
437
438 elif ftype=='textfield':
439 newwhere += ' and (%s REGEXP %%s) ' % (k,)
440 queryValues.append(v)
441 elif k=='firstTime':
442 v2 = Time.isoToTimestamp(v)
443 newwhere += ' and %s >= %%s ' % (k,)
444 queryValues.append(v2)
445 elif k=='lastTime':
446 v2 = Time.isoToTimestamp(v)
447 newwhere += ' and %s >= %%s ' % (k, )
448 queryValues.append(v2)
449 elif ftype=='multiselectmenu':
450 if isinstance(v, basestring): v = (v,)
451 if len(v)>0:
452 sevstr = ' or '.join(['%s=%%s' % (k,) for s in v])
453 queryValues.extend(v)
454 newwhere += ' and (%s) ' % sevstr
455 values.extend(queryValues)
456 return where + newwhere
457
458
460 """
461 Used by both the id matching sub queries
462 this gets brain results and returns a list of
463 ids
464 @param catalog: catalog we are searching
465 @param querySet: Advanced Query Search Terms
466 @return [string]: all the ids of the search results that match
467 the queryset
468 """
469 try:
470 brains = catalog.evalAdvancedQuery(querySet)
471 except:
472
473 return None
474
475 ids = []
476 for brain in brains:
477 ids.append(brain.id)
478 return ids
479
481 """
482 This returns a list of component ids where the titleOrId
483 matches the searchTerm
484 @param searchTerm: query for which we want to search for (can be regex)
485 """
486 catalog = self.dmd.global_catalog
487 querySet = [MatchRegexp('name', '.*%s.*' % searchTerm),
488 In('objectImplements',
489 'Products.ZenModel.DeviceComponent.DeviceComponent')]
490 return self._filterCatalogResultsForIds(catalog, And(*querySet))
491
493 """
494 This returns a list of device ids where the titleOrId
495 matches the searchTerm
496 @param searchTerm: query for which we want to search for (can be regex)
497 """
498 catalog = self.dmd.Devices.deviceSearch
499 if globSearch:
500 searchTerm = '.*%s.*' % searchTerm
501 querySet = MatchRegexp('titleOrId', searchTerm)
502 return self._filterCatalogResultsForIds(catalog, querySet)
503
504 - def getEventBatchME(self, me, selectstatus=None, resultFields=[],
505 where="", orderby="", severity=None, state=2,
506 startdate=None, enddate=None, offset=0, rows=0,
507 getTotalCount=False, filter="", goodevids=[],
508 badevids=[], **kwargs):
509 """
510 Returns a batch of events based on criteria from checked rows on the
511 event console.
512
513 The event console can show thousands of events, and we want to support a
514 "Select All" feature; enter this method. It builds a query based on the
515 select status from the console ("All", "None", "Acknowledged",
516 "Unacknowledged") and any checkboxes that have been modified manually.
517
518 @param me: The managed entity for which to query events.
519 @type me: L{ManagedEntity}
520 @param resultFields: The columns to return from the database.
521 @type resultFields: list
522 @param where: The base where clause to modify.
523 @type where: string
524 @param orderby: The "ORDER BY" string governing sort order.
525 @type orderby: string
526 @param severity: The minimum severity for which to query.
527 @type severity: int
528 @param state: The minimum state for which to query.
529 @type state: int
530 @param startdate: The early date limit
531 @type startdate: string, DateTime
532 @param enddate: The late date limit
533 @type enddate: string, DateTime
534 @param offset: The row at which to begin returning
535 @type offset: int
536 @param rows: DEPRECATED The number of rows to return (ignored).
537 @type rows: int
538 @param getTotalCount: Whether or not to return a count of the total
539 number of rows
540 @type getTotalCount: bool
541 @param filter: A glob by which to filter events
542 @type filter: string
543 @param goodevids: Ids of events that specifically should be included
544 @type goodevids: list
545 @param badevids: Ids of events that specifically should not be included
546 @type badevids: list
547 @return: Ids of matching events
548 @rtype: list
549 @todo: Remove unused parameters from the method definition
550 """
551 unused(getTotalCount, rows)
552 newwhere = self.lookupManagedEntityWhere(me)
553 if where: newwhere = self._wand(newwhere, '%s%s', where, '')
554 where = newwhere
555 badevidsstr, goodevidsstr = '',''
556 if not isinstance(goodevids, (list, tuple)): goodevids = [goodevids]
557 if not isinstance(badevids, (list, tuple)): badevids = [badevids]
558 if badevids: badevidsstr = " and evid not in ('%s')" %(
559 "','".join(badevids))
560 if goodevids: goodevidsstr = " and evid in ('%s')" %(
561 "','".join(goodevids))
562 if selectstatus=='all':
563 where += badevidsstr
564 elif selectstatus=='none':
565 where += goodevidsstr or ' and 0'
566 elif selectstatus=='acked':
567 oper = bool(goodevidsstr) and ' or' or ' and'
568 where += goodevidsstr + oper + " (eventstate=1 %s) " % badevidsstr
569 elif selectstatus=='unacked':
570 oper = bool(goodevidsstr) and ' or' or 'and'
571 where += goodevidsstr + oper + " (eventstate=0 %s) " % badevidsstr
572 try:
573 resultFields = kwargs['resultFields']; del kwargs['resultFields']
574 except KeyError:
575 resultFields = self.lookupManagedEntityResultFields(me.event_key)
576 events = self.getEventList(
577 filter=filter,
578 offset=offset,
579 getTotalCount=False,
580 startdate=startdate,
581 enddate=enddate, severity=severity,
582 state=state, orderby=orderby,
583 resultFields=resultFields,
584 where=where,**kwargs)
585 return [ev.evid for ev in events]
586
587 - def getEventIds(self, me, selectstatus=None,
588 orderby="", filters=None, evids=[],
589 excludeIds=[], asof=None):
590 """
591 Returns a batch of events based on criteria from checked rows on the
592 event console.
593
594 The event console can show thousands of events, and we want to support a
595 "Select All" feature; enter this method. It builds a query based on the
596 select status from the console ("All", "New", "Acknowledged",
597 "Suppressed") and any checkboxes that have been modified manually.
598
599 @param me: The managed entity for which to query events.
600 @type me: L{ManagedEntity}
601 @param orderby: The "ORDER BY" string governing sort order.
602 @type orderby: string
603 @param filters: Values for which to create filters (e.g.,
604 {'device':'^loc.*$', 'severity':[4, 5]})
605 @type filters: dict or JSON str representing dict
606 @param evids: Ids of events that specifically should be included
607 @type evids: list
608 @param excludeIds: Ids of events that specifically should not be included
609 @type excludeIds: list
610 @return: Ids of matching events
611 @rtype: set
612 """
613 where = self.lookupManagedEntityWhere(me)
614 eventids = set()
615 if evids:
616 eventids.update(evids)
617 if selectstatus:
618 if selectstatus != 'All':
619 eventStates= dict(self.eventStateConversions)
620 assert selectstatus in (eventStates.keys())
621 where = self._wand(where, '%s = %s', self.stateField,
622 eventStates[selectstatus])
623 if asof:
624 if where: where += ' and '
625 where += (" not (stateChange>FROM_UNIXTIME(%s) "
626 "and eventState=0)" % self.dateDB(asof))
627 events = self.getEventList( resultFields=['evid'],
628 filters=filters,
629 getTotalCount=False,
630 orderby=orderby,
631 where=where)
632 for ev in events:
633 eventids.add(ev.evid)
634
635 if eventids and excludeIds:
636 eventids.difference_update(excludeIds)
637 return eventids
638
640 """This is a hook do not delete me!"""
641 return where
642
643 - def getEventList(self, resultFields=None, where="", orderby="",
644 severity=None, state=2, startdate=None, enddate=None, offset=0,
645 rows=0, getTotalCount=False, filter="", filters=None,
646 parameterizedWhere=None, **kwargs):
647 """
648 Fetch a list of events from the database matching certain criteria.
649
650 @param resultFields: The columns to return from the database.
651 @type resultFields: list
652 @param where: The base where clause to modify.
653 @type where: string
654 @param orderby: The "ORDER BY" string governing sort order.
655 @type orderby: string
656 @param severity: The minimum severity for which to query.
657 @type severity: int
658 @param state: The minimum state for which to query.
659 @type state: int
660 @param startdate: The early date limit
661 @type startdate: string, DateTime
662 @param enddate: The late date limit
663 @type enddate: string, DateTime
664 @param offset: The row at which to begin returning
665 @type offset: int
666 @param rows: The number of rows to return.
667 @type rows: int
668 @param getTotalCount: Whether or not to return a count of the total
669 number of rows
670 @type getTotalCount: bool
671 @param filter: A glob by which to filter events
672 @type filter: string
673 @return: Matching events as L{ZEvent}s.
674 @rtype: list
675 @todo: Remove unused parameters from the method definition
676 """
677 unused(kwargs)
678 paramValues = []
679 try:
680 where = self.restrictedUserFilter(where)
681 if not resultFields:
682 resultFields = self.defaultResultFields
683 resultFields = list(resultFields)
684 resultFields.extend(self.defaultFields)
685
686
687 fieldList = [x.lower() for x in self.getFieldList()]
688 for field in resultFields:
689 if field.lower() not in fieldList:
690 raise ('requested column %s is not valid' % field)
691
692 calcfoundrows = ''
693 if getTotalCount:
694 calcfoundrows = 'SQL_CALC_FOUND_ROWS'
695 select = ["select ", calcfoundrows, ','.join(resultFields),
696 "from %s where" % self.statusTable ]
697 if not where:
698 where = self.defaultWhere
699
700
701 where = where.replace('%', '%%')
702
703 if parameterizedWhere is not None:
704 pwhere, pvals = parameterizedWhere
705 if where: where += " and "
706 where += pwhere
707 paramValues.extend(pvals)
708
709 def paramWhereAnd(where, fmt, field, value):
710 if value != None and where.find(field) == -1:
711 if where: where += " and "
712 where += fmt % (field,)
713 paramValues.append(value)
714 return where
715
716 where = paramWhereAnd(where, "%s >= %%s", self.severityField, severity)
717 where = paramWhereAnd(where, "%s <= %%s", self.stateField, state)
718
719 log.debug("filter is %s" % filter )
720 if filter:
721 where += ' and (%s) ' % (' or '.join(['%s LIKE "%%%s%%"' % (
722 x, filter) for x in resultFields]))
723 if startdate:
724 startdate, enddate = self._setupDateRange(startdate, enddate)
725 where = paramWhereAnd(where,"%s >= %%s",self.lastTimeField, startdate )
726 where = paramWhereAnd(where,"%s <= %%s",self.firstTimeField, enddate )
727
728 if filters is not None:
729 where = self.filteredWhere(where, filters, paramValues)
730 select.append(where)
731 if not orderby:
732 orderby = self.defaultOrderby
733 if orderby:
734
735 orderby = self._scrubOrderby(orderby)
736 select.append("order by")
737 select.append(orderby)
738
739 if rows:
740
741 int(offset)
742 int(rows)
743 select.append("limit %s, %s" % (offset, rows))
744 select.append(';')
745 select = " ".join(select)
746 if getTotalCount:
747 try: retdata, totalCount = self.checkCache(select)
748 except TypeError:
749 retdata, totalCount = self.checkCache(select), 100
750 else: retdata = self.checkCache(select)
751 if not False:
752 conn = self.connect()
753 try:
754 curs = conn.cursor()
755 log.debug(select % tuple(paramValues))
756 curs.execute(select, paramValues)
757 retdata = []
758
759
760 if self.checkRemotePerm(ZEN_VIEW, self.dmd.Events):
761 eventPermission = True
762 else:
763 eventPermission = False
764 for row in curs.fetchall():
765 row = map(self.convert, resultFields, row)
766 evt = ZEvent(self, resultFields, row, eventPermission)
767 retdata.append(evt)
768 if getTotalCount:
769 curs.execute("SELECT FOUND_ROWS()")
770 totalCount = curs.fetchone()[0]
771 finally: self.close(conn)
772 if getTotalCount: self.addToCache(select, (retdata, totalCount))
773 else: self.addToCache(select, retdata)
774 self.cleanCache()
775 if getTotalCount:
776 return retdata, totalCount
777 else: return retdata
778 except:
779 log.exception("Failure querying events")
780 raise
781
782
784 """
785 Return the CSS class, number of acknowledged events, and number of
786 unacknowledged events, per severity, for a C{ManagedEntity}.
787
788 @param me: The object of the inquiry.
789 @type me: L{ManagedEntity}
790 @param severity: The minimum severity for which to retrieve events
791 @type severity: int
792 @param state: The minimum state for which to retrieve events
793 @type state: int
794 @param prodState: The minimum production state for which to retrieve
795 events
796 @type prodState: int
797 @return: List of lists of the form [class, acked count, unacked count].
798 @rtype: list
799 """
800 try:
801 where = self.lookupManagedEntityWhere(me)
802 return self.getEventSummary(where, severity, state, prodState)
803 except:
804 log.exception("event summary for %s failed" % me.getDmdKey())
805 raise
806
807
809 """
810 Return a list of tuples with number of events and the color of the
811 severity that the number represents.
812
813 This method should not be called directly, but overridden by subclasses.
814
815 @param where: The base where clause to modify.
816 @type where: string
817 @param severity: The minimum severity for which to retrieve events
818 @type severity: int
819 @param state: The minimum state for which to retrieve events
820 @type state: int
821 @param prodState: The minimum production state for which to retrieve
822 events
823 @type prodState: int
824 @return: List of lists of the form [class, acked count, unacked count].
825 @rtype: list
826 """
827 raise NotImplementedError
828
829
830 - def getEventDetailFromStatusOrHistory(self, evid=None, dedupid=None,
831 better=False):
832 try:
833 event = self.dmd.ZenEventManager.getEventDetail(
834 evid, dedupid, better)
835 except ZenEventNotFound:
836 event = self.dmd.ZenEventHistory.getEventDetail(evid, dedupid,
837 better)
838 return event
839
840
842 """
843 Return an EventDetail object for a particular event.
844
845 @param evid: Event ID
846 @type evid: string
847 @param dedupid: string used to determine duplicates
848 @type dedupid: string
849 @param better: provide even more detail than normal?
850 @type better: boolean
851 @return: fields from the event
852 @rtype: EventDetail object
853 """
854 idfield = evid and "evid" or "dedupid"
855 if not evid: evid = dedupid
856 cachekey = '%s%s' % (idfield, evid)
857 event = self.checkCache(cachekey)
858 if event: return event
859 fields = self.getFieldList()
860 evid = self.escape(evid)
861 selectevent = "select "
862 selectevent += ", ".join(fields)
863 selectevent += " from %s where" % self.statusTable
864 selectevent += " %s = '%s'" % (idfield, evid)
865 conn = self.connect()
866 try:
867 curs = conn.cursor()
868 curs.execute(selectevent)
869 evrow = curs.fetchone()
870 if not evrow:
871 raise ZenEventNotFound( "Event id '%s' not found" % evid)
872 evdata = map(self.convert, fields, evrow)
873 if better:
874 event = BetterEventDetail(self, fields, evdata)
875 else:
876 event = EventDetail(self, fields, evdata)
877
878 selectdetail = "select name, value from %s where" % self.detailTable
879 selectdetail += " evid = '%s'" % event.evid
880
881 curs.execute(selectdetail)
882 event._details = curs.fetchall()
883
884 selectlogs = "select userName, ctime, text"
885 selectlogs += " from %s where" % self.logTable
886 selectlogs += " evid = '%s' order by ctime desc" % event.evid
887
888 curs.execute(selectlogs)
889 jrows = curs.fetchall()
890 logs = []
891 for row in jrows:
892 user = self.cleanstring(row[0])
893 date = self.dateString(row[1])
894 text = row[2]
895 logs.append((user, date, text))
896 event._logs = logs
897 finally: self.close(conn)
898
899 self.addToCache(cachekey, event)
900 self.cleanCache()
901 return event
902
903
904 - def getStatusME(self, me, statusclass=None, **kwargs):
918
919
921 """Return status based on a where clause defined for the me event_type.
922 No fancy caching done this might be a little slow if there are a lot
923 of events. Where clause is evaled
924 """
925 where = self.lookupManagedEntityWhere(me)
926 select = "select count(*) from %s where %s" % (self.statusTable, where)
927 statusCount = self.checkCache(select)
928 if not statusCount:
929 conn = self.connect()
930 try:
931 curs = conn.cursor()
932
933 curs.execute(select)
934 statusCount = curs.fetchone()[0]
935 finally: self.close(conn)
936
937 self.addToCache(select,statusCount)
938 return statusCount
939
940
941 - def getOrganizerStatus(self, org, statusclass=None, severity=None,
942 state=0, where=""):
943 """see IEventStatus
944 """
945 orgfield = self.lookupManagedEntityField(org.event_key)
946 select = "select %s from %s where " % (orgfield, self.statusTable)
947 where = self._wand(where, "%s = '%s'", self.eventClassField,statusclass)
948 where = self._wand(where, "%s >= %s", self.severityField, severity)
949 where = self._wand(where, "%s <= %s", self.stateField, state)
950 select += where
951
952 statusCache = self.checkCache(select)
953 if not statusCache:
954 conn = self.connect()
955 try:
956 curs = conn.cursor()
957 curs.execute(select)
958 statusCache=[]
959 orgdict={}
960 for row in curs.fetchall():
961 orgfield = self.cleanstring(row[0])
962 if not orgfield: continue
963 if orgfield.startswith("|"): orgfield = orgfield[1:]
964 for orgname in orgfield.split("|"):
965 orgdict.setdefault(orgname, 0)
966 orgdict[orgname] += 1
967 statusCache = orgdict.items()
968 self.addToCache(select,statusCache)
969 finally: self.close(conn)
970 countevts = 0
971 for key, value in statusCache:
972 if key.startswith(org.getOrganizerName()):
973 countevts += value
974 return countevts
975
976
979 """Return list of tuples (org, count) for all organizers with events.
980 """
981 orgfield = self.lookupManagedEntityField(event_key)
982 select = "select %s, count from %s where " % (orgfield,self.statusTable)
983 where = self._wand(where, "%s >= %s", self.severityField, severity)
984 where = self._wand(where, "%s <= %s", self.stateField, state)
985 where = self._wand(where,"%s like '%s'",self.eventClassField,"/Status%")
986 select += where
987 statusCache = self.checkCache(select)
988 if not statusCache:
989 conn = self.connect()
990 try:
991 curs = conn.cursor()
992 curs.execute(select)
993 statusCache=[]
994 orgdict={}
995 for row in curs.fetchall():
996 orgfield = self.cleanstring(row[0])
997 if not orgfield: continue
998 if orgfield.startswith("|"): orgfield = orgfield[1:]
999 for orgname in orgfield.split("|"):
1000 if not orgname: continue
1001 count, total = orgdict.setdefault(orgname, (0,0))
1002 count+=1
1003 total+=row[1]
1004 orgdict[orgname] = (count,total)
1005 statusCache = [ [n, c[0], int(c[1])] for n, c in orgdict.items() ]
1006 statusCache.sort(lambda x,y: cmp(x[1],y[1]))
1007 statusCache.reverse()
1008 if limit:
1009 statusCache = statusCache[:limit]
1010 self.addToCache(select,statusCache)
1011 finally: self.close(conn)
1012 return statusCache
1013
1014
1022
1023
1029
1030
1031 - def getDeviceIssues(self,severity=1,state=0,where="",mincount=0,limit=0):
1032 """Return list of tuples (device, count, total) of events for
1033 all devices with events.
1034 """
1035 where = self._wand(where, "%s >= %s", self.severityField, severity)
1036 where = self._wand(where, "%s <= %s", self.stateField, state)
1037 select = """select distinct device, count(device) as evcount,
1038 sum(count) from status where %s group by device
1039 having evcount > %s""" % (where, mincount)
1040 statusCache = self.checkCache(select)
1041 if not statusCache:
1042 try:
1043 conn = self.connect()
1044 try:
1045 curs = conn.cursor()
1046 curs.execute(select)
1047 statusCache = [ [d,int(c),int(s)] for d,c,s in curs.fetchall() ]
1048
1049 statusCache.sort(lambda x,y: cmp(x[1],y[1]))
1050 statusCache.reverse()
1051 if limit:
1052 statusCache = statusCache[:limit]
1053 finally: self.close(conn)
1054 except:
1055 log.exception(select)
1056 raise
1057 return statusCache
1058
1059
1060 - def getDeviceStatus(self, device, statclass=None, countField=None,
1061 severity=3, state=None, where=""):
1062 """see IEventStatus
1063 """
1064 if countField == None: countField = self.countField
1065 select = "select %s, %s from %s where " % (
1066 self.deviceField, self.countField, self.statusTable)
1067 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1068 where = self._wand(where, "%s >= %s", self.severityField, severity)
1069 where = self._wand(where, "%s <= %s", self.stateField, state)
1070 select += where
1071
1072 statusCache = self.checkCache(select)
1073 if not statusCache:
1074 try:
1075 conn = self.connect()
1076 try:
1077 curs = conn.cursor()
1078 curs.execute(select)
1079 statusCache = {}
1080 for dev, count in curs.fetchall():
1081 dev = self.cleanstring(dev)
1082 statusCache[dev] = count
1083 self.addToCache(select,statusCache)
1084 finally: self.close(conn)
1085 except:
1086 log.exception("status failed for device %s", device)
1087 return -1
1088 return statusCache.get(device, 0)
1089
1090
1093
1094
1097
1098
1100 import Availability
1101 allowedFilters = (
1102 "device", "component", "eventClass", "systems", "severity",
1103 "prodState", "manager", "agent", "DeviceClass", "Location",
1104 "System", "DeviceGroup", "DevicePriority", "monitor")
1105
1106 for name in allowedFilters:
1107 if hasattr(state, name):
1108 kw.setdefault(name, getattr(state, name))
1109 if getattr(state, 'startDate', None) is not None:
1110 kw.setdefault('startDate', Time.ParseUSDate(state.startDate))
1111 if getattr(state, 'endDate', None) is not None:
1112
1113
1114 kw.setdefault('endDate', Time.getEndOfDay(Time.ParseUSDate(
1115 state.endDate)))
1116 kw.setdefault('startDate',
1117 time.time() - 60*60*24*self.defaultAvailabilityDays)
1118 return Availability.query(self.dmd, **kw)
1119
1120
1121 - def getHeartbeat(self, failures=True, simple=False, limit=0, db=None):
1122 """Return all heartbeat issues list of tuples (device, component, secs)
1123 """
1124 sel = "select device, component, lastTime from heartbeat"
1125 if failures:
1126 sel += " where DATE_ADD(lastTime, INTERVAL timeout SECOND) <= NOW()"
1127
1128 statuses = []
1129 conn = self.connect()
1130 try:
1131 curs = conn.cursor()
1132 curs.execute(sel)
1133 res = list(curs.fetchall())
1134 res.sort(lambda x,y: cmp(x[2],y[2]))
1135 devclass = self.getDmdRoot("Devices")
1136 for devname, comp, lastTime in res:
1137 dtime = int(time.time() - lastTime.timeTime())
1138
1139 alink = devname
1140 if not simple:
1141 dev = devclass.findDevice(devname)
1142 if dev:
1143 alink = "<a href='%s'>%s</a>" % (
1144 dev.getPrimaryUrlPath(), dev.titleOrId())
1145
1146 statuses.append([alink, comp, str(dtime), devname])
1147 if limit:
1148 statuses = statuses[:limit]
1149 finally:
1150 self.close(conn)
1151 return statuses
1152
1154 beats = self.getHeartbeat(failures, simple, limit, db)
1155 return [{'alink':b[0], 'comp':b[1], 'dtime':b[2], 'devId':b[3]}
1156 for b in beats]
1157
1158
1159 - def getAllComponentStatus(self,
1160 statclass,
1161 countField=None,
1162 severity=3,
1163 state=1,
1164 where=""):
1165 "Fetch the counts on all components matching statClass"
1166 if countField == None: countField = self.countField
1167 select = "select %s, %s, %s from %s where "\
1168 % (self.deviceField, self.componentField, countField,
1169 self.statusTable)
1170 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1171 where = self._wand(where, "%s >= %s", self.severityField, severity)
1172 where = self._wand(where, "%s <= %s", self.stateField, state)
1173 select += where
1174 conn = self.connect()
1175 try:
1176 curs = conn.cursor()
1177 curs.execute(select)
1178 result = {}
1179 for dev, comp, count in curs.fetchall():
1180 dev = self.cleanstring(dev)
1181 comp = self.cleanstring(comp)
1182 result[dev,comp] = count
1183 return result
1184 finally:
1185 self.close(conn)
1186
1187
1204
1205
1206 - def getComponentStatus(self, device, component, statclass=None,
1207 countField=None, severity=3, state=1, where=""):
1208 """see IEventStatus
1209 """
1210 if countField == None: countField = self.countField
1211 select = "select %s, %s, %s from %s where "\
1212 % (self.deviceField, self.componentField, countField,
1213 self.statusTable)
1214 where = self._wand(where, "%s = '%s'", self.eventClassField, statclass)
1215 where = self._wand(where, "%s >= %s", self.severityField, severity)
1216 where = self._wand(where, "%s <= %s", self.stateField, state)
1217 select += where
1218 statusCache = self.checkCache(select)
1219 if not statusCache:
1220 conn = self.connect()
1221 try:
1222 curs = conn.cursor()
1223 curs.execute(select)
1224 statusCache ={}
1225 for dev, comp, count in curs.fetchall():
1226 dev = self.cleanstring(dev)
1227 comp = self.cleanstring(comp)
1228 statusCache[dev+comp] = count
1229 self.addToCache(select,statusCache)
1230 finally: self.close(conn)
1231 return statusCache.get(device+component, 0)
1232
1234 """
1235 Given a list of dicts with keys 'device', 'component', make a query to
1236 get an overall severity and device status for the group.
1237 """
1238 severity, state = 3, 1
1239 components = list(components)
1240
1241 def doQuery(query):
1242 conn = self.connect()
1243 data = None
1244 try:
1245 curs = conn.cursor()
1246 curs.execute(query)
1247 data = curs.fetchall()
1248 finally: self.close(conn)
1249 return data
1250
1251 select = "select MAX(%s) from %s where " % (self.severityField,
1252 self.statusTable)
1253 where = self._wand("", "%s >= %s", self.severityField, severity)
1254 where = self._wand(where, "%s <= %s", self.stateField, state)
1255 def componentWhere(device, component):
1256 return "device = '%s' and component= '%s'" % (device, component)
1257 cwheres = ' and ' + ' or '.join(['(%s)'% componentWhere(**comp)
1258 for comp in components])
1259 sevquery = select + where + cwheres
1260
1261 select = "select MAX(%s) from %s where " % (self.countField,
1262 self.statusTable)
1263 where = self._wand("", "%s >= %s", self.severityField, severity)
1264 where = self._wand(where, "%s <= %s", self.stateField, state)
1265 where = self._wand(where, "%s = '%s'", self.eventClassField,
1266 '/Status/Ping')
1267 devwhere = lambda d:"device = '%s'" % d
1268 dwheres = ' and ' + '(%s)' % (' or '.join(
1269 map(devwhere, [x['device'] for x in components])))
1270 statquery = select + where + dwheres
1271
1272 maxseverity = doQuery(sevquery)[0][0]
1273 maxstatus = doQuery(statquery)[0][0]
1274 return maxseverity, maxstatus
1275
1285
1286
1288 """Return a list of userids that correspond to the events in where.
1289 select distinct ownerid from status where
1290 device="win2k.confmon.loc" and eventState > 2
1291 """
1292 select ="select distinct ownerid from status where "
1293 where = self._wand(where, "%s >= %s", self.severityField, severity)
1294 where = self._wand(where, "%s <= %s", self.stateField, state)
1295 select += where
1296
1297 statusCache = self.checkCache(select)
1298 if statusCache: return statusCache
1299 conn = self.connect()
1300 try:
1301 curs = conn.cursor()
1302 curs.execute(select)
1303 statusCache = [ uid[0] for uid in curs.fetchall() if uid[0] ]
1304 self.addToCache(select,statusCache)
1305 finally: self.close(conn)
1306 return statusCache
1307
1308
1310 """
1311 Lookup and build where clause for managed entity.
1312
1313 @param me: managed entity
1314 @type me: object
1315 @return: where clause
1316 @rtype: string
1317 """
1318 key = me.event_key + "Where"
1319 wheretmpl = getattr(aq_base(self), key, False)
1320 if not wheretmpl:
1321 raise ValueError("No 'where' clause found for event_key %s" % me.event_key)
1322
1323
1324
1325
1326 if me.id == 'Devices':
1327 return '(DeviceCLass like \'/%\')'
1328 return eval(wheretmpl,{'me':me})
1329
1330
1332 """
1333 Lookup database field for managed entity default
1334 using event_key.
1335
1336 @param event_key: event
1337 @type event_key: string
1338 @return: field for the managed entity
1339 @rtype: object
1340 """
1341 key = event_key + "Field"
1342 return getattr(aq_base(self), key, event_key)
1343
1344
1346 """
1347 Gets the column names that should be requested in an event query for
1348 this entity type.
1349
1350 Returns a set of result fields predefined for this entity type. If
1351 none have been defined, returns the default result fields.
1352
1353 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Device')
1354 >>> f==dmd.ZenEventManager.DeviceResultFields
1355 True
1356 >>> f = dmd.ZenEventManager.lookupManagedEntityResultFields('Robot')
1357 >>> f==dmd.ZenEventManager.defaultResultFields
1358 True
1359
1360 @param event_key: The event key of a managed entity.
1361 @type event_key: string
1362 @return: A tuple of strings representing columns in the database.
1363 """
1364 key = event_key + "ResultFields"
1365 fields = getattr(aq_base(self), key, self.defaultResultFields)
1366 return fields
1367
1368
1369 - def _wand(self, where, fmt, field, value):
1370 """
1371 >>> dmd.ZenEventManager._wand('where 1=1', '%s=%s', 'a', 'b')
1372 'where 1=1 and a=b'
1373 >>> dmd.ZenEventManager._wand('where a=5', '%s=%s', 'a', 'b')
1374 'where a=5'
1375 >>> dmd.ZenEventManager._wand('where b=a', '%s=%s', 'a', 'b')
1376 'where b=a'
1377 """
1378 if value != None and where.find(field) == -1:
1379 if where: where += " and "
1380 where += fmt % (field, value)
1381 return where
1382
1385 """
1386 Make a start and end date range that is at least one day long.
1387 returns a start and end date as a proper database element.
1388 """
1389 if enddate is None:
1390 enddate = DateTime.DateTime()-1
1391 if startdate is None:
1392 startdate = DateTime.DateTime()
1393 if type(enddate) == types.StringType:
1394 enddate = DateTime.DateTime(enddate, datefmt='us')
1395 enddate = enddate.latestTime()
1396 if type(startdate) == types.StringType:
1397 startdate = DateTime.DateTime(startdate, datefmt='us')
1398 startdate = startdate.earliestTime()
1399 startdate = self.dateDB(startdate)
1400 enddate = self.dateDB(enddate)
1401 return startdate, enddate
1402
1404 """
1405 validates the syntax and columns for an orderby used on the status and
1406 history tables. Also removes adjacent sorts of the same columns as an
1407 optimization
1408 @return: orderby by clause
1409 @raise exception: if order by clause is invalid
1410 """
1411 sortValues = []
1412 prevSortCol = None
1413 fieldList = [x.lower() for x in self.getFieldList()]
1414 log.debug("orderby is %s" % orderby)
1415 for x in orderby.split(','):
1416 col, dir = x.split()
1417 col = col.lower()
1418 dir = dir.upper()
1419 if dir not in ['DESC','ASC'] or col not in fieldList:
1420 raise ("order by value %s %s not valid" % (col, dir))
1421
1422 if not prevSortCol or prevSortCol != col:
1423 sortValues.append((col, dir))
1424 prevSortCol = col
1425
1426 orderby = ', '.join([' '.join(x) for x in sortValues])
1427
1428 log.debug("final orderby is %s" % orderby)
1429 return orderby
1430
1432 conn = self.connect()
1433 try:
1434 curs = conn.cursor()
1435 selstatement = ("SELECT AVG(CHAR_LENGTH(mycol))+20 FROM (SELECT %s AS "
1436 "mycol FROM %s WHERE %s IS NOT NULL LIMIT 500) AS "
1437 "a;") % (fieldname, self.statusTable, fieldname)
1438 curs.execute(selstatement)
1439 avglen = curs.fetchone()
1440 finally: self.close(conn)
1441 try: return float(avglen[0])
1442 except TypeError: return 10.
1443
1444
1445
1446
1447
1448
1449 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvents')
1451 """Send a group of events to the backend.
1452 """
1453 count = 0
1454 for event in events:
1455 try:
1456 self.sendEvent(event)
1457 count += 1
1458 except Exception, ex:
1459 log.exception(ex)
1460 return count
1461
1462
1463 security.declareProtected(ZEN_SEND_EVENTS, 'sendEvent')
1465 """
1466 Send an event to the backend.
1467
1468 @param event: event
1469 @type event: event object
1470 @todo: implement
1471 """
1472 raise NotImplementedError
1473
1474
1475
1476
1477
1478
1494
1495
1496 security.declareProtected(ZEN_VIEW, "getFieldList")
1498 """Return a list of all fields in the status table of the backend.
1499 """
1500 if not getattr(self, '_fieldlist', None):
1501 self.loadSchema()
1502 return self._fieldlist
1503
1508
1510 """Return a list of possible event actions.
1511 """
1512 return self.eventActions
1513
1514 security.declareProtected(ZEN_COMMON,'getSeverities')
1516 """Return a list of tuples of severities [('Warning', 3), ...]
1517 """
1518 return self.severityConversions
1519
1521 """Return a string representation of the severity.
1522 """
1523 try:
1524 return self.severities[severity]
1525 except KeyError:
1526 return "Unknown"
1527
1529 """Return a list of tuples of priorities [('Warning', 3), ...]
1530 """
1531 return self.priorityConversions
1532
1534 """Return the priority name
1535 """
1536 try:
1537 return self.priorities[priority]
1538 except IndexError:
1539 return "Unknown"
1540
1569
1570
1575
1576
1578 ''' Return the img source for a status number
1579 '''
1580 if status < 0:
1581 src = 'grey'
1582 elif status == 0:
1583 src = 'green'
1584 else:
1585 src = 'red'
1586 return '/zport/dmd/img/%s_dot.png' % src
1587
1588
1590 """return the css class name to be used for this event.
1591 """
1592 __pychecker__='no-constCond'
1593 value = severity < 0 and "unknown" or severity
1594 acked = acked and "acked" or "noack"
1595 return "zenevents_%s_%s %s" % (value, acked, acked)
1596
1597
1599 """Check to see if a column is of type date.
1600 """
1601 if not self._schema:
1602 self.getFieldList()
1603 return self._schema.get(colName, False)
1604
1605
1612
1613
1614
1616 """Convert a date to its database format.
1617 """
1618 if isinstance(value, DateTime.DateTime):
1619 return "%.3f" % value.timeTime()
1620 elif isinstance(value, basestring):
1621 return "%.3f" % DateTime.DateTime(value).timeTime()
1622 return value
1623
1624
1626 """
1627 Prepare string values for db by escaping special characters.
1628
1629 @param value: string
1630 @type value: string
1631 @todo: implement
1632 """
1633 raise NotImplementedError
1634
1635
1637 """Load schema from database. If field is a date set value to true."""
1638 schema = {}
1639 fieldlist = []
1640 sql = "describe %s;" % self.statusTable
1641 conn = self.connect()
1642 try:
1643 curs = conn.cursor()
1644 curs.execute(sql)
1645 for row in curs.fetchall():
1646 fieldlist.append(row[0])
1647 col = self.cleanstring(row[0])
1648 if self.backend == "mysql":
1649 type = row[1] in ("datetime", "timestamp", "double")
1650 schema[col] = type
1651 if schema: self._schema = schema
1652 self._fieldlist = fieldlist
1653 finally: self.close(conn)
1654
1655
1657 """Are there event controls on this event list.
1658 """
1659 if self.isManager() and self.statusTable in ["status","history"]:
1660 return 1
1661 return 0
1662
1663 - def updateEvents(self, stmt, whereClause, reason,
1664 table="status", toLog=True):
1665 userId = getSecurityManager().getUser().getId()
1666 insert = 'INSERT INTO log (evid, userName, text) ' + \
1667 'SELECT evid, "%s", "%s" ' % (userId, reason) + \
1668 'FROM %s ' % table + whereClause
1669 query = stmt + ' ' + whereClause
1670 conn = self.connect()
1671 try:
1672 curs = conn.cursor()
1673 if toLog: curs.execute(insert)
1674 curs.execute(query)
1675 finally: self.close(conn)
1676 self.clearCache()
1677 self.manage_clearCache()
1678
1679 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_addEvent')
1681 ''' Create an event from user supplied data
1682 '''
1683 if REQUEST is None:
1684 return
1685
1686 defaultFields = set(self.dmd.ZenEventManager.getFieldList())
1687 ignoreFields = set(['eventClass', 'count', 'clearid',
1688 'clearid', 'eventClassMapping', 'stateChange', 'evid',
1689 'eventState', 'lastTime', 'dedupid', 'firstTime', 'prodState', ])
1690 eventDict = dict([(field, REQUEST.get(field, '')) \
1691 for field in defaultFields - ignoreFields ])
1692
1693
1694
1695 if REQUEST.get('eventClass', None):
1696 eventDict['eventClass'] = REQUEST['eventClass']
1697
1698 try:
1699 evid = self.sendEvent(eventDict)
1700 except ZenEventError, ex:
1701 log.exception("Invalid event not processed = %s" % eventDict)
1702 messaging.IMessageSender(self).sendToBrowser(
1703 'Invalid Event', str(ex),
1704 priority=messaging.WARNING
1705 )
1706 return self.callZenScreen(REQUEST)
1707 except ZenBackendFailure, ex:
1708 log.exception("Event not processed = %s" % eventDict)
1709 messaging.IMessageSender(self).sendToBrowser(
1710 'Unable to process Event', str(ex),
1711 priority=messaging.WARNING
1712 )
1713 return self.callZenScreen(REQUEST)
1714
1715 if 'RESPONSE' in REQUEST:
1716 REQUEST['RESPONSE'].redirect('/zport/dmd/Events/viewEvents')
1717 else:
1718 return evid
1719
1720
1722 self.updateEvents('DELETE FROM status', whereClause, reason)
1723
1724
1725 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteEvents')
1727 "Delete the given event ids"
1728 if type(evids) == type(''):
1729 evids = [evids]
1730 num = len(evids)
1731 if evids:
1732 evids = ",".join([ "'%s'" % evid for evid in evids])
1733 userid = getSecurityManager().getUser()
1734 update = "update status set ownerid='%s' " % userid
1735 whereClause = ' where evid in (%s)' % evids
1736 self.updateEvents(update, whereClause, '', toLog=False)
1737 self.deleteEvents(whereClause, 'Deleted by user')
1738 if REQUEST:
1739 messaging.IMessageSender(self).sendToBrowser(
1740 'Moved To History',
1741 '%s event%s have been moved to history.' % (
1742 num, (num != 1 and 's') or '')
1743 )
1744 return self.callZenScreen(REQUEST)
1745
1747 fields = self.getFieldList()
1748 if 'deletedTime' in fields:
1749 fields.remove('deletedTime')
1750 fields = ','.join(fields)
1751
1752 fields = fields.replace('clearid','NULL')
1753 evmgr = self.dmd.ZenEventManager
1754 evmgr.updateEvents('INSERT status ' + \
1755 'SELECT %s FROM history' % fields,
1756 whereClause + ' ON DUPLICATE KEY UPDATE '+
1757 'status.count=status.count+history.count',
1758 reason, 'history', toLog=False)
1759 evmgr.updateEvents('DELETE FROM history', whereClause, \
1760 reason, 'history')
1761
1762 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_undeleteEvents')
1764 "Move the given event ids into status and delete from history"
1765 if type(evids) == type(''):
1766 evids = [evids]
1767 num = len(evids)
1768 if evids:
1769 evids = ",".join([ "'%s'" % evid for evid in evids])
1770 whereClause = ' where evid in (%s)' % evids
1771 self.undeleteEvents(whereClause, 'Undeleted by user')
1772 if REQUEST:
1773 messaging.IMessageSender(self).sendToBrowser(
1774 'Undeleted',
1775 '%s events have been moved out of history.' % num
1776 )
1777 return self.callZenScreen(REQUEST)
1778
1779
1780 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteAllEvents')
1791
1792
1793 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHistoricalEvents')
1796 """
1797 Delete historical events. If devices is given then only delete
1798 events for that device. If agedDays is given then only delete
1799 events that are older than that many days.
1800 devname and agedDays are mutually exclusive. No real reason for this
1801 other than there is no current need to use both in same call and I
1802 don't want to test the combination.
1803 This is an option during device deletion. It is also used
1804 by zenactions to keep history table clean.
1805
1806 NB: Device.deleteDevice() is not currently calling this when devices
1807 are deleted. See ticket #2996.
1808 """
1809 import subprocess
1810 import os
1811 import Products.ZenUtils.Utils as Utils
1812
1813 cmd = Utils.zenPath('Products', 'ZenUtils', 'ZenDeleteHistory.py')
1814 if devname:
1815 args = ['--device=%s' % devname]
1816 elif agedDays:
1817 args = ['--numDays=%s' % agedDays]
1818 else:
1819 return
1820 proc = subprocess.Popen(
1821 [cmd]+args, stdout=subprocess.PIPE,
1822 stderr=subprocess.STDOUT, env=os.environ)
1823
1824
1825
1826 if REQUEST:
1827 messaging.IMessageSender(self).sendToBrowser(
1828 'Events Deleted',
1829 'Historical events have been deleted.'
1830 )
1831 return self.callZenScreen(REQUEST)
1832 else:
1833
1834 return proc
1835
1836
1837 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_deleteHeartbeat')
1852
1853
1854 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_ackEvents')
1867
1868
1869 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_ackEvents')
1882
1883
1884 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1887 reason = None
1888 if eventState is not None and evids:
1889 eventState = int(eventState)
1890 if eventState > 0 and not userid:
1891 userid = getSecurityManager().getUser().getId()
1892 update = "update status set eventState=%s, ownerid='%s' " % (
1893 eventState, userid)
1894 whereClause = "where evid in ("
1895 whereClause += ",".join([ "'%s'" % evid for evid in evids]) + ")"
1896 reason = 'Event state changed to '
1897 try:
1898 reason += self.eventStateConversions[eventState][0]
1899 except KeyError:
1900 reason += 'unknown (%d)' % eventState
1901 self.updateEvents(update, whereClause, reason)
1902 if REQUEST:
1903 return self.callZenScreen(REQUEST)
1904
1905
1906 security.declareProtected(ZEN_MANAGE_EVENTS,'manage_setEventStates')
1909 """Create an event map from an event or list of events.
1910 """
1911 evclass = None
1912 evmap = None
1913 numCreated = 0
1914 numNotUnknown = 0
1915 numNoKey = 0
1916 if eventClass and evids:
1917 evclass = self.getDmdRoot("Events").getOrganizer(eventClass)
1918 sel = """select eventClassKey, eventClass, message
1919 from %s where evid in ('%s')"""
1920 sel = sel % (self.statusTable, "','".join(evids))
1921 conn = self.connect()
1922 try:
1923 curs = conn.cursor()
1924 curs.execute(sel);
1925 for row in curs.fetchall():
1926 evclasskey, curevclass, msg = row
1927 if curevclass != Unknown:
1928 numNotUnknown += 1
1929 continue
1930 if not evclasskey:
1931 numNoKey += 1
1932 continue
1933 evmap = evclass.createInstance(evclasskey)
1934 evmap.eventClassKey = evclasskey
1935 evmap.example = msg
1936 numCreated += 1
1937 evmap.index_object()
1938 finally: self.close(conn)
1939 elif REQUEST:
1940 if not evids:
1941 messaging.IMessageSender(self).sendToBrowser(
1942 'Error',
1943 'No events selected',
1944 priority=messaging.WARNING
1945 )
1946 elif not eventClass:
1947 messaging.IMessageSender(self).sendToBrowser(
1948 'Error',
1949 'No event class selected',
1950 priority=messaging.WARNING
1951 )
1952
1953 msg = ''
1954 if numNotUnknown:
1955 msg += ((msg and ' ') +
1956 '%s event%s %s not class /Unknown.' % (
1957 numNotUnknown,
1958 (numNotUnknown != 1 and 's') or '',
1959 (numNotUnknown != 1 and 'are') or 'is'))
1960 if numNoKey:
1961 msg += ((msg and ' ') +
1962 '%s event%s %s not have an event class key.' % (
1963 numNoKey,
1964 (numNoKey != 1 and 's') or '',
1965 (numNoKey != 1 and 'do') or 'does'))
1966 msg += (msg and ' ') + 'Created %s event mapping%s.' % (
1967 numCreated,
1968 (numCreated != 1 and 's') or '')
1969
1970 url = None
1971 if len(evids) == 1 and evmap:
1972 url = evmap.absolute_url()
1973 elif evclass and evmap:
1974 url = evclass.absolute_url()
1975
1976 if REQUEST:
1977 msg = REQUEST.get('message', '') + msg
1978
1979 messaging.IMessageSender(self).sendToBrowser('Event Map', msg)
1980
1981
1982 if getattr(REQUEST, 'dontRender', False):
1983 return ''
1984 if url:
1985 REQUEST['RESPONSE'].redirect(url)
1986 else:
1987 return msg, url
1988
1989
1990 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_refreshConversions')
2000
2001
2002 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editCache')
2004 """Reset cache values"""
2005 self.timeout = int(timeout)
2006 self.clearthresh = int(clearthresh)
2007 if REQUEST:
2008 message = "Cache parameters set"
2009 return self.editCache(self, REQUEST, manage_tabs_message=message)
2010
2011
2012 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearCache')
2022
2023
2024 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_editEventManager')
2026 ''' Call zmanage_editProperties then take care of saving a few
2027 values to ZenEventHistory
2028 '''
2029 assert(self == self.dmd.ZenEventManager)
2030 self.zmanage_editProperties(REQUEST)
2031 self.dmd.ZenEventHistory.timeout = REQUEST['history_timeout']
2032 self.dmd.ZenEventHistory.clearthresh = REQUEST['history_clearthresh']
2033 self.dmd.ZenEventHistory.username = self.dmd.ZenEventManager.username
2034 self.dmd.ZenEventHistory.password = self.dmd.ZenEventManager.password
2035 self.dmd.ZenEventHistory.database = self.dmd.ZenEventManager.database
2036 self.dmd.ZenEventHistory.host = self.dmd.ZenEventManager.host
2037 self.dmd.ZenEventHistory.port = self.dmd.ZenEventManager.port
2038 if REQUEST: return self.callZenScreen(REQUEST)
2039
2040
2041 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'manage_clearHeartbeats')
2054
2055 security.declareProtected(ZEN_MANAGE_EVENTMANAGER,'zmanage_editProperties')
2057 ''' Need to handle editing of history event fields differently
2058 '''
2059 assert(self == self.dmd.ZenEventManager)
2060 screenName = REQUEST.get('zenScreenName', '')
2061 if screenName.find('editEventManagerHistoryFields') != -1 :
2062 obj = self.dmd.ZenEventHistory
2063 else:
2064 obj = self
2065 if screenName == 'editEventManager.pt':
2066
2067
2068 if REQUEST.has_key('mysql_pass'):
2069 REQUEST.form['password'] = REQUEST['mysql_pass']
2070 editProperties = ZenModelRM.zmanage_editProperties
2071
2072 editProperties(obj, REQUEST)
2073 if REQUEST: return self.callZenScreen(REQUEST)
2074
2075 security.declareProtected(ZEN_MANAGE_EVENTS, 'manage_addLogMessage')
2077 """
2078 Add a log message to an event
2079 """
2080 if not evid:
2081 return
2082 userId = getSecurityManager().getUser().getId()
2083 conn = self.connect()
2084 try:
2085 curs = conn.cursor()
2086 insert = 'INSERT INTO log (evid, userName, text) '
2087 insert += 'VALUES ("%s", "%s", "%s")' % (evid,
2088 userId,
2089 conn.escape_string(message))
2090 curs.execute(insert)
2091 finally: self.close(conn)
2092 self.clearCache('evid' + evid)
2093 self.dmd.ZenEventHistory.clearCache('evid' + evid)
2094 if REQUEST: return self.callZenScreen(REQUEST)
2095
2096
2097 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_addCommand')
2125
2126
2127 security.declareProtected(ZEN_MANAGE_EVENTMANAGER, 'manage_deleteCommands')
2129 """
2130 Delete an EventCommand
2131
2132 @param ids: commands to delete
2133 @type ids: list of strings
2134 @param REQUEST: Zope REQUEST object
2135 @type REQUEST: Zope REQUEST object
2136 """
2137 for id in ids:
2138 self.commands._delObject(id)
2139 if REQUEST: return self.callZenScreen(REQUEST)
2140
2141
2142
2143
2144
2145
2147 """Install skins into portal.
2148 """
2149 from Products.CMFCore.utils import getToolByName
2150 from Products.CMFCore.DirectoryView import addDirectoryViews
2151 from cStringIO import StringIO
2152 import string
2153
2154 out = StringIO()
2155 skinstool = getToolByName(self, 'portal_skins')
2156 if 'zenevents' not in skinstool.objectIds():
2157 addDirectoryViews(skinstool, 'skins', globals())
2158 out.write("Added 'zenevents' directory view to portal_skins\n")
2159 skins = skinstool.getSkinSelections()
2160 for skin in skins:
2161 path = skinstool.getSkinPath(skin)
2162 path = map(string.strip, string.split(path,','))
2163 if 'zenevents' not in path:
2164 try: path.insert(path.index('zenmodel'), 'zenevents')
2165 except ValueError:
2166 path.append('zenevents')
2167 path = string.join(path, ', ')
2168 skinstool.addSkinSelection(skin, path)
2169 out.write("Added 'zenevents' to %s skin\n" % skin)
2170 else:
2171 out.write(
2172 "Skipping %s skin, 'zenevents' is already set up\n" % skin)
2173 return out.getvalue()
2174