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