1
2
3
4
5
6
7
8
9
10
11 """
12 Operations for Events.
13
14 Available at: /zport/dmd/evconsole_router
15 """
16
17 import cgi
18 import logging
19 import re
20 import time
21 from json import loads
22 from lxml.html.clean import clean_html
23 from zenoss.protocols.exceptions import NoConsumersException, PublishException
24 from zenoss.protocols.protobufs.zep_pb2 import STATUS_NEW, STATUS_ACKNOWLEDGED
25 from zenoss.protocols.services import ServiceResponseError
26 from AccessControl import getSecurityManager
27 from Products import Zuul
28 from Products.ZenUtils.Ext import DirectRouter
29 from Products.ZenUtils.extdirect.router import DirectResponse
30 from Products.Zuul.decorators import require, serviceConnectionError
31 from Products.ZenUtils.guid.interfaces import IGlobalIdentifier, IGUIDManager
32 from Products.ZenEvents.EventClass import EventClass
33 from Products.ZenMessaging.audit import audit
34 from Products.ZenModel.ZenossSecurity import ZEN_MANAGE_EVENTS
35 from Products.ZenUtils.deprecated import deprecated
36 from Products.Zuul.utils import resolve_context
37 from Products.Zuul.utils import ZuulMessageFactory as _t
38 from Products.Zuul.utils import get_dmd
39 from Products.ZenUI3.browser.eventconsole.grid import column_config
40 from Products.ZenUI3.security.security import permissionsForContext
41 from Products.Zuul.catalog.interfaces import IModelCatalogTool
42 from Products.Zuul.infos.event import EventCompatInfo, EventCompatDetailInfo
43
44 READ_WRITE_ROLES = ['ZenManager', 'Manager', 'ZenOperator']
45
46 log = logging.getLogger('zen.%s' % __name__)
49 """
50 Parses the filter related params received from the ui to search
51 for "or clauses", "NULLs" and "NOTs"
52 """
53
54 NOT_SEPARATOR = "!!"
55 OR_SEPARATOR = "||"
56 NULL_CHAR='""'
57
59 """ """
60
61 detail_list = zep_facade.getDetailsMap().keys()
62 param_to_detail_mapping = zep_facade.ZENOSS_DETAIL_OLD_TO_NEW_MAPPING
63 null_numeric_detail_value = zep_facade.ZENOSS_NULL_NUMERIC_DETAIL_INDEX_VALUE
64 null_text_detail_value = zep_facade.ZENOSS_NULL_TEXT_DETAIL_INDEX_VALUE
65 numeric_details = [ d['key'] for d in zep_facade.getDetails() if d['type'] == 2 ]
66
67
68 self.PARSEABLE_PARAMS = [ 'device', 'component', 'eventClass', 'ownerid', 'summary', 'message', 'monitor',
69 'agent', 'eventClassKey', 'eventGroup', 'eventKey', 'dedupid', 'evid' ]
70 self.PARAM_TO_FIELD_MAPPING = { 'device': 'element_title',
71 'component': 'element_sub_title',
72 'eventClass': 'event_class',
73 'ownerid': 'current_user_name',
74 'summary': 'event_summary',
75 'message' :'message',
76 'monitor': 'monitor',
77 'agent': 'agent',
78 'eventClassKey': 'event_class_key',
79 'eventGroup': 'event_group',
80 'eventKey': 'event_key',
81 'dedupid': 'fingerprint',
82 'evid': 'uuid' }
83 self.PARSEABLE_DETAILS = detail_list
84 self.PARAM_TO_DETAIL_MAPPING = param_to_detail_mapping
85 for detail in self.PARSEABLE_DETAILS:
86 if detail not in self.PARAM_TO_DETAIL_MAPPING.values():
87 self.PARAM_TO_DETAIL_MAPPING[detail] = detail
88 self.TRANSLATE_NULL = self.PARAM_TO_DETAIL_MAPPING.values()
89 self.EXCLUDABLE = self.PARSEABLE_PARAMS + self.PARAM_TO_DETAIL_MAPPING.keys()
90 self.NULL_NUMERIC_INDEX = null_numeric_detail_value
91 self.NULL_TEXT_INDEX = null_text_detail_value
92 self.NO_FRONT_WILDCARD = [ 'device', 'component', 'eventClass' ]
93 self.NUMERIC_DETAILS = numeric_details
94 self.NO_WILDCARD = self.NUMERIC_DETAILS[:]
95
97 """
98 Look for filter params that contain the NOT_SEPARATOR
99 @type params: dictionary
100 @param params: dictionary containing filter parameters from the ui
101 @return: dictionary with the params that must be NOT filtered
102 """
103 exclude_params = {}
104 if params is not None and isinstance(params, dict) and len(params) > 0:
105 for param in self.EXCLUDABLE:
106 value = params.get(param)
107 if value is not None and isinstance(value, basestring) and self.NOT_SEPARATOR in value:
108 value = self._cleanText(value)
109 clauses = value.split(self.NOT_SEPARATOR)
110 inclusion_clause = clauses[0].strip()
111 exclusion_clause = clauses[1].strip()
112
113 if len(exclusion_clause) > 0:
114 exclude_params[param] = exclusion_clause
115 if len(inclusion_clause) == 0:
116 del params[param]
117 else:
118 params[param] = inclusion_clause
119
120 return exclude_params
121
122 - def _cleanText(self, clause):
123 """ """
124 clause = re.sub('\s+', ' ', clause)
125 clause = clause.strip(' *')
126 return clause
127
129 """ """
130 filter = value.strip()
131 if filter != self.NULL_CHAR and field not in self.NO_WILDCARD:
132 if field in self.NO_FRONT_WILDCARD:
133 filter = '{0}*'.format(filter.strip())
134 else:
135 filter = '*{0}*'.format(filter.strip())
136
137 return filter
138
140 """
141 Given a filter field value, check if it contains the OR_SEPARATOR.
142 @type field: string
143 @param field: name of the field
144 @type value: string
145 @param value: field value received from the UI
146 @return: list of OR clauses
147 """
148 or_clauses = []
149
150 if isinstance(value, basestring):
151 value = self._cleanText(value)
152 if self.OR_SEPARATOR in value:
153 temp_or_clauses = value.split(self.OR_SEPARATOR)
154 or_clauses = [ self._addWildcardsToFilter(field, clause) for clause in temp_or_clauses if len(clause)>0 and clause != ' ']
155 elif field in self.TRANSLATE_NULL and self.NULL_CHAR in value:
156 or_clauses.append(self.NULL_CHAR)
157 else:
158 or_clauses.append(self._addWildcardsToFilter(field, value))
159 elif isinstance(value, list):
160 or_clauses = value
161
162
163
164
165
166 if len(or_clauses) > 0 and field in self.TRANSLATE_NULL:
167 null_index = self.NULL_NUMERIC_INDEX if field in self.NUMERIC_DETAILS else self.NULL_TEXT_INDEX
168 or_clauses = [ null_index if self.NULL_CHAR in str(c) else c for c in or_clauses ]
169
170 return or_clauses
171
173 """
174 Parses the filter params passed from the UI looking
175 for OR clauses or NULL values
176 @type params: dictionary
177 @param params: dict of filter params passed from the UI
178 @return
179 """
180 parsed_params = {}
181 for par in self.PARSEABLE_PARAMS:
182 if params.get(par) is not None:
183 value = params.get(par)
184 or_clauses = self._getOrClauses(field=par, value=value)
185 filter_param = self.PARAM_TO_FIELD_MAPPING[par]
186 parsed_params[filter_param] = or_clauses
187 return parsed_params
188
190 """
191 Parses the filter details passed from the UI looking
192 for OR clauses or NULL values
193 @type details: dictionary
194 @param details: dict of filter details passed from the UI
195 @return
196 """
197 parsed_details = {}
198 for detail in self.PARSEABLE_DETAILS:
199 if details.get(detail) is not None:
200 detail_value = details.get(detail)
201 or_clauses = self._getOrClauses(field=detail, value=detail_value)
202 parsed_details[detail] = or_clauses
203 return parsed_details
204
206 """
207 A JSON/ExtDirect interface to operations on events in ZEP
208 """
209
211 super(EventsRouter, self).__init__(context, request)
212 self.zep = Zuul.getFacade('zep', context)
213 self.catalog = IModelCatalogTool(context)
214 self.manager = IGUIDManager(context.dmd)
215 self._filterParser = _FilterParser(self.zep)
216 self.use_permissions = False
217
219 """
220 To view any events you either have to have administered roles or
221 be a global roled user
222 """
223 user = self.context.dmd.ZenUsers.getUserSettings()
224 if not user.hasNoGlobalRoles():
225 return True
226
227 if len(user.getAllAdminRoles()) > 0:
228 self.use_permissions = True
229 return len(user.getAllAdminRoles()) > 0
230
232 try:
233 values = []
234 splitter = ' TO ' if ' TO ' in value else '/'
235 for t in value.split(splitter):
236 values.append(float(t))
237 return values
238 except ValueError:
239 log.warning("Invalid timestamp: %s", value)
240 return ()
241
243 """
244 When querying archived events we need to make sure that
245 we do not link to devices and components that are no longer valid
246 """
247 manager = self.manager
248 for event_summary in events:
249 occurrence = event_summary['occurrence'][0]
250 actor = occurrence['actor']
251
252 if actor.get('element_uuid') and \
253 actor.get('element_uuid') not in manager.table:
254 del actor['element_uuid']
255
256
257 if actor.get('element_sub_uuid') and \
258 actor.get('element_sub_uuid') not in manager.table:
259 del actor['element_sub_uuid']
260 yield event_summary
261
262 @serviceConnectionError
263 @require('ZenCommon')
264 - def queryArchive(self, page=None, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None, uid=None, detailFormat=False):
265 if not self._canViewEvents():
266 return DirectResponse.succeed(
267 events = [],
268 totalCount = 0,
269 asof = time.time()
270 )
271
272 exclude_params = self._filterParser.findExclusionParams(params)
273 if len(exclude_params) > 0:
274 if exclusion_filter is None:
275 exclusion_filter = exclude_params
276 else:
277 exclusion_filter.update(exclude_params)
278
279 filter = self._buildFilter([uid], params)
280 if exclusion_filter is not None:
281 exclusion_filter = self._buildFilter([uid], exclusion_filter)
282 events = self.zep.getEventSummariesFromArchive(limit=limit, offset=start, sort=self._buildSort(sort,dir),
283 filter=filter, exclusion_filter=exclusion_filter)
284 eventFormat = EventCompatInfo
285 if detailFormat:
286 eventFormat = EventCompatDetailInfo
287
288 dmd = self.context.dmd
289
290 evdata = self._filterInvalidUuids(events['events'])
291 eventObs = [eventFormat(dmd, e) for e in evdata]
292 return DirectResponse.succeed(
293 events = Zuul.marshal(eventObs, keys),
294 totalCount = events['total'],
295 asof = time.time()
296 )
297
298 @serviceConnectionError
299 @require('ZenCommon')
300 - def query(self, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None,
301 page=None, archive=False, uid=None, detailFormat=False):
302 """
303 Query for events.
304
305 @type limit: integer
306 @param limit: (optional) Max index of events to retrieve (default: 0)
307 @type start: integer
308 @param start: (optional) Min index of events to retrieve (default: 0)
309 @type sort: string
310 @param sort: (optional) Key on which to sort the return results (default:
311 'lastTime')
312 @type dir: string
313 @param dir: (optional) Sort order; can be either 'ASC' or 'DESC'
314 (default: 'DESC')
315 @type params: dictionary
316 @param params: (optional) Key-value pair of filters for this search.
317 (default: None)
318 @type history: boolean
319 @param history: (optional) True to search the event history table instead
320 of active events (default: False)
321 @type uid: string
322 @param uid: (optional) Context for the query (default: None)
323 @rtype: dictionary
324 @return: B{Properties}:
325 - events: ([dictionary]) List of objects representing events
326 - totalCount: (integer) Total count of events returned
327 - asof: (float) Current time
328 """
329 if not self._canViewEvents():
330 return DirectResponse.succeed(
331 events = [],
332 totalCount = 0,
333 asof = time.time()
334 )
335
336 if archive:
337 return self.queryArchive(limit=limit, start=start, sort=sort,
338 dir=dir, params=params, exclusion_filter=exclusion_filter, keys=keys, uid=uid,
339 detailFormat=detailFormat)
340
341 def child_uids(org):
342 """Return list of uids for children of Organizer org."""
343 return [x.getPrimaryId() for x in org.children()]
344
345
346
347
348
349 uids = {
350 '/zport/dmd/Devices': child_uids(self.context.dmd.Devices),
351 '/zport/dmd/Locations': child_uids(self.context.dmd.Locations),
352 '/zport/dmd/Groups': child_uids(self.context.dmd.Groups),
353 '/zport/dmd/Systems': child_uids(self.context.dmd.Systems),
354 }.get(uid, [uid])
355
356 exclude_params = self._filterParser.findExclusionParams(params)
357 if len(exclude_params) > 0:
358 if exclusion_filter is None:
359 exclusion_filter = exclude_params
360 else:
361 exclusion_filter.update(exclude_params)
362
363 filter = self._buildFilter(uids, params)
364 if exclusion_filter is not None:
365 exclusion_filter = self._buildFilter(uids, exclusion_filter)
366 events = self.zep.getEventSummaries(limit=limit, offset=start, sort=self._buildSort(sort,dir), filter=filter,
367 exclusion_filter=exclusion_filter, use_permissions=self.use_permissions)
368 eventFormat = EventCompatInfo
369 if detailFormat:
370 eventFormat = EventCompatDetailInfo
371
372 dmd = self.context.dmd
373
374 evdata = self._filterInvalidUuids(events['events'])
375 eventObs = [eventFormat(dmd, e) for e in evdata]
376
377 return DirectResponse.succeed(
378 events = Zuul.marshal(eventObs, keys),
379 totalCount = events['total'],
380 asof = time.time()
381 )
382
383
384 @serviceConnectionError
385 @require('ZenCommon')
386 - def queryGenerator(self, sort='lastTime', dir='desc', evids=None, excludeIds=None, params=None,
387 archive=False, uid=None, detailFormat=False):
388 """
389 Query for events.
390
391 @type sort: string
392 @param sort: (optional) Key on which to sort the return results (default:
393 'lastTime')
394 @type dir: string
395 @param dir: (optional) Sort order; can be either 'ASC' or 'DESC'
396 (default: 'DESC')
397 @type params: dictionary
398 @param params: (optional) Key-value pair of filters for this search.
399 (default: None)
400 @type archive: boolean
401 @param archive: (optional) True to search the event archive instead
402 of active events (default: False)
403 @type uid: string
404 @param uid: (optional) Context for the query (default: None)
405 @rtype: generator
406 @return: Generator returning events.
407 """
408
409 if isinstance(params, basestring):
410 params = loads(params)
411
412 if not self._canViewEvents():
413 return
414 includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds)
415
416 events = self.zep.getEventSummariesGenerator(filter=includeFilter, exclude=excludeFilter,
417 sort=self._buildSort(sort,dir), archive=archive)
418 eventFormat = EventCompatInfo
419 if detailFormat:
420 eventFormat = EventCompatDetailInfo
421 for event in events:
422 yield Zuul.marshal(eventFormat(self.context.dmd, event))
423
424 - def _buildSort(self, sort='lastTime', dir='desc'):
425 sort_list = [(sort,dir)]
426
427 if sort not in ('lastTime','evid'):
428 sort_list.append(('lastTime','desc'))
429 return sort_list
430
431
432 - def _buildFilter(self, uids, params, specificEventUuids=None, includeContextInUid=True):
433 """
434 Construct a dictionary that can be converted into an EventFilter protobuf.
435
436 @type params: dictionary
437 @param params: (optional) Key-value pair of filters for this search.
438 (default: None)
439 @type uids: iterable(string)
440 @param uids: (optional) Contexts for the query (default: None)
441 """
442 if not uids:
443 uids=[]
444 elif isinstance(uids, basestring):
445 uids = [uids]
446
447 if params:
448 log.debug('logging params for building filter: %s', params)
449 if isinstance(params, basestring):
450 params = loads(params)
451
452
453
454
455
456 params, details = self.zep.parseParameterDetails(params)
457
458 filterEventUuids = []
459
460
461 if specificEventUuids is None:
462 log.debug('No specific event uuids were passed in.')
463
464
465
466
467 evid = params.get('evid')
468 if evid:
469 if not isinstance(evid,(list, tuple)):
470 evid = [evid]
471 filterEventUuids.extend(evid)
472
473
474 else:
475 log.debug('Specific event uuids passed in: %s', specificEventUuids)
476 if not isinstance(specificEventUuids,(list, tuple)):
477 filterEventUuids = [specificEventUuids]
478 else:
479 filterEventUuids = specificEventUuids
480
481 log.debug('FilterEventUuids is: %s', filterEventUuids)
482
483
484
485 param_tags = params.get('tags')
486 if params.get('excludeNonActionables') and not Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.context):
487 if not param_tags:
488 us = self.context.dmd.ZenUsers.getUserSettings()
489 param_tags = [IGlobalIdentifier(ar.managedObject()).getGUID() for ar in us.getAllAdminRoles()]
490 if param_tags:
491 param_tags = [tag for tag in param_tags if Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.manager.getObject(tag))]
492 if not param_tags:
493 param_tags = ['dne']
494
495 status_filter = params.get('eventState', [])
496 if params.get('eventStateText', None):
497 status_filter = list(set(status_filter + params.get('eventStateText')))
498
499 filter_params = {
500 'severity': params.get('severity'),
501 'status': status_filter,
502 'event_class': filter(None, [params.get('eventClass')]),
503 'first_seen': params.get('firstTime') and self._timeRange(params.get('firstTime')),
504 'last_seen': params.get('lastTime') and self._timeRange(params.get('lastTime')),
505 'status_change': params.get('stateChange') and self._timeRange(params.get('stateChange')),
506 'uuid': filterEventUuids,
507 'count_range': params.get('count'),
508 'element_title': params.get('device'),
509 'element_sub_title': params.get('component'),
510 'event_summary': params.get('summary'),
511 'current_user_name': params.get('ownerid'),
512 'agent': params.get('agent'),
513 'monitor': params.get('monitor'),
514 'fingerprint': params.get('dedupid'),
515 'tags': param_tags,
516 'details': details,
517 'event_key': params.get('eventKey'),
518 'event_class_key': params.get('eventClassKey'),
519 'event_group': params.get('eventGroup'),
520 'message': params.get('message'),
521 }
522 parsed_params = self._filterParser.parseParams(params)
523
524
525 filter_params.update(parsed_params)
526 if filter_params['first_seen'] is not None and len(filter_params['first_seen']) == 2:
527 filter_params['first_seen'][1] = filter_params['first_seen'][1]+1000
528 if filter_params['last_seen'] is not None and len(filter_params['last_seen']) == 2:
529 filter_params['last_seen'][1] = filter_params['last_seen'][1]+1000
530
531 parsed_details = self._filterParser.parseDetails(details)
532 if len(parsed_details) > 0:
533 filter_params['details'].update(parsed_details)
534
535 event_filter = self.zep.createEventFilter(**filter_params)
536 log.debug('Found params for building filter, ended up building the following:')
537 log.debug(event_filter)
538 elif specificEventUuids:
539
540 event_filter = self.zep.createEventFilter(
541 uuid = specificEventUuids
542 )
543 else:
544 log.debug('Did not get parameters, using empty filter.')
545 event_filter = {}
546
547 if not uids and includeContextInUid:
548 uids = [self.context]
549
550 contexts = (resolve_context(uid) for uid in uids)
551
552 context_uuids = []
553 for context in contexts:
554 if context and context.id not in ('Events', 'Devices', 'dmd'):
555 try:
556
557 if not context_uuids:
558 context_tag_filter = {
559 'tag_uuids': context_uuids
560 }
561
562
563 tag_filter = event_filter.setdefault('tag_filter', [])
564 tag_filter.append(context_tag_filter)
565 context_uuids.append(IGlobalIdentifier(context).getGUID())
566
567 except TypeError:
568 if isinstance(context, EventClass):
569 event_filter['event_class'] = [context.getDmdKey()]
570 else:
571 raise Exception('Unknown context %s' % context)
572
573 log.debug('Final filter will be:')
574 log.debug(event_filter)
575
576 return event_filter
577
579 """
580 Get event details.
581
582 @type evid: string
583 @param evid: Event ID to get details
584 @type history: boolean
585 @param history: Deprecated
586 @rtype: DirectResponse
587 @return: B{Properties}:
588 - event: ([dictionary]) List containing a dictionary representing
589 event details
590 """
591 event_summary = self.zep.getEventSummary(evid)
592 if event_summary:
593 eventData = Zuul.marshal(EventCompatDetailInfo(self.context.dmd, event_summary))
594 return DirectResponse.succeed(event=[eventData])
595 else:
596 raise Exception('Could not find event %s' % evid)
597
599 try:
600 dmd = get_dmd()
601 target_permission = permission.lower()
602 events_filter = self._buildFilter(uids=None, params={}, specificEventUuids=evids)
603 event_summaries = self.zep.getEventSummaries(0, filter=events_filter, use_permissions=True)
604 devices = set()
605 for summary in event_summaries['events']:
606 d = EventCompatInfo(self.context.dmd, summary)
607 dev_obj = dmd.getObjByPath(d.device['uid'])
608 devices.add(dev_obj)
609 for device in devices:
610 if not permissionsForContext(device)[target_permission]:
611 return False
612 return True
613 except Exception as e:
614 log.debug(e)
615 return False
616
617 - def manage_events(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None):
618 user = self.context.dmd.ZenUsers.getUserSettings()
619 if Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.context):
620 return True
621 if params.get('excludeNonActionables'):
622 return Zuul.checkPermission('ZenCommon', self.context)
623 if user.hasNoGlobalRoles():
624 try:
625 if uid is not None:
626 organizer_name = self.context.dmd.Devices.getOrganizer(uid).getOrganizerName()
627 else:
628 return self._hasPermissionsForAllEvents(ZEN_MANAGE_EVENTS, evids)
629 except (AttributeError, KeyError):
630 return False
631 manage_events_for = (r.managedObjectName() for r in user.getAllAdminRoles() if r.role in READ_WRITE_ROLES)
632 return organizer_name in manage_events_for
633 return False
634
636 data = self.detail(evid).data['event'][0]
637 uuid = data['component_uuid'] or data['device_uuid']
638 if uuid is None:
639 ctx = self.context
640 else:
641 ctx = self.manager.getObject(uuid)
642 return Zuul.checkPermission(ZEN_MANAGE_EVENTS, ctx)
643
644 @require(write_event_logs)
645 - def write_log(self, evid=None, message=None):
646 """
647 Write a message to an event's log.
648
649 @type evid: string
650 @param evid: Event ID to log to
651 @type message: string
652 @param message: Message to log
653 @rtype: DirectResponse
654 @return: Success message
655 """
656
657 userName = getSecurityManager().getUser().getId()
658
659 self.zep.addNote(uuid=evid, message=clean_html(message), userName=userName)
660
661 return DirectResponse.succeed()
662
663 @require(ZEN_MANAGE_EVENTS)
664 - def postNote(self, uuid, note):
665 self.zep.postNote(uuid, note)
666 return DirectResponse.succeed()
667
669 """
670 Given common request parameters, build the inclusive and exclusive
671 filters for event update requests.
672 """
673
674 if uid is None and isinstance(self.context, EventClass):
675 uid = self.context
676
677 log.debug('Context while building request filters is: %s', uid)
678
679
680
681
682
683 includeUuids = None
684 if isinstance(evids, (list, tuple)):
685 log.debug('Found specific event ids, adding to params.')
686 includeUuids = evids
687
688 exclude_params = self._filterParser.findExclusionParams(params)
689 includeFilter = self._buildFilter([uid], params, specificEventUuids=includeUuids)
690
691
692
693 excludeFilter = None
694 if excludeIds or len(exclude_params) > 0:
695 if excludeIds is None:
696 excludeIds = {}
697
698
699 excludeFilter = self._buildFilter(None, exclude_params,
700 specificEventUuids=excludeIds.keys(),
701 includeContextInUid=False)
702
703 log.debug('The exclude filter:' + str(excludeFilter))
704 log.debug('Finished building request filters.')
705
706 return includeFilter, excludeFilter
707
708 @require(ZEN_MANAGE_EVENTS)
710 """
711 When performing updates from the event console, updates are performed in batches
712 to allow the user to see the progress of event changes and cancel out of updates
713 while they are in progress. This works by specifying a limit to one of the close,
714 acknowledge, or reopen calls in this router. The response will contain an
715 EventSummaryUpdateResponse, and if there are additional updates to be performed,
716 it will contain a next_request field with all of the parameters used to update
717 the next range of events.
718
719 @type next_request: dictionary
720 @param next_request: The next_request field from the previous updates.
721 """
722 log.debug('Starting next batch of updates')
723 status, summaryUpdateResponse = self.zep.nextEventSummaryUpdate(next_request)
724
725 log.debug('Completed updates: %s', summaryUpdateResponse)
726 return DirectResponse.succeed(data=summaryUpdateResponse)
727
728 @require(ZEN_MANAGE_EVENTS)
730 """
731 @type params: dictionary
732 @param params: Key-value pair of filters for this search.
733 """
734 if isinstance(params, basestring):
735 params = loads(params)
736
737 device = params['device']
738
739 log.debug('Clearing heartbeats for device: {device}'.format(device=device))
740
741 params['eventState'] = [STATUS_NEW, STATUS_ACKNOWLEDGED]
742 params['eventClass'] = '/Status/Heartbeat'
743
744 includeFilter, excludeFilter = self._buildRequestFilters(None, params, None, None)
745
746 status, summaryUpdateResponse = self.zep.closeEventSummaries(
747 eventFilter=includeFilter,
748 exclusionFilter=excludeFilter,
749 limit=limit,
750 )
751
752 log.debug('Done clearing heartbeats for device: {device}'.format(device=device))
753 log.debug(summaryUpdateResponse)
754 audit('UI.Device.ClearHeartbeats', device=device)
755
756 return DirectResponse.succeed(data=summaryUpdateResponse)
757
758 @require(manage_events)
759 - def close(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None):
760 """
761 Close event(s).
762
763 @type evids: [string]
764 @param evids: (optional) List of event IDs to close (default: None)
765 @type excludeIds: [string]
766 @param excludeIds: (optional) List of event IDs to exclude from
767 close (default: None)
768 @type params: dictionary
769 @param params: (optional) Key-value pair of filters for this search.
770 (default: None)
771 @type uid: string
772 @param uid: (optional) Context for the query (default: None)
773 @type asof: float
774 @param asof: (optional) Only close if there has been no state
775 change since this time (default: None)
776 @type limit: The maximum number of events to update in this batch.
777 @param limit: (optional) Maximum number of events to update (default: None).
778 @type timeout: int
779 @param timeout: The time (in seconds) before the underlying saved search times out.
780 @rtype: DirectResponse
781 @return: Success message
782 """
783
784 log.debug('Issuing a close request.')
785
786 includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds)
787
788 status, summaryUpdateResponse = self.zep.closeEventSummaries(
789 eventFilter=includeFilter,
790 exclusionFilter=excludeFilter,
791 limit=limit,
792 timeout=timeout,
793 )
794
795 log.debug('Done issuing close request.')
796 log.debug(summaryUpdateResponse)
797
798 return DirectResponse.succeed(data=summaryUpdateResponse)
799
800 @require(manage_events)
801 - def acknowledge(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None):
802 """
803 Acknowledge event(s).
804
805 @type evids: [string]
806 @param evids: (optional) List of event IDs to acknowledge (default: None)
807 @type excludeIds: [string]
808 @param excludeIds: (optional) List of event IDs to exclude from
809 acknowledgment (default: None)
810 @type params: dictionary
811 @param params: (optional) Key-value pair of filters for this search.
812 (default: None)
813 @type uid: string
814 @param uid: (optional) Context for the query (default: None)
815 @type asof: float
816 @param asof: (optional) Only acknowledge if there has been no state
817 change since this time (default: None)
818 @type limit: The maximum number of events to update in this batch.
819 @param limit: (optional) Maximum number of events to update (default: None).
820 @type timeout: int
821 @param timeout: The time (in seconds) before the underlying saved search times out.
822 @rtype: DirectResponse
823 @return: Success message
824 """
825 log.debug('Issuing an acknowledge request.')
826
827 includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds)
828
829 status, summaryUpdateResponse = self.zep.acknowledgeEventSummaries(
830 eventFilter=includeFilter,
831 exclusionFilter=excludeFilter,
832 limit=limit,
833 timeout=timeout,
834 )
835 log.debug('Done issuing acknowledge request.')
836 log.debug(summaryUpdateResponse)
837
838 return DirectResponse.succeed(data=summaryUpdateResponse)
839
840 @require(manage_events)
841 @deprecated
843 """
844 Deprecated, Use reopen
845 """
846 return self.reopen(*args, **kwargs)
847
848 @require(manage_events)
849 - def reopen(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None):
850 """
851 Reopen event(s).
852
853 @type evids: [string]
854 @param evids: (optional) List of event IDs to reopen (default: None)
855 @type excludeIds: [string]
856 @param excludeIds: (optional) List of event IDs to exclude from
857 reopen (default: None)
858 @type params: dictionary
859 @param params: (optional) Key-value pair of filters for this search.
860 (default: None)
861 @type uid: string
862 @param uid: (optional) Context for the query (default: None)
863 @type asof: float
864 @param asof: (optional) Only reopen if there has been no state
865 change since this time (default: None)
866 @type limit: The maximum number of events to update in this batch.
867 @param limit: (optional) Maximum number of events to update (Default: None).
868 @type timeout: int
869 @param timeout: The time (in seconds) before the underlying saved search times out.
870 @rtype: DirectResponse
871 @return: Success message
872 """
873
874 log.debug('Issuing a reopen request.')
875
876 includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds)
877
878 status, summaryUpdateResponse = self.zep.reopenEventSummaries(
879 eventFilter=includeFilter,
880 exclusionFilter=excludeFilter,
881 limit=limit,
882 timeout=timeout,
883 )
884
885 log.debug('Done issuing reopen request.')
886 log.debug(summaryUpdateResponse)
887
888 return DirectResponse.succeed(data=summaryUpdateResponse)
889
890
891 @require(ZEN_MANAGE_EVENTS)
892 - def updateEventSummaries(self, update, event_filter=None, exclusion_filter=None, limit=None, timeout=None):
893 status, response = self.zep.updateEventSummaries(update, event_filter, exclusion_filter, limit, timeout=timeout)
894 return DirectResponse.succeed(data=response)
895
896 @require(ZEN_MANAGE_EVENTS)
897 - def add_event(self, summary, device, component, severity, evclasskey,
898 evclass=None, monitor=None, **kwargs):
899 """
900 Create a new event.
901
902 @type summary: string
903 @param summary: New event's summary
904 @type device: string
905 @param device: Device id to use for new event
906 @type component: string
907 @param component: Component uid to use for new event
908 @type severity: string
909 @param severity: Severity of new event. Can be one of the following:
910 Critical, Error, Warning, Info, Debug, or Clear
911 @type evclasskey: string
912 @param evclasskey: The Event Class Key to assign to this event
913 @type evclass: string
914 @param evclass: Event class for the new event
915 @rtype: DirectResponse
916
917 For other parameters please see class Event.
918 """
919 device = device.strip()
920 try:
921
922 summary = cgi.escape(summary)
923 device = cgi.escape(device)
924 component = cgi.escape(component)
925 evclasskey = cgi.escape(evclasskey)
926 if evclass is not None and len(evclass) > 0:
927 evclass = cgi.escape(evclass)
928 self.zep.create(summary, severity, device, component,
929 eventClassKey=evclasskey, eventClass=evclass,
930 monitor=monitor, **kwargs)
931 return DirectResponse.succeed("Created event")
932 except NoConsumersException:
933
934
935 msg = 'Queued event. Check zeneventd status on <a href="/zport/dmd/daemons">Services</a>'
936 return DirectResponse.succeed(msg, sticky=True)
937 except PublishException, e:
938
939 log.exception("Failed creating event")
940 return DirectResponse.exception(e, "Failed to create event")
941
942 @property
944 configSchema =[{
945 'id': 'event_age_disable_severity',
946 'name': _t("Don't Age This Severity and Above"),
947 'xtype': 'eventageseverity',
948 },{
949 'id': 'event_age_severity_inclusive',
950 'xtype': 'hidden',
951 },{
952 'id': 'event_age_interval_minutes',
953 'name': _t('Event Aging Threshold (minutes)'),
954 'xtype': 'numberfield',
955 'minValue': 0,
956 'allowNegative': False,
957 },{
958 'id': 'aging_interval_milliseconds',
959 'name': _t('Event Aging Interval (milliseconds)'),
960 'xtype': 'numberfield',
961 'minValue': 1,
962 'allowNegative': False
963 },{
964 'id': 'aging_limit',
965 'name': _t('Event Aging Limit'),
966 'xtype': 'numberfield',
967 'minValue': 1,
968 'allowNegative': False
969 },{
970 'id': 'event_archive_interval_minutes',
971 'name': _t('Event Archive Threshold (minutes)'),
972 'xtype': 'numberfield',
973 'minValue': 1,
974 'maxValue': 43200,
975 'allowNegative': False,
976 },{
977 'id': 'archive_interval_milliseconds',
978 'name': _t('Event Archive Interval (milliseconds)'),
979 'xtype': 'numberfield',
980 'minValue': 1,
981 'allowNegative': False,
982 },{
983 'id': 'archive_limit',
984 'name': _t('Event Archive Limit'),
985 'xtype': 'numberfield',
986 'minValue': 1,
987 'allowNegative': False,
988 },{
989 'id': 'event_archive_purge_interval_days',
990 'minValue': 1,
991 'name': _t('Delete Archived Events Older Than (days)'),
992 'xtype': 'numberfield',
993 'allowNegative': False,
994 },{
995 'id': 'default_syslog_priority',
996 'name': _t('Default Syslog Priority'),
997 'xtype': 'numberfield',
998 'allowNegative': False,
999 'value': self.context.dmd.ZenEventManager.defaultPriority
1000 },{
1001 'id': 'default_availability_days',
1002 'name': _t('Default Availability Report (days)'),
1003 'xtype': 'numberfield',
1004 'allowNegative': False,
1005 'minValue': 1,
1006 'value': self.context.dmd.ZenEventManager.defaultAvailabilityDays
1007 },{
1008 'id': 'event_max_size_bytes',
1009 'name': _t('Max Event Size In Bytes'),
1010 'xtype': 'numberfield',
1011 'allowNegative': False,
1012 'minValue': 8192,
1013 'maxValue': 102400,
1014 },{
1015 'id': 'index_summary_interval_milliseconds',
1016 'name': _t('Summary Index Interval (milliseconds)'),
1017 'xtype': 'numberfield',
1018 'allowNegative': False,
1019 'minValue': 1
1020 },{
1021 'id': 'index_archive_interval_milliseconds',
1022 'name': _t('Archive Index Interval (milliseconds)'),
1023 'xtype': 'numberfield',
1024 'allowNegative': False,
1025 'minValue': 1
1026 },{
1027 'id': 'index_limit',
1028 'name': _t('Index Limit'),
1029 'xtype': 'numberfield',
1030 'allowNegative': False,
1031 'minValue': 1
1032 },{
1033 'id': 'event_time_purge_interval_days',
1034 'name': _t('Event Time Purge Interval (days)'),
1035 'xtype': 'numberfield',
1036 'allowNegative': False,
1037 'minValue': 1
1038 },{
1039 'id': 'enable_event_flapping_detection',
1040 'name': _t('Enable Event Flapping Detection'),
1041 'xtype': 'checkbox',
1042 }, {
1043 'id': 'flapping_event_class',
1044 'name': _t('Event Flapping Event Class'),
1045 'xtype': 'eventclass'
1046 }]
1047 return configSchema
1048
1050 """
1051 Copy the values and defaults from ZEP to our schema
1052 """
1053 for conf in configSchema:
1054 if not data.get(conf['id']):
1055 continue
1056 prop = data[conf['id']]
1057 conf.update(prop)
1058 return configSchema
1059
1060 @require('ZenCommon')
1068
1069 @require('Manage DMD')
1071 """
1072 @type values: Dictionary
1073 @param values: Key Value pairs of config values
1074 """
1075
1076 empty_keys = [k for k,v in values.iteritems() if isinstance(v, basestring) and not len(v)]
1077 for empty_key in empty_keys:
1078 del values[empty_key]
1079
1080
1081 defaultSyslogPriority = values.pop('default_syslog_priority', None)
1082 if defaultSyslogPriority is not None:
1083 self.context.dmd.ZenEventManager.defaultPriority = int(defaultSyslogPriority)
1084
1085 defaultAvailabilityDays = values.pop('default_availability_days', None)
1086 if defaultAvailabilityDays is not None:
1087 self.context.dmd.ZenEventManager.defaultAvailabilityDays = int(defaultAvailabilityDays)
1088
1089 self.zep.setConfigValues(values)
1090 return DirectResponse.succeed()
1091
1093 """
1094 Get the current event console field column configuration.
1095
1096 @type uid: string
1097 @param uid: (optional) UID context to use (default: None)
1098 @type archive: boolean
1099 @param archive: (optional) True to use the event archive instead
1100 of active events (default: False)
1101 @rtype: [dictionary]
1102 @return: A list of objects representing field columns
1103 """
1104 return column_config(self.request, archive)
1105
1106 @require(ZEN_MANAGE_EVENTS)
1108 """
1109 Associate event(s) with an event class.
1110
1111 @type evrows: [dictionary]
1112 @param evrows: List of event rows to classify
1113 @type evclass: string
1114 @param evclass: Event class to associate events to
1115 @rtype: DirectResponse
1116 @return: B{Properties}:
1117 - msg: (string) Success/failure message
1118 - success: (boolean) True if class update successful
1119 """
1120 msg, url = self.zep.createEventMapping(evrows, evclass)
1121 if url:
1122 msg += " | " + url.split('/dmd/')[1]
1123 audit('UI.Event.Classify', evrows, message=msg, event_class=evclass)
1124 return DirectResponse(msg, success=bool(url))
1125
1126 @require(ZEN_MANAGE_EVENTS)
1128 """
1129 Clear all heartbeat events
1130
1131 @rtype: DirectResponse
1132 @return: B{Properties}:
1133 - success: (boolean) True if heartbeats deleted successfully
1134 """
1135 self.zep.deleteHeartbeats()
1136 audit('UI.Event.ClearHeartbeats', self.context)
1137 return DirectResponse.succeed()
1138
1139 @require(ZEN_MANAGE_EVENTS)
1141 """
1142 Clears a specific heartbeat event.
1143
1144 @type monitor: basestring
1145 @param monitor: The heartbeat monitor (i.e. 'localhost').
1146 @type daemon: basestring
1147 @param daemon: The heartbeat daemon (i.e. 'zenhub').
1148 @rtype: DirectResponse
1149 @return: A DirectResponse indicating success or failure.
1150 """
1151 self.zep.deleteHeartbeat(monitor, daemon)
1152 audit('UI.Event.ClearHeartbeat', self.context, monitor=monitor,
1153 daemon=daemon)
1154 return DirectResponse.succeed()
1155
1156 @require(ZEN_MANAGE_EVENTS)
1158 """
1159 On success, returns the status.
1160 """
1161 try:
1162 resp = self.zep.updateDetails(evid, **detailInfo)
1163 except ServiceResponseError as ex:
1164 return DirectResponse.fail(msg=str(ex))
1165 audit('UI.Event.UpdateEventDetails', self.context, evid=evid,
1166 details=detailInfo)
1167 return DirectResponse.succeed(status=resp[0]['status'])
1168