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.Zuul.utils import get_dmd 
  37  from Products.ZenUI3.browser.eventconsole.grid import column_config 
  38  from Products.ZenUI3.security.security import permissionsForContext 
  39  from Products.Zuul.interfaces import ICatalogTool 
  40  from Products.Zuul.infos.event import EventCompatInfo, EventCompatDetailInfo 
  41  from zenoss.protocols.services import ServiceResponseError 
  42  from lxml.html.clean import clean_html 
  43   
  44  READ_WRITE_ROLES = ['ZenManager', 'Manager', 'ZenOperator'] 
  45   
  46  log = logging.getLogger('zen.%s' % __name__) 
47 48 -class _FilterParser(object):
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
58 - def __init__(self, zep_facade):
59 """ """ 60 # Gets some config params from the zep facade 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 # Sets config variables 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
96 - def findExclusionParams(self, params):
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
128 - def _addWildcardsToFilter(self, field, value):
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
139 - def _getOrClauses(self, field, value):
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 # For details we need to translate the NULL_CHAR to the value used to index null 163 # details in lucene. 164 # The value used to index null details is different depending on if the detail 165 # is numeric or text 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
172 - def parseParams(self, params):
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
189 - def parseDetails(self, details):
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
205 -class EventsRouter(DirectRouter):
206 """ 207 A JSON/ExtDirect interface to operations on events in ZEP 208 """ 209
210 - def __init__(self, context, request):
211 super(EventsRouter, self).__init__(context, request) 212 self.zep = Zuul.getFacade('zep', context) 213 self.catalog = ICatalogTool(context) 214 self.manager = IGUIDManager(context.dmd) 215 self._filterParser = _FilterParser(self.zep) 216 self.use_permissions = False
217
218 - def _canViewEvents(self):
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 # make sure they have view permission on something 227 if len(user.getAllAdminRoles()) > 0: 228 self.use_permissions = True 229 return len(user.getAllAdminRoles()) > 0
230
231 - def _timeRange(self, value):
232 try: 233 values = [] 234 splitter = ' TO ' if ' TO ' in value else '/' 235 for t in value.split(splitter): 236 values.append(int(isoToTimestamp(t)) * 1000) 237 return values 238 except ValueError: 239 log.warning("Invalid timestamp: %s", value) 240 return ()
241
242 - def _filterInvalidUuids(self, events):
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 # element 252 if actor.get('element_uuid') and \ 253 actor.get('element_uuid') not in manager.table: 254 del actor['element_uuid'] 255 256 # sub element 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 # filter out the component and device UUIDs that no longer exist in our system 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 # Events don't get tagged with the top-level organizers. We compensate 346 # for that here so that the events view for Devices, Groups, Systems, 347 # and Locations will show aggregation of all of the top-level 348 # organizers children. 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 # filter out the component and device UUIDs that no longer exist in our system 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 # Add secondary sort of last time descending 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 # params comes from the grid's filtering column - 453 # some of these properties are normal properties on an event 454 # while others are considered event details. Separate the 455 # two here. 456 params, details = self.zep.parseParameterDetails(params) 457 458 filterEventUuids = [] 459 # No specific event uuids passed in- 460 # check for event ids from the grid parameters 461 if specificEventUuids is None: 462 log.debug('No specific event uuids were passed in.') 463 464 # The evid's from params only ever mean anything for filtering - if 465 # specific uuids are passed in, this filter will ignore the grid 466 # parameters and just act on or filter using these specific event uuids. 467 evid = params.get('evid') 468 if evid: 469 if not isinstance(evid,(list, tuple)): 470 evid = [evid] 471 filterEventUuids.extend(evid) 472 473 # Specific event uuids were passed in, use those for this filter. 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 # 'tags' comes from managed object guids. 484 # see Zuul/security/security.py 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'] # Filter everything (except "does not exist'). An empty tag list would be ignored. 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 # ZEN-23418/ZEN-26147: Add 1 sec to first_seen and last_seen range filters, 524 # so that it includes {events: event['{first|last}_seen'] <= params['{firstTime|lastTime}']} 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 # if they passed in specific uuids but not other params 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 # make a specific instance of tag_filter just for the context tag. 557 if not context_uuids: 558 context_tag_filter = { 559 'tag_uuids': context_uuids 560 } 561 # if it exists, filter['tag_filter'] will be a list. just append the special 562 # context tag filter to whatever that list is. 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
578 - def detail(self, evid):
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
598 - def _hasPermissionsForAllEvents(self, permission, evids):
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
635 - def write_event_logs(self, evid=None, message=None):
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
668 - def _buildRequestFilters(self, uid, params, evids, excludeIds):
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 # if the request contains specific event summaries to act on, they will 680 # be passed in as evids. Excluded event summaries are passed in under 681 # the keyword argument 'excludeIds'. If these exist, pass them in as 682 # parameters to be used to construct the EventFilter. 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 # the only thing excluded in an event filter is a list of event uuids 692 # which are passed as EventTagFilter using the OR operator. 693 excludeFilter = None 694 if excludeIds or len(exclude_params) > 0: 695 if excludeIds is None: 696 excludeIds = {} 697 # make sure the exclude filter doesn't include the context 698 # otherwise all event actions wont have an effect. 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)
709 - def nextEventSummaryUpdate(self, next_request):
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)
729 - def clear_device_heartbeats(self, params, limit=None):
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
842 - def unacknowledge(self, *args, **kwargs):
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 897 @require(ZEN_MANAGE_EVENTS)
898 - def add_event(self, summary, device, component, severity, evclasskey, 899 evclass=None, monitor=None, **kwargs):
900 """ 901 Create a new event. 902 903 @type summary: string 904 @param summary: New event's summary 905 @type device: string 906 @param device: Device id to use for new event 907 @type component: string 908 @param component: Component uid to use for new event 909 @type severity: string 910 @param severity: Severity of new event. Can be one of the following: 911 Critical, Error, Warning, Info, Debug, or Clear 912 @type evclasskey: string 913 @param evclasskey: The Event Class Key to assign to this event 914 @type evclass: string 915 @param evclass: Event class for the new event 916 @rtype: DirectResponse 917 918 For other parameters please see class Event. 919 """ 920 device = device.strip() # ZEN-2479: support entries like "localhost " 921 try: 922 self.zep.create(summary, severity, device, component, 923 eventClassKey=evclasskey, eventClass=evclass, 924 monitor=monitor, **kwargs) 925 return DirectResponse.succeed("Created event") 926 except NoConsumersException: 927 # This occurs if the event is queued but there are no consumers - i.e. zeneventd is not 928 # currently running. 929 msg = 'Queued event. Check zeneventd status on <a href="/zport/dmd/daemons">Services</a>' 930 return DirectResponse.succeed(msg, sticky=True) 931 except PublishException, e: 932 # This occurs if there is a failure publishing the event to the queue. 933 log.exception("Failed creating event") 934 return DirectResponse.exception(e, "Failed to create event")
935 936 @property
937 - def configSchema(self):
938 configSchema =[{ 939 'id': 'event_age_disable_severity', 940 'name': _t("Don't Age This Severity and Above"), 941 'xtype': 'eventageseverity', 942 },{ 943 'id': 'event_age_severity_inclusive', 944 'xtype': 'hidden', 945 },{ 946 'id': 'event_age_interval_minutes', 947 'name': _t('Event Aging Threshold (minutes)'), 948 'xtype': 'numberfield', 949 'minValue': 0, 950 'allowNegative': False, 951 },{ 952 'id': 'aging_interval_milliseconds', 953 'name': _t('Event Aging Interval (milliseconds)'), 954 'xtype': 'numberfield', 955 'minValue': 1, 956 'allowNegative': False 957 },{ 958 'id': 'aging_limit', 959 'name': _t('Event Aging Limit'), 960 'xtype': 'numberfield', 961 'minValue': 1, 962 'allowNegative': False 963 },{ 964 'id': 'event_archive_interval_minutes', 965 'name': _t('Event Archive Threshold (minutes)'), 966 'xtype': 'numberfield', 967 'minValue': 1, 968 'maxValue': 43200, 969 'allowNegative': False, 970 },{ 971 'id': 'archive_interval_milliseconds', 972 'name': _t('Event Archive Interval (milliseconds)'), 973 'xtype': 'numberfield', 974 'minValue': 1, 975 'allowNegative': False, 976 },{ 977 'id': 'archive_limit', 978 'name': _t('Event Archive Limit'), 979 'xtype': 'numberfield', 980 'minValue': 1, 981 'allowNegative': False, 982 },{ 983 'id': 'event_archive_purge_interval_days', 984 'minValue': 1, 985 'name': _t('Delete Archived Events Older Than (days)'), 986 'xtype': 'numberfield', 987 'allowNegative': False, 988 },{ 989 'id': 'default_syslog_priority', 990 'name': _t('Default Syslog Priority'), 991 'xtype': 'numberfield', 992 'allowNegative': False, 993 'value': self.context.dmd.ZenEventManager.defaultPriority 994 },{ 995 'id': 'default_availability_days', 996 'name': _t('Default Availability Report (days)'), 997 'xtype': 'numberfield', 998 'allowNegative': False, 999 'minValue': 1, 1000 'value': self.context.dmd.ZenEventManager.defaultAvailabilityDays 1001 },{ 1002 'id': 'event_max_size_bytes', 1003 'name': _t('Max Event Size In Bytes'), 1004 'xtype': 'numberfield', 1005 'allowNegative': False, 1006 'minValue': 8192, 1007 'maxValue': 102400, 1008 },{ 1009 'id': 'index_summary_interval_milliseconds', 1010 'name': _t('Summary Index Interval (milliseconds)'), 1011 'xtype': 'numberfield', 1012 'allowNegative': False, 1013 'minValue': 1 1014 },{ 1015 'id': 'index_archive_interval_milliseconds', 1016 'name': _t('Archive Index Interval (milliseconds)'), 1017 'xtype': 'numberfield', 1018 'allowNegative': False, 1019 'minValue': 1 1020 },{ 1021 'id': 'index_limit', 1022 'name': _t('Index Limit'), 1023 'xtype': 'numberfield', 1024 'allowNegative': False, 1025 'minValue': 1 1026 },{ 1027 'id': 'event_time_purge_interval_days', 1028 'name': _t('Event Time Purge Interval (days)'), 1029 'xtype': 'numberfield', 1030 'allowNegative': False, 1031 'minValue': 1 1032 },{ 1033 'id': 'enable_event_flapping_detection', 1034 'name': _t('Enable Event Flapping Detection'), 1035 'xtype': 'checkbox', 1036 }, { 1037 'id': 'flapping_event_class', 1038 'name': _t('Event Flapping Event Class'), 1039 'xtype': 'eventclass' 1040 }] 1041 return configSchema
1042
1043 - def _mergeSchemaAndZepConfig(self, data, configSchema):
1044 """ 1045 Copy the values and defaults from ZEP to our schema 1046 """ 1047 for conf in configSchema: 1048 if not data.get(conf['id']): 1049 continue 1050 prop = data[conf['id']] 1051 conf.update(prop) 1052 return configSchema
1053 1054 @require('ZenCommon')
1055 - def getConfig(self):
1056 # this data var is not a ZepConfig, it's a config structure that has been 1057 # constructed to include default values and be keyed by the protobuf 1058 # property name. 1059 data = self.zep.getConfig() 1060 config = self._mergeSchemaAndZepConfig(data, self.configSchema) 1061 return DirectResponse.succeed(data=config)
1062 1063 @require('Manage DMD')
1064 - def setConfigValues(self, values):
1065 """ 1066 @type values: Dictionary 1067 @param values: Key Value pairs of config values 1068 """ 1069 # Remove empty strings from values 1070 empty_keys = [k for k,v in values.iteritems() if isinstance(v, basestring) and not len(v)] 1071 for empty_key in empty_keys: 1072 del values[empty_key] 1073 1074 # we store default syslog priority and default availability days on the event manager 1075 defaultSyslogPriority = values.pop('default_syslog_priority', None) 1076 if defaultSyslogPriority is not None: 1077 self.context.dmd.ZenEventManager.defaultPriority = int(defaultSyslogPriority) 1078 1079 defaultAvailabilityDays = values.pop('default_availability_days', None) 1080 if defaultAvailabilityDays is not None: 1081 self.context.dmd.ZenEventManager.defaultAvailabilityDays = int(defaultAvailabilityDays) 1082 1083 self.zep.setConfigValues(values) 1084 return DirectResponse.succeed()
1085
1086 - def column_config(self, uid=None, archive=False):
1087 """ 1088 Get the current event console field column configuration. 1089 1090 @type uid: string 1091 @param uid: (optional) UID context to use (default: None) 1092 @type archive: boolean 1093 @param archive: (optional) True to use the event archive instead 1094 of active events (default: False) 1095 @rtype: [dictionary] 1096 @return: A list of objects representing field columns 1097 """ 1098 return column_config(self.request, archive)
1099 1100 @require(ZEN_MANAGE_EVENTS)
1101 - def classify(self, evrows, evclass):
1102 """ 1103 Associate event(s) with an event class. 1104 1105 @type evrows: [dictionary] 1106 @param evrows: List of event rows to classify 1107 @type evclass: string 1108 @param evclass: Event class to associate events to 1109 @rtype: DirectResponse 1110 @return: B{Properties}: 1111 - msg: (string) Success/failure message 1112 - success: (boolean) True if class update successful 1113 """ 1114 msg, url = self.zep.createEventMapping(evrows, evclass) 1115 if url: 1116 msg += " | " + url.split('/dmd/')[1] 1117 audit('UI.Event.Classify', evrows, message=msg, event_class=evclass) 1118 return DirectResponse(msg, success=bool(url))
1119 1120 @require(ZEN_MANAGE_EVENTS)
1121 - def clear_heartbeats(self):
1122 """ 1123 Clear all heartbeat events 1124 1125 @rtype: DirectResponse 1126 @return: B{Properties}: 1127 - success: (boolean) True if heartbeats deleted successfully 1128 """ 1129 self.zep.deleteHeartbeats() 1130 audit('UI.Event.ClearHeartbeats', self.context) 1131 return DirectResponse.succeed()
1132 1133 @require(ZEN_MANAGE_EVENTS)
1134 - def clear_heartbeat(self, monitor, daemon):
1135 """ 1136 Clears a specific heartbeat event. 1137 1138 @type monitor: basestring 1139 @param monitor: The heartbeat monitor (i.e. 'localhost'). 1140 @type daemon: basestring 1141 @param daemon: The heartbeat daemon (i.e. 'zenhub'). 1142 @rtype: DirectResponse 1143 @return: A DirectResponse indicating success or failure. 1144 """ 1145 self.zep.deleteHeartbeat(monitor, daemon) 1146 audit('UI.Event.ClearHeartbeat', self.context, monitor=monitor, 1147 daemon=daemon) 1148 return DirectResponse.succeed()
1149 1150 @require(ZEN_MANAGE_EVENTS)
1151 - def updateDetails(self, evid, **detailInfo):
1152 """ 1153 On success, returns the status. 1154 """ 1155 try: 1156 resp = self.zep.updateDetails(evid, **detailInfo) 1157 except ServiceResponseError as ex: 1158 return DirectResponse.fail(msg=str(ex)) 1159 audit('UI.Event.UpdateEventDetails', self.context, evid=evid, 1160 details=detailInfo) 1161 return DirectResponse.succeed(status=resp[0]['status'])
1162