Package Products :: Package Zuul :: Package routers :: Module zep
[hide private]
[frames] | no frames]

Source Code for Module Products.Zuul.routers.zep

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