def getDeviceDashboard(self): """return device info for bad device to dashboard""" zep = getFacade('zep') manager = IGUIDManager(self.context.dmd) deviceSeverities = zep.getDeviceIssuesDict() zem = self.context.dmd.ZenEventManager devdata = [] for uuid in deviceSeverities.keys(): dev = manager.getObject(uuid) if dev and isinstance(dev, Device): if (not zem.checkRemotePerm(ZEN_VIEW, dev) or dev.productionState < zem.prodStateDashboardThresh or dev.priority < zem.priorityDashboardThresh): continue alink = dev.getPrettyLink() try: severities = deviceSeverities[uuid] severities = dict( (zep.getSeverityName(sev).lower(), counts) for (sev, counts) in severities.iteritems()) pill = getEventPillME(dev, severities=severities) except ServiceException: continue evts = [alink, pill] devdata.append((evts, severities)) devdata.sort(key=lambda x: (x[1]['critical'], x[1]['error'], x[1]['warning']), reverse=True) return [x[0] for x in devdata[:100]]
def __call__(self, *args, **kwargs): """ Takes a guid in the request and redirects the browser to the object's url """ manager = IGUIDManager(self.context) request = self.request response = self.request.response obj = None guid = request.get('guid', None) if not guid: return response.write("The guid paramater is required") # they passed in a uid instead of a guid try: if guid.startswith("/zport/dmd/"): obj = self.context.unrestrictedTraverse(unquote(guid)) else: obj = manager.getObject(guid) except KeyError: pass if not obj: return response.write("Could not look up guid %s" % guid) path = obj.absolute_url_path() return response.redirect(path)
def getProcessList(self, deviceGuid): if deviceGuid and len(deviceGuid) > 0: s = '' try: device = IGUIDManager(self._dmd).getObject(deviceGuid) s = '# ' + device.id response = Response() zenmodelerOpts = ['run', '--now', '--debug', '-v10', '-d', device.id] exitCode = device.perfServer().executeCollectorCommand('zenmodeler', zenmodelerOpts, REQUEST=None, write=response.write) processList = response.getLines() if not exitCode: filterProcessLine = re.compile('DEBUG zen\.osprocessmatcher: COMMAND LINE: (?P<process>.*)$') for processListLine in processList: m = filterProcessLine.search(processListLine) if m: s += '\n' + m.group('process') else: s += "==> " + str(exitCode) for line in processList: s += '\n# ' + line.strip() except Exception, e: s += '\n# ' + str(e) yield { 'uid': '0', 'process': s }
def impacts_for(thing): ''' Return a two element tuple. First element is a list of object ids impacted by thing. Second element is a list of object ids impacting thing. ''' try: from ZenPacks.zenoss.Impact.impactd.interfaces import \ IRelationshipDataProvider except ImportError: return ([], []) impacted_by = [] impacting = [] guid_manager = IGUIDManager(thing.getDmd()) for subscriber in subscribers([thing], IRelationshipDataProvider): for edge in subscriber.getEdges(): source = guid_manager.getObject(edge.source) impacted = guid_manager.getObject(edge.impacted) if source == thing: impacted_by.append(impacted.id) elif impacted == thing: impacting.append(source.id) return (impacted_by, impacting)
def getProcessList(self, deviceGuid): if deviceGuid and len(deviceGuid) > 0: s = '' try: import subprocess from Products.ZenUtils.Utils import binPath device = IGUIDManager(self._dmd).getObject(deviceGuid) s = '# ' + device.id zenmodelerOpts = ['run', '--now', '--debug', '-v10', '-d', device.id] isPerfMonRemote = False zenmodelerName = 'zenmodeler' zenmodelerPath = binPath(zenmodelerName) monitorId = device.perfServer().id if monitorId != 'localhost': zenmodelerName = monitorId + '_' + zenmodelerName zenmodelerPath = binPath(zenmodelerName) if len(zenmodelerPath) == 0: isPerfMonRemote = True if isPerfMonRemote: cmd = "ssh %s %s %s" % (device.perfServer().hostname, zenmodelerName, ' '.join(zenmodelerOpts)) else: cmd = "%s %s" % (zenmodelerPath, ' '.join(zenmodelerOpts)) processList = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT).splitlines() filterProcessLine = re.compile('DEBUG zen\.osprocessmatcher: COMMAND LINE: (?P<process>.*)$') for processListLine in processList: m = filterProcessLine.search(processListLine) if m: s += '\n' + m.group('process') except Exception, e: s += '\n# ' + str(e) yield { 'uid': '0', 'process': s }
def impacts_for(thing): ''' Return a two element tuple. First element is a list of object ids impacted by thing. Second element is a list of object ids impacting thing. ''' from ZenPacks.zenoss.Impact.impactd.interfaces \ import IRelationshipDataProvider impacted_by = [] impacting = [] guid_manager = IGUIDManager(thing.getDmd()) for subscriber in subscribers([thing], IRelationshipDataProvider): for edge in subscriber.getEdges(): source = guid_manager.getObject(edge.source) impacted = guid_manager.getObject(edge.impacted) if source.id == thing.id: impacted_by.append(impacted.id) elif impacted.id == thing.id: impacting.append(source.id) return (impacted_by, impacting)
def getProcessList(self, deviceGuid): if deviceGuid and len(deviceGuid) > 0: s = '' try: device = IGUIDManager(self._dmd).getObject(deviceGuid) s = '# ' + device.id response = Response() zenmodelerOpts = [ 'run', '--now', '--debug', '-v10', '-d', device.id ] exitCode = device.perfServer().executeCollectorCommand( 'zenmodeler', zenmodelerOpts, REQUEST=None, write=response.write) processList = response.getLines() if not exitCode: filterProcessLine = re.compile( 'DEBUG zen\.osprocessmatcher: COMMAND LINE: (?P<process>.*)$' ) for processListLine in processList: m = filterProcessLine.search(processListLine) if m: s += '\n' + m.group('process') else: s += "==> " + str(exitCode) for line in processList: s += '\n# ' + line.strip() except Exception, e: s += '\n# ' + str(e) yield {'uid': '0', 'process': s}
def getProcessList(self, deviceGuid): if deviceGuid and len(deviceGuid) > 0: s = '' try: import subprocess from Products.ZenUtils.Utils import binPath device = IGUIDManager(self._dmd).getObject(deviceGuid) s = '# ' + device.id zenmodelerOpts = [ 'run', '--now', '--debug', '-v10', '-d', device.id ] isPerfMonRemote = False zenmodelerName = 'zenmodeler' zenmodelerPath = binPath(zenmodelerName) monitorId = device.perfServer().id if monitorId != 'localhost': isPerfMonRemote = True if isPerfMonRemote: cmd = 'zminion --minion-name zminion_%s run -- "%s %s"' % ( device.getPerformanceServerName(), zenmodelerName, ' '.join(zenmodelerOpts)) else: cmd = "%s %s" % (zenmodelerPath, ' '.join(zenmodelerOpts)) processList = subprocess.check_output( cmd, shell=True, stderr=subprocess.STDOUT).splitlines() filterProcessLine = re.compile( 'DEBUG zen\.osprocessmatcher: COMMAND LINE: (?P<process>.*)$' ) for processListLine in processList: m = filterProcessLine.search(processListLine) if m: s += '\n' + m.group('process') except Exception, e: s += '\n# ' + str(e) yield {'uid': '0', 'process': s}
def lookupGuid(guid): """ Given a guid this returns the object that it identifies """ from Products.ZenUtils.guid.interfaces import IGUIDManager manager = IGUIDManager(dmd) return manager.getObject(guid)
def getDeviceDashboard(self): """return device info for bad device to dashboard""" zep = getFacade('zep') manager = IGUIDManager(self.context.dmd) deviceSeverities = zep.getDeviceIssuesDict() zem = self.context.dmd.ZenEventManager devdata = [] for uuid in deviceSeverities.keys(): dev = manager.getObject(uuid) if dev and isinstance(dev, Device): if (not zem.checkRemotePerm(ZEN_VIEW, dev) or dev.productionState < zem.prodStateDashboardThresh or dev.priority < zem.priorityDashboardThresh): continue alink = dev.getPrettyLink() try: severities = deviceSeverities[uuid] severities = dict((zep.getSeverityName(sev).lower(), counts) for (sev, counts) in severities.iteritems()) pill = getEventPillME(dev, severities=severities) except ServiceException: continue evts = [alink,pill] devdata.append((evts, severities)) devdata.sort(key=lambda x:(x[1]['critical'], x[1]['error'], x[1]['warning']), reverse=True) return [x[0] for x in devdata[:100]]
def __init__(self, dmd, event_summary): self._dmd = dmd self._event_summary = event_summary self._eventOccurrence = event_summary['occurrence'][0] self._eventActor = self._eventOccurrence['actor'] self._eventDetails = self._findDetails(self._eventOccurrence) self._catalog = IModelCatalogTool(dmd) self._manager = IGUIDManager(dmd)
def __init__(self, context): super(ZepFacade, self).__init__(context) config = getGlobalConfiguration() zep_url = config.get('zep-uri', 'http://localhost:8084') schema = getUtility(IQueueSchema) self.client = ZepServiceClient(zep_url, schema) self.configClient = ZepConfigClient(zep_url, schema) self.heartbeatClient = ZepHeartbeatClient(zep_url, schema) self._guidManager = IGUIDManager(context.dmd)
def manage_addAdministrativeRole(self, name=None, type='device', role=None, guid=None, uid=None, REQUEST=None): "Add a Admin Role to the passed object" unused(role) mobj = None if guid or uid: # look up our object by either guid or uid if guid: manager = IGUIDManager(self.dmd) mobj = manager.getObject(guid) elif uid: mobj = self.unrestrictedTraverse(uid) else: # use magic to look up our object if not name: name = REQUEST.deviceName if type == 'device': mobj =self.getDmdRoot("Devices").findDevice(name) else: try: root = type.capitalize()+'s' if type == "deviceClass": mobj = self.getDmdRoot("Devices").getOrganizer(name) else: mobj = self.getDmdRoot(root).getOrganizer(name) except KeyError: pass if not mobj: if REQUEST: messaging.IMessageSender(self).sendToBrowser( 'Error', "%s %s not found"%(type.capitalize(),name), priority=messaging.WARNING ) return self.callZenScreen(REQUEST) else: return roleNames = [ r.id for r in mobj.adminRoles() ] if self.id in roleNames: if REQUEST: messaging.IMessageSender(self).sendToBrowser( 'Error', (("Administrative Role for %s %s " "for user %s already exists.") % (type, name, self.id)), priority=messaging.WARNING ) return self.callZenScreen(REQUEST) else: return mobj.manage_addAdministrativeRole(self.id) if REQUEST: messaging.IMessageSender(self).sendToBrowser( 'Role Added', ("Administrative Role for %s %s for user %s added" % (type, name, self.id)) ) audit('UI.User.AddAdministrativeRole', username=self.id, data_={mobj.meta_type:mobj.getPrimaryId()}) return self.callZenScreen(REQUEST)
def _initCatalogs(self): self._guidManager = IGUIDManager(self.dmd) self._devices = self.dmd._getOb('Devices') self._networks = self.dmd._getOb('Networks') self._events = self.dmd._getOb('Events') self._catalogs = { DEVICE: self._devices, }
def __init__(self, context): super(TriggersFacade, self).__init__(context) self._guidManager = IGUIDManager(self._dmd) config = getGlobalConfiguration() schema = getUtility(IQueueSchema) self.triggers_service = TriggerServiceClient(config.get('zep_uri', 'http://localhost:8084'), schema) self.notificationPermissions = NotificationPermissionManager() self.triggerPermissions = TriggerPermissionManager()
def getElements(self): manager = IGUIDManager(self.getDmd()) memberObjs = [] for poolmember in self.members: obj = manager.getObject(poolmember) if obj: memberObjs.append(obj) else: log.warn("Stale ElementPool member: %s", poolmember) return memberObjs
def _getDevices(self, dmd, results): """ Look up the devices from the events. """ manager = IGUIDManager(dmd) events = results['events'] for event in events: occurrence = event['occurrence'][0] actor_uuid = occurrence['actor'].get('element_uuid') if not actor_uuid: continue dev = manager.getObject(actor_uuid) if dev: yield dev
def _getDevices(self, dmd, results, eventClass): """ Look up the devices from the events. """ manager = IGUIDManager(dmd) events = results['events'] for event in events: occurrence = event['occurrence'][0] actor_uuid = occurrence['actor'].get('element_uuid') if not actor_uuid: continue dev = manager.getObject(actor_uuid) if dev: yield dev
def __init__(self, context, request): super(EventsRouter, self).__init__(context, request) self.zep = Zuul.getFacade('zep', context) self.catalog = IModelCatalogTool(context) self.manager = IGUIDManager(context.dmd) self._filterParser = _FilterParser(self.zep) self.use_permissions = False
def id_from_guid_fn(dmd): guid_manager = IGUIDManager(dmd) def id_from_guid(guid): return guid_manager.getObject(guid).id return id_from_guid
def getDeviceDashboard(self): """return device info for bad device to dashboard""" zep = getFacade('zep') manager = IGUIDManager(self.context.dmd) deviceSeverities = zep.getDeviceIssuesDict() zem = self.context.dmd.ZenEventManager bulk_data = [] for uuid in deviceSeverities.keys(): uuid_data = {} uuid_data['uuid'] = uuid severities = deviceSeverities[uuid] try: uuid_data['severities'] = dict( (zep.getSeverityName(sev).lower(), counts) for (sev, counts) in severities.iteritems()) except ServiceException: continue bulk_data.append(uuid_data) bulk_data.sort(key=lambda x: (x['severities']['critical'], x[ 'severities']['error'], x['severities']['warning']), reverse=True) devices_found = 0 MAX_DEVICES = 100 devdata = [] for data in bulk_data: uuid = data['uuid'] severities = data['severities'] dev = manager.getObject(uuid) if dev and isinstance(dev, Device): if (not zem.checkRemotePerm(ZEN_VIEW, dev) or dev.getProductionState() < zem.prodStateDashboardThresh or dev.priority < zem.priorityDashboardThresh): continue alink = dev.getPrettyLink() pill = getEventPillME(dev, severities=severities) evts = [alink, pill] devdata.append(evts) devices_found = devices_found + 1 if devices_found >= MAX_DEVICES: break return devdata
def __init__(self, dmd, event_summary): self._dmd = dmd self._event_summary = event_summary self._eventOccurrence = event_summary['occurrence'][0] self._eventActor = self._eventOccurrence['actor'] self._eventDetails = self._findDetails(self._eventOccurrence) self._catalog = ICatalogTool(dmd) self._manager = IGUIDManager(dmd)
def getDeviceIssues(self): zep = getFacade('zep', self._dmd) manager = IGUIDManager(self._dmd) deviceSeverities = zep.getDeviceIssuesDict() zem = self.context.dmd.ZenEventManager devdata = [] # only get the first 100 since this is just the portlet for uuid in deviceSeverities.keys()[:100]: dev = manager.getObject(uuid) if dev and isinstance(dev, Device): if (not zem.checkRemotePerm(ZEN_VIEW, dev) or dev.productionState < zem.prodStateDashboardThresh or dev.priority < zem.priorityDashboardThresh): continue severities = deviceSeverities[uuid] info = IInfo(dev) info.setEventSeverities(severities) devdata.append(info) return devdata
def getDeviceDashboard(self): """return device info for bad device to dashboard""" zep = getFacade('zep') manager = IGUIDManager(self.context.dmd) deviceSeverities = zep.getDeviceIssuesDict() zem = self.context.dmd.ZenEventManager bulk_data = [] for uuid in deviceSeverities.keys(): uuid_data = {} uuid_data['uuid'] = uuid severities = deviceSeverities[uuid] try: uuid_data['severities'] = dict((zep.getSeverityName(sev).lower(), counts) for (sev, counts) in severities.iteritems()) except ServiceException: continue bulk_data.append(uuid_data) bulk_data.sort(key=lambda x:(x['severities']['critical'], x['severities']['error'], x['severities']['warning']), reverse=True) devices_found = 0 MAX_DEVICES = 100 devdata = [] for data in bulk_data: uuid = data['uuid'] severities = data['severities'] dev = manager.getObject(uuid) if dev and isinstance(dev, Device): if (not zem.checkRemotePerm(ZEN_VIEW, dev) or dev.productionState < zem.prodStateDashboardThresh or dev.priority < zem.priorityDashboardThresh): continue alink = dev.getPrettyLink() pill = getEventPillME(dev, severities=severities) evts = [alink,pill] devdata.append(evts) devices_found = devices_found + 1 if devices_found >= MAX_DEVICES: break return devdata
def impacts_for(thing): ''' Return a two element tuple. First element is a list of object ids impacted by thing. Second element is a list of object ids impacting thing. ''' from ZenPacks.zenoss.Impact.impactd.interfaces \ import IRelationshipDataProvider impacted_by = [] impacting = [] guid_manager = IGUIDManager(thing.getDmd()) for subscriber in subscribers([thing], IRelationshipDataProvider): for edge in subscriber.getEdges(): if edge.source == guid(thing): impacted_by.append(guid_manager.getObject(edge.impacted).id) elif edge.impacted == guid(thing): impacting.append(guid_manager.getObject(edge.source).id) return (impacted_by, impacting)
class TriggersFacade(ZuulFacade): def __init__(self, context): super(TriggersFacade, self).__init__(context) self._guidManager = IGUIDManager(self._dmd) config = getGlobalConfiguration() schema = getUtility(IQueueSchema) self.triggers_service = TriggerServiceClient( config.get('zep_uri', 'http://localhost:8084'), schema) self.notificationPermissions = NotificationPermissionManager() self.triggerPermissions = TriggerPermissionManager() def _removeNode(self, obj): """ Remove an object in ZODB. This method was created to provide a hook for unit tests. """ context = aq_parent(obj) return context._delObject(obj.id) def _removeTriggerFromZep(self, uuid): """ Remove a trigger from ZEP. This method was created to provide a hook for unit tests. """ return self.triggers_service.removeTrigger(uuid) def removeNode(self, uid): obj = self._getObject(uid) return self._removeNode(obj) def _setTriggerGuid(self, trigger, guid): """ @param trigger: The trigger object to set the guid on. @type trigger: Products.ZenModel.Trigger.Trigger @param guid: The guid @type guid: str This method was created to provide a hook for unit tests. """ IGlobalIdentifier(trigger).guid = guid def _getTriggerGuid(self, trigger): """ @param trigger: The trigger object in zodb. @type trigger: Products.ZenModel.Trigger.Trigger This method was created to provide a hook for unit tests. """ return IGlobalIdentifier(trigger).guid def _setupTriggerPermissions(self, trigger): """ This method was created to provide a hook for unit tests. """ self.triggerPermissions.setupTrigger(trigger) def synchronize(self): """ This method will first synchronize all triggers that exist in ZEP to their corresponding objects in ZODB. Then, it will clean up notifications and remove any subscriptions to triggers that no longer exist. """ log.debug('SYNC: Starting trigger and notification synchronization.') _, trigger_set = self.triggers_service.getTriggers() zep_uuids = set(t.uuid for t in trigger_set.triggers) zodb_triggers = self._getTriggerManager().objectValues() # delete all triggers in zodb that do not exist in zep. for t in zodb_triggers: if not self._getTriggerGuid(t) in zep_uuids: log.info( 'SYNC: Found trigger in zodb that does not exist in zep, removing: %s' % t.id) self._removeNode(t) zodb_triggers = self._getTriggerManager().objectValues() zodb_uuids = set(self._getTriggerGuid(t) for t in zodb_triggers) # create all triggers in zodb that do not exist in zep. for t in trigger_set.triggers: if not t.uuid in zodb_uuids: log.info( 'SYNC: Found trigger uuid in zep that does not seem to exist in zodb, creating: %s' % t.name) triggerObject = Trigger(str(t.name)) try: self._getTriggerManager()._setObject( triggerObject.id, triggerObject) except BadRequest: # looks like the id already exists, remove this specific # trigger from zep. This can happen if multiple createTrigger # requests are sent from the browser at once - the transaction # will not abort until after the requests to create a trigger # have already been sent to zep. # See https://dev.zenoss.com/tracint/ticket/28272 log.info( 'SYNC: Found trigger with duplicate id in zodb, deleting from zep: %s (%s)' % (triggerObject.id, t.uuid)) self._removeTriggerFromZep(t.uuid) else: # setting a guid fires off events, we have to acquire the object # before we adapt it, otherwise adapters responding to the event # will get the 'regular' Trigger object and not be able to handle # it. self._setTriggerGuid( self._getTriggerManager().findChild(triggerObject.id), str(t.uuid)) self._setupTriggerPermissions( self._getTriggerManager().findChild(t.name)) # sync notifications for n in self._getNotificationManager().getChildNodes(): is_changed = False subs = list(n.subscriptions) for s in subs: if s not in zep_uuids: # this trigger no longer exists in zep, remove it from # this notification's subscriptions. log.info( 'SYNC: Notification subscription no longer valid: %s' % s) is_changed = True n.subscriptions.remove(s) if is_changed: log.debug('SYNC: Updating notification subscriptions: %s' % n.id) self.updateNotificationSubscriptions(n) log.debug('SYNC: Trigger and notification synchronization complete.') def getTriggers(self): self.synchronize() user = getSecurityManager().getUser() response, trigger_set = self.triggers_service.getTriggers() trigger_set = to_dict(trigger_set) if 'triggers' in trigger_set: return self.triggerPermissions.findTriggers( user, self._guidManager, trigger_set['triggers']) else: return [] def parseFilter(self, source): """ Parse a filter to make sure it's sane. @param source: The python expression to test. @type source: string @todo: make this not allow nasty python. """ if isinstance(source, basestring): if source: tree = parser.expr(source) if parser.isexpr(tree): return source else: raise Exception('Invalid filter expression.') else: return True # source is empty string def addTrigger(self, newId): return self.createTrigger(name=newId, uuid=None, rule=dict(source='')) def createTrigger(self, name, uuid=None, rule=None): name = str(name) zodb_triggers = self._getTriggerManager().objectValues() zodb_trigger_names = set(t.id for t in zodb_triggers) if name in zodb_trigger_names: raise DuplicateTriggerName, ( 'The id "%s" is invalid - it is already in use.' % name) triggerObject = Trigger(name) self._getTriggerManager()._setObject(name, triggerObject) acquired_trigger = self._getTriggerManager().findChild(name) if uuid: IGlobalIdentifier(acquired_trigger).guid = str(uuid) else: IGlobalIdentifier(acquired_trigger).create() self.triggerPermissions.setupTrigger(acquired_trigger) trigger = zep.EventTrigger() trigger.uuid = IGlobalIdentifier(acquired_trigger).guid trigger.name = name trigger.rule.api_version = 1 trigger.rule.type = zep.RULE_TYPE_JYTHON if rule and 'source' in rule: trigger.rule.source = rule['source'] else: trigger.rule.source = '' self.triggers_service.addTrigger(trigger) log.debug('Created trigger with uuid: %s ' % trigger.uuid) return trigger.uuid def removeTrigger(self, uuid): user = getSecurityManager().getUser() trigger = self._guidManager.getObject(uuid) if self.triggerPermissions.userCanUpdateTrigger(user, trigger): # If a user has the ability to update (remove) a trigger, it is # presumed that they will be consciously deleting triggers that # may have subscribers. # # Consider that that trigger may be subscribed to by notifications # that the current user cannot read/edit. # if there was an error, the triggers service will throw an exception self._removeTriggerFromZep(uuid) context = aq_parent(trigger) context._delObject(trigger.id) relevant_notifications = self.getNotificationsBySubscription(uuid) updated_count = 0 for n in relevant_notifications: n.subscriptions.remove(uuid) log.debug('Removing trigger uuid %s from notification: %s' % (uuid, n.id)) self.updateNotificationSubscriptions(n) updated_count += 1 return updated_count else: log.warning( 'User not authorized to remove trigger: User: %s, Trigger: %s' % (user.getId(), trigger.id)) raise Exception( 'User not authorized to remove trigger: User: %s, Trigger: %s' % (user.getId(), trigger.id)) def getNotificationsBySubscription(self, trigger_uuid): for n in self._getNotificationManager().getChildNodes(): if trigger_uuid in n.subscriptions: yield n def getTrigger(self, uuid): user = getSecurityManager().getUser() trigger = self._guidManager.getObject(uuid) log.debug('Trying to fetch trigger: %s' % trigger.id) if self.triggerPermissions.userCanViewTrigger(user, trigger): response, trigger = self.triggers_service.getTrigger(uuid) return to_dict(trigger) else: log.warning('User not authorized to view this trigger: %s' % trigger.id) raise Exception('User not authorized to view this trigger: %s' % trigger.id) def getTriggerList(self): """ Retrieve a list of all triggers by uuid and name. This is used by the UI to render triggers that a user may not have permission to otherwise view, edit or manage. """ response, trigger_set = self.triggers_service.getTriggers() trigger_set = to_dict(trigger_set) triggerList = [] if 'triggers' in trigger_set: for t in trigger_set['triggers']: triggerList.append(dict(uuid=t['uuid'], name=t['name'])) return sorted(triggerList, key=lambda k: k['name']) def updateTrigger(self, **data): user = getSecurityManager().getUser() triggerObj = self._guidManager.getObject(data['uuid']) log.debug('Trying to update trigger: %s' % triggerObj.id) if self.triggerPermissions.userCanManageTrigger(user, triggerObj): if 'globalRead' in data: triggerObj.globalRead = data.get('globalRead', False) log.debug('setting globalRead %s' % triggerObj.globalRead) if 'globalWrite' in data: triggerObj.globalWrite = data.get('globalWrite', False) log.debug('setting globalWrite %s' % triggerObj.globalWrite) if 'globalManage' in data: triggerObj.globalManage = data.get('globalManage', False) log.debug('setting globalManage %s' % triggerObj.globalManage) triggerObj.users = data.get('users', []) self.triggerPermissions.clearPermissions(triggerObj) self.triggerPermissions.updatePermissions(self._guidManager, triggerObj) if self.triggerPermissions.userCanUpdateTrigger(user, triggerObj): if "name" in data: triggerObj.setTitle(data["name"]) trigger = from_dict(zep.EventTrigger, data) response, content = self.triggers_service.updateTrigger(trigger) return content def _getTriggerManager(self): return self._dmd.findChild('Triggers') def _getNotificationManager(self): return self._dmd.findChild('NotificationSubscriptions') def getNotifications(self): self.synchronize() user = getSecurityManager().getUser() for n in self.notificationPermissions.findNotifications( user, self._getNotificationManager().getChildNodes()): yield IInfo(n) def _updateContent(self, notification, data=None): try: util = getUtility(IAction, notification.action) except ComponentLookupError: raise InvalidTriggerActionType( "Invalid action type specified: %s" % notification.action) fields = {} for iface in providedBy(util.getInfo(notification)): f = getFields(iface) if f: fields.update(f) data = util.getDefaultData(self._dmd) for k, v in fields.iteritems(): if k not in data: data[k] = v.default util.updateContent(notification.content, data) def addNotification(self, newId, action): notification = self.createNotification(newId, action) return IInfo(notification) def createNotification(self, id, action, guid=None): notification = NotificationSubscription(id) notification.action = action self._updateContent(notification) self._getNotificationManager()._setObject(id, notification) acquired_notification = self._getNotificationManager().findChild(id) self.notificationPermissions.setupNotification(acquired_notification) self.updateNotificationSubscriptions(notification) notification = self._getNotificationManager().findChild(id) notification.userRead = True notification.userWrite = True notification.userManage = True if guid: IGlobalIdentifier(notification).guid = guid return notification def removeNotification(self, uid): user = getSecurityManager().getUser() notification = self._getObject(uid) if self.notificationPermissions.userCanUpdateNotification( user, notification): return self.removeNode(uid) else: log.warning( 'User not authorized to remove notification: User: %s, Notification: %s' % (user.getId(), notification.id)) raise Exception('User not authorized to remove notification.') def getNotification(self, uid): user = getSecurityManager().getUser() notification = self._getObject(uid) if self.notificationPermissions.userCanViewNotification( user, notification): log.debug('getting notification: %s' % notification) return IInfo(notification) else: log.warning('User not authorized to view this notification: %s' % uid) raise Exception( 'User not authorized to view this notification: %s' % uid) def updateNotificationSubscriptions(self, notification): triggerSubscriptions = [] notification_guid = IGlobalIdentifier(notification).getGUID() for subscription in notification.subscriptions: triggerSubscriptions.append( dict( delay_seconds=notification.delay_seconds, repeat_seconds=notification.repeat_seconds, subscriber_uuid=notification_guid, send_initial_occurrence=notification. send_initial_occurrence, trigger_uuid=subscription, )) subscriptionSet = from_dict(zep.EventTriggerSubscriptionSet, dict(subscriptions=triggerSubscriptions)) self.triggers_service.updateSubscriptions(notification_guid, subscriptionSet) def updateNotification(self, **data): log.debug(data) uid = data['uid'] # can't change action type after creation if 'action' in data: del data['action'] notification = self._getObject(uid) if not notification: raise Exception('Could not find notification to update: %s' % uid) # don't update any properties unless the current user has the correct # permission. user = getSecurityManager().getUser() if self.notificationPermissions.userCanUpdateNotification( user, notification): # update the action content data action = getUtility(IAction, notification.action) action.updateContent(notification.content, data) if self.notificationPermissions.userCanManageNotification( user, notification): # if these values are not sent (in the case that the fields have been # disabled, do not set the value. if 'notification_globalRead' in data: notification.globalRead = data.get('notification_globalRead') log.debug('setting globalRead') if 'notification_globalWrite' in data: notification.globalWrite = data.get('notification_globalWrite') log.debug('setting globalWrite') if 'notification_globalManage' in data: notification.globalManage = data.get( 'notification_globalManage') log.debug('setting globalManage') for field in notification._properties: notification._updateProperty(field['id'], data.get(field['id'])) notification.subscriptions = data.get('subscriptions') self.updateNotificationSubscriptions(notification) notification.recipients = data.get('recipients', []) self.notificationPermissions.clearPermissions(notification) self.notificationPermissions.updatePermissions( self._guidManager, notification) log.debug('updated notification: %s' % notification) def getRecipientOptions(self): users = self._dmd.ZenUsers.getAllUserSettings() groups = self._dmd.ZenUsers.getAllGroupSettings() data = [] for u in users: data.append(self.fetchRecipientOption(u)) for g in groups: data.append(self.fetchRecipientOption(g)) return data def fetchRecipientOption(self, recipient): my_type = 'group' if isinstance(recipient, GroupSettings) else 'user' return dict( type=my_type, label='%s (%s)' % (recipient.getId(), my_type.capitalize()), value=IGlobalIdentifier(recipient).getGUID(), ) def getWindows(self, uid): notification = self._getObject(uid) for window in notification.windows(): yield IInfo(window) def addWindow(self, contextUid, newId): notification = self._getObject(contextUid) window = NotificationSubscriptionWindow(newId) notification.windows._setObject(newId, window) new_window = notification.windows._getOb(newId) return IInfo(new_window) def removeWindow(self, uid): return self.removeNode(uid) def getWindow(self, uid): window = self._getObject(uid) return IInfo(window) def updateWindow(self, data): uid = data['uid'] window = self._getObject(uid) if not window: raise Exception('Could not find window to update: %s' % uid) for field in window._properties: if field['id'] == 'start': start = data['start'] start = start + " T" + data['starttime'] startDT = datetime.strptime(start, "%m-%d-%Y T%H:%M") setattr(window, 'start', int(startDT.strftime('%s'))) elif field['id'] == 'duration': setattr(window, 'duration', int(data['duration'])) elif field['id'] == 'skip': skip = data.get('skip') if skip is not None: window.skip = skip else: setattr(window, field['id'], data.get(field['id'])) log.debug('updated window') def exportConfiguration(self, triggerIds=None, notificationIds=None): notifications = [] if notificationIds is None: notificationIds = [] notifications = list(self.getNotifications()) elif isinstance(notificationIds, str): notificationIds = [notificationIds] triggers = self.getTriggers() if triggerIds is not None: names = [triggerIds] if isinstance(triggerIds, str) else triggerIds triggers = [x for x in triggers if x['name'] in names] for trigger in triggers: uid = trigger['uuid'] nsIds = [ x.id for x in self.getNotificationsBySubscription(uid) ] notificationIds.extend(nsIds) triggerData = self.exportTriggers(triggers) if notificationIds: notifications = [ x for x in notifications if x.id in notificationIds ] notificationData = self.exportNotifications(notifications) return triggerData, notificationData def exportTriggers(self, triggers): configs = [] junkColumns = ('id', 'newId') for config in triggers: for item in junkColumns: if item in config: del config[item] configs.append(config) return configs def exportNotifications(self, notifications): configs = [] junkColumns = ('id', 'newId', 'uid', 'inspector_type', 'meta_type') for notificationInfo in notifications: config = marshal(notificationInfo) for item in junkColumns: if item in config: del config[item] contentsTab = self._extractNotificationContentInfo(config) del config['content'] config.update(contentsTab) config['recipients'] = [r['label'] for r in config['recipients']] config['subscriptions'] = [ x['name'] for x in config['subscriptions'] ] windows = [] for window in notificationInfo._object.windows(): winconfig = marshal(IInfo(window)) for witem in ('meta_type', 'newId', 'id', 'inspector_type', 'uid'): del winconfig[witem] windows.append(winconfig) config['windows'] = windows configs.append(config) return configs def _extractNotificationContentInfo(self, notification): contents = {} try: for itemInfo in notification['content']['items'][0]['items']: key = itemInfo['name'] contents[key] = itemInfo['value'] except Exception: log.exception("Unable to extract data from notifcation: %s", notification) return contents def importConfiguration(self, triggers=None, notifications=None): itcount, incount = 0, 0 if triggers: itcount = self.importTriggers(triggers) if notifications: incount = self.importNotifications(notifications) return itcount, incount def importTriggers(self, triggers): """ Add any new trigger definitions to the system. Note: modifies the triggers argument to add 'new_uuid' to the definition. Does not attempt to link a trigger to a notification. """ existingTriggers = [x['name'] for x in self.getTriggerList()] existingUsers = [x.id for x in self._dmd.ZenUsers.getAllUserSettings()] removeDataList = ['subscriptions'] imported = 0 for trigger in triggers: name = trigger.get('name') if name is None: log.warn("Missing name in trigger definition: %s", trigger) continue if name in existingTriggers: log.warn("Skipping existing trigger '%s'", name) continue data = deepcopy(trigger) trigger['new_uuid'] = data['uuid'] = self.addTrigger(name) # Cleanup for key in removeDataList: if key in data: del data[key] # Don't delete data from list you're looping through for user in trigger.get('users', []): if user not in existingUsers: log.warning( "Unable to find trigger %s user '%s' on this server -- skipping", name, user) data['users'].remove(user) # Make changes to the definition self.updateTrigger(**data) imported += 1 return imported def importNotifications(self, notifications): """ Add new notification definitions to the system. """ existingNotifications = [x.id for x in self.getNotifications()] existingTypes = [x.action for x in self.getNotifications()] usersGroups = dict((x['label'], x) for x in self.getRecipientOptions()) trigerToUuid = dict((x['name'], x['uuid']) for x in self.getTriggers()) imported = 0 for notification in notifications: name = notification.get('name') if name is None: log.warn("Missing name in notification definition: %s", notification) continue if name in existingNotifications: log.warn("Skipping existing notification '%s'", name) continue ntype = notification.get('action') if ntype is None: log.warn("Missing 'action' in notification definition: %s", notification) continue if ntype not in existingTypes: log.warn( "The notification %s references an unknown action type: %s", name, ntype) continue data = deepcopy(notification) obj = self.addNotification(name, ntype) notification['uid'] = data['uid'] = obj.getPrimaryUrlPath() self.getRecipientsToImport(name, data, usersGroups) if 'action' in data: del data['action'] self.linkImportedNotificationToTriggers(data, trigerToUuid) windows = data.get('windows', []) if windows: for window in windows: iwindow = self.addWindow(data['uid'], window['name']) del window['name'] window['uid'] = iwindow.uid self.updateWindow(window) del data['windows'] # Make changes to the definition self.updateNotification(**data) imported += 1 return imported def getRecipientsToImport(self, name, data, usersGroups): recipients = [] for label in data.get('recipients', []): if label in usersGroups: recipients.append(usersGroups[label]) else: log.warn( "Unable to find %s for recipients for notification %s", label, name) data['recipients'] = recipients def linkImportedNotificationToTriggers(self, notification, trigerToUuid): subscriptions = [] for subscription in notification.get('subscriptions', []): uuid = trigerToUuid.get(subscription['name']) if uuid is not None: subscriptions.append(uuid) else: log.warn( "Unable to link notification %s to missing trigger '%s'", notification['name'], subscription['name']) notification['subscriptions'] = subscriptions
def load(self, pack, app): """ Load Notifications and Triggers from an actions.json file Given a JSON-formatted configuration located at {zenpack}/actions/actions.json, create or update triggers and notifications specific to this zenpack. When creating or updating, the object is first checked to see whether or not an object exists with the configured guid for notifications or uuid for triggers. If an object is not found, one will be created. During creation, care is taken with regard to the id - integer suffixes will be appended to try to create a unique id. If we do not find a unique id after 100 tries, an error will occur. When updating an object, care is taken to not change the name as it may have since been altered by the user (or by this loader adding a suffix). """ log.debug("ZPTriggerAction: load") import Products.Zuul as Zuul from Products.Zuul.facades import ObjectNotFoundException tf = Zuul.getFacade('triggers', app.dmd) guidManager = IGUIDManager(app) for conf in findFiles(pack, 'zep',lambda f: f == 'actions.json'): import json data = json.load(open(conf, "r")) log.debug("DATA IS: %s" % data) triggers = data.get('triggers', []) notifications = data.get('notifications', []) tf.synchronize() all_names = set(t['name'] for t in tf.getTriggerList()) for trigger_conf in triggers: existing_trigger = guidManager.getObject(trigger_conf['uuid']) if existing_trigger: trigger_data = tf.getTrigger(trigger_conf['uuid']) trigger_conf['name'] = trigger_data['name'] log.info('Existing trigger found, updating: %s' % trigger_conf['name']) tf.updateTrigger(**trigger_conf) else: test_name = trigger_conf['name'] for x in xrange(1,101): if test_name in all_names: test_name = '%s_%d' % (trigger_conf['name'], x) else: log.debug('Found unused trigger name: %s' % test_name) break else: # if we didn't find a unique name raise Exception('Could not find unique name for trigger: "%s".' % trigger_conf['name']) log.info('Creating trigger: %s' % test_name) tf.createTrigger(test_name, uuid=trigger_conf['uuid'], rule=trigger_conf['rule']) for notification_conf in notifications: existing_notification = guidManager.getObject(str(notification_conf['guid'])) if existing_notification: log.info("Existing notification found, updating: %s" % existing_notification.id) subscriptions = set(existing_notification.subscriptions + notification_conf['subscriptions']) notification_conf['uid'] = '/zport/dmd/NotificationSubscriptions/%s' % existing_notification.id notification_conf['subscriptions'] = list(subscriptions) notification_conf['name'] = existing_notification.id tf.updateNotification(**notification_conf) else: test_id = notification_conf['id'] for x in xrange(1,101): test_uid = '/zport/dmd/NotificationSubscriptions/%s' % test_id try: tf.getNotification(test_uid) except ObjectNotFoundException: break test_id = '%s_%d' % (notification_conf['id'], x) else: # if we didn't find a unique name raise Exception('Could not find unique name for notification: "%s".' % notification_conf['id']) log.info('Creating notification: %s' % test_id) tf.createNotification(str(test_id), notification_conf['action'], notification_conf['guid']) notification_conf['uid'] = '/zport/dmd/NotificationSubscriptions/%s' % test_id tf.updateNotification(**notification_conf)
def cutover(self, dmd): # Check to see if the BTree exists btree = getattr(dmd, 'prodstate_table', None) if btree: guidManager = IGUIDManager(dmd) count = 0 total = len(btree) for guid, states in btree.iteritems(): obj = guidManager.getObject(guid) # 'ProdState' code no longer exists so 'states' object is going to be broken # Setting it this way instead of using 'setProdState' will NOT trigger a re-index # but anybody upgrading to this version is going to have to run a full re-index post-upgrade if not obj: continue try: obj.productionState = states.__Broken_state__['productionState'] obj.preMWProductionState = states.__Broken_state__['preMWProductionState'] except AttributeError: log.warning("Coulnd't get production state for %s, setting up 'Production' to it", obj) obj.productionState = 1000 obj.preMWProductionState = 1000 count += 1 if count % 100 == 0: log.info("Migrated production state for %d objects of %d", count, total) if count % 1000 == 0: log.info("Committing transaction for 1000 objects") transaction.commit() # Tidy up whatever's in the last 1000 that didn't get committed if count % 100: log.info("Migrated %d objects of %d", count, total) if count % 1000: log.info("Committing transaction for %d objects", count % 1000) transaction.commit() # Now drop the BTree log.info("Removing production state BTree") dmd._delOb('prodstate_table') transaction.commit() log.info("Migration Complete") elif not getattr(dmd, MIGRATED_FLAG, False): # We don't have a BTree, but we haven't been migrated yet, so this is from back before the BTree existed, # When prodstate was an attribute called 'productionState' on the object. # Since 'productionState' is now the name of a property, we have to pull the old productionState off of the # object's __dict__ def migrate_object(obj): obj._p_activate() # Activate the object so the old attributes end up in __dict__ if 'preMWProductionState' in obj.__dict__: obj.preMWProductionState = obj.__dict__['preMWProductionState'] del obj.__dict__['preMWProductionState'] if 'productionState' in obj.__dict__: obj.productionState = obj.__dict__['productionState'] del obj.__dict__['productionState'] count = 0 for device in dmd.Devices.getSubDevicesGen_recursive(): migrate_object(device) count += 1 # migrate components try: cmps = device.getDeviceComponents() except AttributeError: pass else: for c in cmps: migrate_object(c) if count % 100 == 0: log.info("Migrated production state for %d devices", count) if count % 1000 == 0: log.info("Committing transaction for 1000 objects") transaction.commit() # Tidy up whatever's in the last 1000 that didn't get committed if count % 100: log.info("Migrated %d devices", count) if count % 1000: log.info("Committing transaction for %d devices", count % 1000) transaction.commit() else: log.info("Nothing to migrate") setattr(dmd, MIGRATED_FLAG, True)
class Manager(object): """ Provides lookup access to processing pipes and performs caching. """ ELEMENT_TYPE_MAP = { DEVICE: Device, COMPONENT: DeviceComponent, } def __init__(self, dmd): self.dmd = dmd self._initCatalogs() def _initCatalogs(self): self._guidManager = IGUIDManager(self.dmd) self._devices = self.dmd._getOb('Devices') self._networks = self.dmd._getOb('Networks') self._events = self.dmd._getOb('Events') self._catalogs = { DEVICE: self._devices, } def reset(self): self._initCatalogs() def getEventClassOrganizer(self, eventClassName): try: return self._events.getOrganizer(eventClassName) except KeyError: # Unknown organizer return None def lookupEventClass(self, eventContext): """ Find a Device's EventClass """ return self._events.lookup(eventContext.eventProxy, eventContext.deviceObject) def getElementByUuid(self, uuid): """ Get a Device/Component by UUID """ if uuid: return self._guidManager.getObject(uuid) def uuidFromBrain(self, brain): """ Helper method to deal with catalog brains which are out of date. If the uuid is not set on the brain, we attempt to load it from the object. """ uuid = brain.uuid return uuid if uuid else IGlobalIdentifier(brain.getObject()).getGUID() @FunctionCache("getElementUuidById", cache_miss_marker=-1, default_timeout=300) def getElementUuidById(self, catalog, element_type_id, id): """ Find element by ID but only cache UUID. This forces us to lookup elements each time by UUID (pretty fast) which gives us a chance to see if the element has been deleted. """ cls = self.ELEMENT_TYPE_MAP.get(element_type_id) if cls: catalog = catalog or self._catalogs.get(element_type_id) if catalog: results = ICatalogTool(catalog).search(cls, query=Or( Eq('id', id), Eq('name', id)), filterPermissions=False, limit=1) if results.total: return self.uuidFromBrain(results.results.next()) def getElementById(self, catalog, element_type_id, id): """ Find element by ID, first checking a cache for UUIDs then using that UUID to load the element. If the element can't be found by UUID, the UUID cache is cleared and lookup tried again. """ uuid = self.getElementUuidById(catalog, element_type_id, id) if uuid: element = self.getElementByUuid(uuid) if not element: # Lookup cache must be invalid, try looking up again self.getElementUuidById.clear() log.warning( 'Clearing ElementUuidById cache becase we could not find %s' % uuid) uuid = self.getElementUuidById(catalog, element_type_id, id) element = self.getElementByUuid(uuid) return element def getElementUuid(self, obj): if obj: return IGlobalIdentifier(obj).getGUID() def _findDevices(self, identifier, ipAddress, limit=None): """ Returns a tuple ([device brains], [devices]) searching manage IP and interface IPs. limit is the maximum total number in both lists. """ dev_cat = ICatalogTool(self._devices) try: ip_address = next(i for i in (ipAddress, identifier) if isip(i)) ip_decimal = ipToDecimal(ip_address) except Exception: ip_address = None ip_decimal = None query_set = Or(Eq('id', identifier), Eq('name', identifier)) if ip_decimal is not None: query_set.addSubquery(Eq('ipAddress', str(ip_decimal))) device_brains = list( dev_cat.search(types=Device, query=query_set, limit=limit, filterPermissions=False)) limit = None if limit is None else limit - len(device_brains) if not limit: return device_brains, [] if ip_decimal is not None: # don't search interfaces for 127.x.x.x IPv4 addresses if ipToDecimal('126.255.255.255') < ip_decimal < ipToDecimal( '128.0.0.0'): ip_decimal = None # don't search interfaces for the ::1 IPv6 address elif ipToDecimal('::1') == ip_decimal: ip_decimal = None if ip_decimal is None: return [], [] net_cat = ICatalogTool(self._networks) results = net_cat.search(types=IpAddress, query=(Eq('name', ip_address)), limit=limit, filterPermissions=False) devices = [brain.getObject().device() for brain in results] return device_brains, devices @FunctionCache("findDeviceUuid", cache_miss_marker=-1, default_timeout=300) def findDeviceUuid(self, identifier, ipAddress): """ This will return the device's @type identifier: string @param identifier: The IP address or id of a device @type ipaddress: string @param ipaddress: The known ipaddress of the device """ device_brains, devices = self._findDevices(identifier, ipAddress, limit=1) if device_brains: return self.uuidFromBrain(device_brains[0]) if devices: return self.getElementUuid(devices[0]) return None def findDevice(self, identifier, ipAddress): uuid = self.findDeviceUuid(identifier, ipAddress) if uuid: return self.getElementByUuid(uuid) def getUuidsOfPath(self, node): """ Looks up all the UUIDs in the tree path of an Organizer """ uuids = set() acquisition_chain = [] for n in aq_chain(node.primaryAq()): if isinstance(n, DataRoot): acquisition_chain.pop() break acquisition_chain.append(n) if acquisition_chain: for obj in filter(None, acquisition_chain): try: uuids.add(self.getElementUuid(obj)) except TypeError: log.debug("Unable to get a uuid for %s " % obj) return filter(None, uuids)
class EventsRouter(DirectRouter): """ A JSON/ExtDirect interface to operations on events in ZEP """ def __init__(self, context, request): super(EventsRouter, self).__init__(context, request) self.zep = Zuul.getFacade('zep', context) self.catalog = ICatalogTool(context) self.manager = IGUIDManager(context.dmd) self._filterParser = _FilterParser(self.zep) def _canViewEvents(self): """ To view any events you either have to have administered roles or be a global roled user """ user = self.context.dmd.ZenUsers.getUserSettings() if not user.hasNoGlobalRoles(): return True # make sure they have view permission on something return len(user.getAllAdminRoles()) > 0 def _timeRange(self, value): try: values = [] splitter = ' TO ' if ' TO ' in value else '/' for t in value.split(splitter): values.append(int(isoToTimestamp(t)) * 1000) return values except ValueError: log.warning("Invalid timestamp: %s", value) return () def _filterInvalidUuids(self, events): """ When querying archived events we need to make sure that we do not link to devices and components that are no longer valid """ manager = self.manager for event_summary in events: occurrence = event_summary['occurrence'][0] actor = occurrence['actor'] # element if actor.get('element_uuid') and \ actor.get('element_uuid') not in manager.table: del actor['element_uuid'] # sub element if actor.get('element_sub_uuid') and \ actor.get('element_sub_uuid') not in manager.table: del actor['element_sub_uuid'] yield event_summary @serviceConnectionError @require('ZenCommon') def queryArchive(self, page=None, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None, uid=None, detailFormat=False): if not self._canViewEvents(): return DirectResponse.succeed( events=[], totalCount=0, asof=time.time() ) filter = self._buildFilter(uid, params) if exclusion_filter is not None: exclusion_filter = self._buildFilter(uid, exclusion_filter) events = self.zep.getEventSummariesFromArchive(limit=limit, offset=start, sort=self._buildSort(sort, dir), filter=filter, exclusion_filter=exclusion_filter) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo dmd = self.context.dmd # filter out the component and device UUIDs that no longer exist in our system evdata = self._filterInvalidUuids(events['events']) eventObs = [eventFormat(dmd, e) for e in evdata] return DirectResponse.succeed( events=Zuul.marshal(eventObs, keys), totalCount=events['total'], asof=time.time() ) @serviceConnectionError @require('ZenCommon') def query(self, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None, page=None, archive=False, uid=None, detailFormat=False): """ Query for events. @type limit: integer @param limit: (optional) Max index of events to retrieve (default: 0) @type start: integer @param start: (optional) Min index of events to retrieve (default: 0) @type sort: string @param sort: (optional) Key on which to sort the return results (default: 'lastTime') @type dir: string @param dir: (optional) Sort order; can be either 'ASC' or 'DESC' (default: 'DESC') @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type history: boolean @param history: (optional) True to search the event history table instead of active events (default: False) @type uid: string @param uid: (optional) Context for the query (default: None) @rtype: dictionary @return: B{Properties}: - events: ([dictionary]) List of objects representing events - totalCount: (integer) Total count of events returned - asof: (float) Current time """ if not self._canViewEvents(): return DirectResponse.succeed( events=[], totalCount=0, asof=time.time() ) if archive: return self.queryArchive(limit=limit, start=start, sort=sort, dir=dir, params=params, exclusion_filter=exclusion_filter, keys=keys, uid=uid, detailFormat=detailFormat) # special case for dmd/Devices in which case we want to show all events # by default events are not tagged with the root device classes because it would be on all events if uid == "/zport/dmd/Devices": uid = "/zport/dmd" exclude_params = self._filterParser.findExclusionParams(params) if len(exclude_params) > 0: if exclusion_filter is None: exclusion_filter = exclude_params else: exclusion_filter.update(exclude_params) filter = self._buildFilter(uid, params) if exclusion_filter is not None: exclusion_filter = self._buildFilter(uid, exclusion_filter) events = self.zep.getEventSummaries(limit=limit, offset=start, sort=self._buildSort(sort, dir), filter=filter, exclusion_filter=exclusion_filter) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo dmd = self.context.dmd eventObs = [eventFormat(dmd, e) for e in events['events']] return DirectResponse.succeed( events=Zuul.marshal(eventObs, keys), totalCount=events['total'], asof=time.time() ) @serviceConnectionError @require('ZenCommon') def queryGenerator(self, sort='lastTime', dir='desc', evids=None, excludeIds=None, params=None, archive=False, uid=None, detailFormat=False): """ Query for events. @type sort: string @param sort: (optional) Key on which to sort the return results (default: 'lastTime') @type dir: string @param dir: (optional) Sort order; can be either 'ASC' or 'DESC' (default: 'DESC') @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type archive: boolean @param archive: (optional) True to search the event archive instead of active events (default: False) @type uid: string @param uid: (optional) Context for the query (default: None) @rtype: generator @return: Generator returning events. """ if not self._canViewEvents(): return includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds) events = self.zep.getEventSummariesGenerator(filter=includeFilter, exclude=excludeFilter, sort=self._buildSort(sort, dir), archive=archive) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo for event in events: yield Zuul.marshal(eventFormat(self.context.dmd, event)) def _buildSort(self, sort='lastTime', dir='desc'): sort_list = [(sort, dir)] # Add secondary sort of last time descending if sort not in ('lastTime', 'evid'): sort_list.append(('lastTime', 'desc')) return sort_list def _buildFilter(self, uid, params, specificEventUuids=None): """ Construct a dictionary that can be converted into an EventFilter protobuf. @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) """ if params: log.debug('logging params for building filter: %s', params) if isinstance(params, basestring): params = loads(params) # params comes from the grid's filtering column - # some of these properties are normal properties on an event # while others are considered event details. Separate the # two here. params, details = self.zep.parseParameterDetails(params) filterEventUuids = [] # No specific event uuids passed in- # check for event ids from the grid parameters if specificEventUuids is None: log.debug('No specific event uuids were passed in.') # The evid's from params only ever mean anything for filtering - if # specific uuids are passed in, this filter will ignore the grid # parameters and just act on or filter using these specific event uuids. evid = params.get('evid') if evid: if not isinstance(evid, (list, tuple)): evid = [evid] filterEventUuids.extend(evid) # Specific event uuids were passed in, use those for this filter. else: log.debug('Specific event uuids passed in: %s', specificEventUuids) if not isinstance(specificEventUuids, (list, tuple)): filterEventUuids = [specificEventUuids] else: filterEventUuids = specificEventUuids log.debug('FilterEventUuids is: %s', filterEventUuids) # 'tags' comes from managed object guids. # see Zuul/security/security.py param_tags = params.get('tags') if params.get('excludeNonActionables') and not Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.context): if not param_tags: us = self.context.dmd.ZenUsers.getUserSettings() param_tags = [IGlobalIdentifier(ar.managedObject()).getGUID() for ar in us.getAllAdminRoles()] if param_tags: param_tags = [tag for tag in param_tags if Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.manager.getObject(tag))] if not param_tags: param_tags = ['dne'] # Filter everything (except "does not exist'). An empty tag list would be ignored. filter_params = { 'severity': params.get('severity'), 'status': [i for i in params.get('eventState', [])], 'event_class': filter(None, [params.get('eventClass')]), 'first_seen': params.get('firstTime') and self._timeRange(params.get('firstTime')), 'last_seen': params.get('lastTime') and self._timeRange(params.get('lastTime')), 'status_change': params.get('stateChange') and self._timeRange(params.get('stateChange')), 'uuid': filterEventUuids, 'count_range': params.get('count'), 'element_title': params.get('device'), 'element_sub_title': params.get('component'), 'event_summary': params.get('summary'), 'current_user_name': params.get('ownerid'), 'agent': params.get('agent'), 'monitor': params.get('monitor'), 'fingerprint': params.get('dedupid'), 'tags': param_tags, 'details': details, 'event_key': params.get('eventKey'), 'event_class_key': params.get('eventClassKey'), 'event_group': params.get('eventGroup'), 'message': params.get('message'), } parsed_params = self._filterParser.parseParams(params) filter_params.update(parsed_params) parsed_details = self._filterParser.parseDetails(details) if len(parsed_details) > 0: filter_params['details'].update(parsed_details) event_filter = self.zep.createEventFilter(**filter_params) log.debug('Found params for building filter, ended up building the following:') log.debug(event_filter) elif specificEventUuids: # if they passed in specific uuids but not other params event_filter = self.zep.createEventFilter( uuid=specificEventUuids ) else: log.debug('Did not get parameters, using empty filter.') event_filter = {} if uid is None: uid = self.context context = resolve_context(uid) if context and context.id not in ('Events', 'dmd'): try: # make a specific instance of tag_filter just for the context tag. context_tag_filter = { 'tag_uuids': [IGlobalIdentifier(context).getGUID()] } # if it exists, filter['tag_filter'] will be a list. just append the special # context tag filter to whatever that list is. tag_filter = event_filter.setdefault('tag_filter', []) tag_filter.append(context_tag_filter) except TypeError: if isinstance(context, EventClass): event_filter['event_class'] = [context.getDmdKey()] else: raise Exception('Unknown context %s' % context) log.debug('Final filter will be:') log.debug(event_filter) return event_filter def detail(self, evid): """ Get event details. @type evid: string @param evid: Event ID to get details @type history: boolean @param history: Deprecated @rtype: DirectResponse @return: B{Properties}: - event: ([dictionary]) List containing a dictionary representing event details """ event_summary = self.zep.getEventSummary(evid) if event_summary: eventData = Zuul.marshal(EventCompatDetailInfo(self.context.dmd, event_summary)) return DirectResponse.succeed(event=[eventData]) else: raise Exception('Could not find event %s' % evid) def manage_events(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None): if Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.context): return True if params.get('excludeNonActionables'): return Zuul.checkPermission('ZenCommon', self.context) return False def write_event_logs(self, evid=None, message=None): data = self.detail(evid).data['event'][0] uuid = data['component_uuid'] or data['device_uuid'] if uuid is None: ctx = self.context else: ctx = self.manager.getObject(uuid) return Zuul.checkPermission(ZEN_MANAGE_EVENTS, ctx) @require(write_event_logs) def write_log(self, evid=None, message=None): """ Write a message to an event's log. @type evid: string @param evid: Event ID to log to @type message: string @param message: Message to log @rtype: DirectResponse @return: Success message """ userName = getSecurityManager().getUser().getId() self.zep.addNote(uuid=evid, message=message, userName=userName) return DirectResponse.succeed() @require(ZEN_MANAGE_EVENTS) def postNote(self, uuid, note): self.zep.postNote(uuid, note) return DirectResponse.succeed() def _buildRequestFilters(self, uid, params, evids, excludeIds): """ Given common request parameters, build the inclusive and exclusive filters for event update requests. """ if uid is None and isinstance(self.context, EventClass): uid = self.context log.debug('Context while building request filters is: %s', uid) # if the request contains specific event summaries to act on, they will # be passed in as evids. Excluded event summaries are passed in under # the keyword argument 'excludeIds'. If these exist, pass them in as # parameters to be used to construct the EventFilter. includeUuids = None if isinstance(evids, (list, tuple)): log.debug('Found specific event ids, adding to params.') includeUuids = evids includeFilter = self._buildFilter(uid, params, specificEventUuids=includeUuids) # the only thing excluded in an event filter is a list of event uuids # which are passed as EventTagFilter using the OR operator. excludeFilter = None if excludeIds: excludeFilter = self._buildFilter(uid, params, specificEventUuids=excludeIds.keys()) log.debug('The exclude filter:' + str(excludeFilter)) log.debug('Finished building request filters.') return includeFilter, excludeFilter @require(ZEN_MANAGE_EVENTS) def nextEventSummaryUpdate(self, next_request): """ When performing updates from the event console, updates are performed in batches to allow the user to see the progress of event changes and cancel out of updates while they are in progress. This works by specifying a limit to one of the close, acknowledge, or reopen calls in this router. The response will contain an EventSummaryUpdateResponse, and if there are additional updates to be performed, it will contain a next_request field with all of the parameters used to update the next range of events. @type next_request: dictionary @param next_request: The next_request field from the previous updates. """ log.debug('Starting next batch of updates') status, summaryUpdateResponse = self.zep.nextEventSummaryUpdate(next_request) log.debug('Completed updates: %s', summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(ZEN_MANAGE_EVENTS) def clear_device_heartbeats(self, params, limit=None): """ @type params: dictionary @param params: Key-value pair of filters for this search. """ if isinstance(params, basestring): params = loads(params) device = params['device'] log.debug('Clearing heartbeats for device: {device}'.format(device=device)) params['eventState'] = [STATUS_NEW, STATUS_ACKNOWLEDGED] params['eventClass'] = '/Status/Heartbeat' includeFilter, excludeFilter = self._buildRequestFilters(None, params, None, None) status, summaryUpdateResponse = self.zep.closeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, ) log.debug('Done clearing heartbeats for device: {device}'.format(device=device)) log.debug(summaryUpdateResponse) audit('UI.Device.ClearHeartbeats', device=device) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) def close(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None): """ Close event(s). @type evids: [string] @param evids: (optional) List of event IDs to close (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from close (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only close if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (default: None). @rtype: DirectResponse @return: Success message """ log.debug('Issuing a close request.') includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.closeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, ) log.debug('Done issuing close request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) def acknowledge(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None): """ Acknowledge event(s). @type evids: [string] @param evids: (optional) List of event IDs to acknowledge (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from acknowledgment (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only acknowledge if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (default: None). @rtype: DirectResponse @return: Success message """ log.debug('Issuing an acknowledge request.') includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.acknowledgeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, ) log.debug('Done issuing acknowledge request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) @deprecated def unacknowledge(self, *args, **kwargs): """ Deprecated, Use reopen """ return self.reopen(*args, **kwargs) @require(manage_events) def reopen(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None): """ Reopen event(s). @type evids: [string] @param evids: (optional) List of event IDs to reopen (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from reopen (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only reopen if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (Default: None). @rtype: DirectResponse @return: Success message """ log.debug('Issuing a reopen request.') includeFilter, excludeFilter = self._buildRequestFilters(uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.reopenEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, ) log.debug('Done issuing reopen request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(ZEN_MANAGE_EVENTS) def updateEventSummaries(self, update, event_filter=None, exclusion_filter=None, limit=None): status, response = self.zep.updateEventSummaries(update, event_filter, exclusion_filter, limit) return DirectResponse.succeed(data=response) @require(ZEN_MANAGE_EVENTS) def add_event(self, summary, device, component, severity, evclasskey, evclass=None): """ Create a new event. @type summary: string @param summary: New event's summary @type device: string @param device: Device uid to use for new event @type component: string @param component: Component uid to use for new event @type severity: string @param severity: Severity of new event. Can be one of the following: Critical, Error, Warning, Info, Debug, or Clear @type evclasskey: string @param evclasskey: The Event Class Key to assign to this event @type evclass: string @param evclass: Event class for the new event @rtype: DirectResponse """ try: self.zep.create(summary, severity, device, component, eventClassKey=evclasskey, eventClass=evclass) return DirectResponse.succeed("Created event") except NoConsumersException: # This occurs if the event is queued but there are no consumers - i.e. zeneventd is not # currently running. msg = 'Queued event. Check zeneventd status on <a href="/zport/About/zenossInfo">Daemons</a>' return DirectResponse.succeed(msg, sticky=True) except PublishException, e: # This occurs if there is a failure publishing the event to the queue. log.exception("Failed creating event") return DirectResponse.exception(e, "Failed to create event")
class EventsRouter(DirectRouter): """ A JSON/ExtDirect interface to operations on events in ZEP """ def __init__(self, context, request): super(EventsRouter, self).__init__(context, request) self.zep = Zuul.getFacade('zep', context) self.catalog = ICatalogTool(context) self.manager = IGUIDManager(context.dmd) self._filterParser = _FilterParser(self.zep) def _canViewEvents(self): """ To view any events you either have to have administered roles or be a global roled user """ user = self.context.dmd.ZenUsers.getUserSettings() if not user.hasNoGlobalRoles(): return True # make sure they have view permission on something return len(user.getAllAdminRoles()) > 0 def _timeRange(self, value): try: values = [] for t in value.split('/'): values.append(int(isoToTimestamp(t)) * 1000) return values except ValueError: log.warning("Invalid timestamp: %s", value) return () def _filterInvalidUuids(self, events): """ When querying archived events we need to make sure that we do not link to devices and components that are no longer valid """ manager = self.manager for event_summary in events: occurrence = event_summary['occurrence'][0] actor = occurrence['actor'] # element if actor.get('element_uuid') and \ actor.get('element_uuid') not in manager.table: del actor['element_uuid'] # sub element if actor.get('element_sub_uuid') and \ actor.get('element_sub_uuid') not in manager.table: del actor['element_sub_uuid'] yield event_summary @serviceConnectionError @require('ZenCommon') def queryArchive(self, page=None, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None, uid=None, detailFormat=False): if not self._canViewEvents(): return DirectResponse.succeed(events=[], totalCount=0, asof=time.time()) exclude_params = self._filterParser.findExclusionParams(params) if len(exclude_params) > 0: if exclusion_filter is None: exclusion_filter = exclude_params else: exclusion_filter.update(exclude_params) filter = self._buildFilter([uid], params) if exclusion_filter is not None: exclusion_filter = self._buildFilter([uid], exclusion_filter) events = self.zep.getEventSummariesFromArchive( limit=limit, offset=start, sort=self._buildSort(sort, dir), filter=filter, exclusion_filter=exclusion_filter) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo dmd = self.context.dmd # filter out the component and device UUIDs that no longer exist in our system evdata = self._filterInvalidUuids(events['events']) eventObs = [eventFormat(dmd, e) for e in evdata] return DirectResponse.succeed(events=Zuul.marshal(eventObs, keys), totalCount=events['total'], asof=time.time()) @serviceConnectionError @require('ZenCommon') def query(self, limit=0, start=0, sort='lastTime', dir='desc', params=None, exclusion_filter=None, keys=None, page=None, archive=False, uid=None, detailFormat=False): """ Query for events. @type limit: integer @param limit: (optional) Max index of events to retrieve (default: 0) @type start: integer @param start: (optional) Min index of events to retrieve (default: 0) @type sort: string @param sort: (optional) Key on which to sort the return results (default: 'lastTime') @type dir: string @param dir: (optional) Sort order; can be either 'ASC' or 'DESC' (default: 'DESC') @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type history: boolean @param history: (optional) True to search the event history table instead of active events (default: False) @type uid: string @param uid: (optional) Context for the query (default: None) @rtype: dictionary @return: B{Properties}: - events: ([dictionary]) List of objects representing events - totalCount: (integer) Total count of events returned - asof: (float) Current time """ if not self._canViewEvents(): return DirectResponse.succeed(events=[], totalCount=0, asof=time.time()) if archive: return self.queryArchive(limit=limit, start=start, sort=sort, dir=dir, params=params, exclusion_filter=exclusion_filter, keys=keys, uid=uid, detailFormat=detailFormat) # special case for dmd/Devices in which case we want to show all events # by default events are not tagged with the root device classes because it would be on all events if uid == "/zport/dmd/Devices": uids = [ x.getPrimaryId() for x in self.context.dmd.Devices.children() ] else: uids = [uid] exclude_params = self._filterParser.findExclusionParams(params) if len(exclude_params) > 0: if exclusion_filter is None: exclusion_filter = exclude_params else: exclusion_filter.update(exclude_params) filter = self._buildFilter(uids, params) if exclusion_filter is not None: exclusion_filter = self._buildFilter(uids, exclusion_filter) events = self.zep.getEventSummaries(limit=limit, offset=start, sort=self._buildSort(sort, dir), filter=filter, exclusion_filter=exclusion_filter) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo dmd = self.context.dmd # filter out the component and device UUIDs that no longer exist in our system evdata = self._filterInvalidUuids(events['events']) eventObs = [eventFormat(dmd, e) for e in evdata] return DirectResponse.succeed(events=Zuul.marshal(eventObs, keys), totalCount=events['total'], asof=time.time()) @serviceConnectionError @require('ZenCommon') def queryGenerator(self, sort='lastTime', dir='desc', evids=None, excludeIds=None, params=None, archive=False, uid=None, detailFormat=False): """ Query for events. @type sort: string @param sort: (optional) Key on which to sort the return results (default: 'lastTime') @type dir: string @param dir: (optional) Sort order; can be either 'ASC' or 'DESC' (default: 'DESC') @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type archive: boolean @param archive: (optional) True to search the event archive instead of active events (default: False) @type uid: string @param uid: (optional) Context for the query (default: None) @rtype: generator @return: Generator returning events. """ if not self._canViewEvents(): return includeFilter, excludeFilter = self._buildRequestFilters( uid, params, evids, excludeIds) events = self.zep.getEventSummariesGenerator(filter=includeFilter, exclude=excludeFilter, sort=self._buildSort( sort, dir), archive=archive) eventFormat = EventCompatInfo if detailFormat: eventFormat = EventCompatDetailInfo for event in events: yield Zuul.marshal(eventFormat(self.context.dmd, event)) def _buildSort(self, sort='lastTime', dir='desc'): sort_list = [(sort, dir)] # Add secondary sort of last time descending if sort not in ('lastTime', 'evid'): sort_list.append(('lastTime', 'desc')) return sort_list def _buildFilter(self, uids, params, specificEventUuids=None, includeContextInUid=True): """ Construct a dictionary that can be converted into an EventFilter protobuf. @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uids: iterable(string) @param uids: (optional) Contexts for the query (default: None) """ if not uids: uids = [] elif isinstance(uids, basestring): uids = [uids] if params: log.debug('logging params for building filter: %s', params) if isinstance(params, basestring): params = loads(params) # params comes from the grid's filtering column - # some of these properties are normal properties on an event # while others are considered event details. Separate the # two here. params, details = self.zep.parseParameterDetails(params) filterEventUuids = [] # No specific event uuids passed in- # check for event ids from the grid parameters if specificEventUuids is None: log.debug('No specific event uuids were passed in.') # The evid's from params only ever mean anything for filtering - if # specific uuids are passed in, this filter will ignore the grid # parameters and just act on or filter using these specific event uuids. evid = params.get('evid') if evid: if not isinstance(evid, (list, tuple)): evid = [evid] filterEventUuids.extend(evid) # Specific event uuids were passed in, use those for this filter. else: log.debug('Specific event uuids passed in: %s', specificEventUuids) if not isinstance(specificEventUuids, (list, tuple)): filterEventUuids = [specificEventUuids] else: filterEventUuids = specificEventUuids log.debug('FilterEventUuids is: %s', filterEventUuids) # 'tags' comes from managed object guids. # see Zuul/security/security.py param_tags = params.get('tags') if params.get( 'excludeNonActionables') and not Zuul.checkPermission( ZEN_MANAGE_EVENTS, self.context): if not param_tags: us = self.context.dmd.ZenUsers.getUserSettings() param_tags = [ IGlobalIdentifier(ar.managedObject()).getGUID() for ar in us.getAllAdminRoles() ] if param_tags: param_tags = [ tag for tag in param_tags if Zuul.checkPermission( ZEN_MANAGE_EVENTS, self.manager.getObject(tag)) ] if not param_tags: param_tags = [ 'dne' ] # Filter everything (except "does not exist'). An empty tag list would be ignored. filter_params = { 'severity': params.get('severity'), 'status': [i for i in params.get('eventState', [])], 'event_class': filter(None, [params.get('eventClass')]), 'first_seen': params.get('firstTime') and self._timeRange(params.get('firstTime')), 'last_seen': params.get('lastTime') and self._timeRange(params.get('lastTime')), 'status_change': params.get('stateChange') and self._timeRange(params.get('stateChange')), 'uuid': filterEventUuids, 'count_range': params.get('count'), 'element_title': params.get('device'), 'element_sub_title': params.get('component'), 'event_summary': params.get('summary'), 'current_user_name': params.get('ownerid'), 'agent': params.get('agent'), 'monitor': params.get('monitor'), 'fingerprint': params.get('dedupid'), 'tags': param_tags, 'details': details, 'event_key': params.get('eventKey'), 'event_class_key': params.get('eventClassKey'), 'event_group': params.get('eventGroup'), 'message': params.get('message'), } parsed_params = self._filterParser.parseParams(params) filter_params.update(parsed_params) parsed_details = self._filterParser.parseDetails(details) if len(parsed_details) > 0: filter_params['details'].update(parsed_details) event_filter = self.zep.createEventFilter(**filter_params) log.debug( 'Found params for building filter, ended up building the following:' ) log.debug(event_filter) elif specificEventUuids: # if they passed in specific uuids but not other params event_filter = self.zep.createEventFilter(uuid=specificEventUuids) else: log.debug('Did not get parameters, using empty filter.') event_filter = {} if not uids and includeContextInUid: uids = [self.context] contexts = (resolve_context(uid) for uid in uids) context_uuids = [] for context in contexts: if context and context.id not in ('Events', 'dmd'): try: # make a specific instance of tag_filter just for the context tag. if not context_uuids: context_tag_filter = {'tag_uuids': context_uuids} # if it exists, filter['tag_filter'] will be a list. just append the special # context tag filter to whatever that list is. tag_filter = event_filter.setdefault('tag_filter', []) tag_filter.append(context_tag_filter) context_uuids.append(IGlobalIdentifier(context).getGUID()) except TypeError: if isinstance(context, EventClass): event_filter['event_class'] = [context.getDmdKey()] else: raise Exception('Unknown context %s' % context) log.debug('Final filter will be:') log.debug(event_filter) return event_filter def detail(self, evid): """ Get event details. @type evid: string @param evid: Event ID to get details @type history: boolean @param history: Deprecated @rtype: DirectResponse @return: B{Properties}: - event: ([dictionary]) List containing a dictionary representing event details """ event_summary = self.zep.getEventSummary(evid) if event_summary: eventData = Zuul.marshal( EventCompatDetailInfo(self.context.dmd, event_summary)) return DirectResponse.succeed(event=[eventData]) else: raise Exception('Could not find event %s' % evid) def manage_events(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None): if Zuul.checkPermission(ZEN_MANAGE_EVENTS, self.context): return True if params.get('excludeNonActionables'): return Zuul.checkPermission('ZenCommon', self.context) return False def write_event_logs(self, evid=None, message=None): data = self.detail(evid).data['event'][0] uuid = data['component_uuid'] or data['device_uuid'] if uuid is None: ctx = self.context else: ctx = self.manager.getObject(uuid) return Zuul.checkPermission(ZEN_MANAGE_EVENTS, ctx) @require(write_event_logs) def write_log(self, evid=None, message=None): """ Write a message to an event's log. @type evid: string @param evid: Event ID to log to @type message: string @param message: Message to log @rtype: DirectResponse @return: Success message """ userName = getSecurityManager().getUser().getId() self.zep.addNote(uuid=evid, message=clean_html(message), userName=userName) return DirectResponse.succeed() @require(ZEN_MANAGE_EVENTS) def postNote(self, uuid, note): self.zep.postNote(uuid, note) return DirectResponse.succeed() def _buildRequestFilters(self, uid, params, evids, excludeIds): """ Given common request parameters, build the inclusive and exclusive filters for event update requests. """ if uid is None and isinstance(self.context, EventClass): uid = self.context log.debug('Context while building request filters is: %s', uid) # if the request contains specific event summaries to act on, they will # be passed in as evids. Excluded event summaries are passed in under # the keyword argument 'excludeIds'. If these exist, pass them in as # parameters to be used to construct the EventFilter. includeUuids = None if isinstance(evids, (list, tuple)): log.debug('Found specific event ids, adding to params.') includeUuids = evids includeFilter = self._buildFilter([uid], params, specificEventUuids=includeUuids) exclude_params = self._filterParser.findExclusionParams(params) # the only thing excluded in an event filter is a list of event uuids # which are passed as EventTagFilter using the OR operator. excludeFilter = None if excludeIds or len(exclude_params) > 0: if excludeIds is None: excludeIds = {} # make sure the exclude filter doesn't include the context # otherwise all event actions wont have an effect. excludeFilter = self._buildFilter( None, exclude_params, specificEventUuids=excludeIds.keys(), includeContextInUid=False) log.debug('The exclude filter:' + str(excludeFilter)) log.debug('Finished building request filters.') return includeFilter, excludeFilter @require(ZEN_MANAGE_EVENTS) def nextEventSummaryUpdate(self, next_request): """ When performing updates from the event console, updates are performed in batches to allow the user to see the progress of event changes and cancel out of updates while they are in progress. This works by specifying a limit to one of the close, acknowledge, or reopen calls in this router. The response will contain an EventSummaryUpdateResponse, and if there are additional updates to be performed, it will contain a next_request field with all of the parameters used to update the next range of events. @type next_request: dictionary @param next_request: The next_request field from the previous updates. """ log.debug('Starting next batch of updates') status, summaryUpdateResponse = self.zep.nextEventSummaryUpdate( next_request) log.debug('Completed updates: %s', summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(ZEN_MANAGE_EVENTS) def clear_device_heartbeats(self, params, limit=None): """ @type params: dictionary @param params: Key-value pair of filters for this search. """ if isinstance(params, basestring): params = loads(params) device = params['device'] log.debug( 'Clearing heartbeats for device: {device}'.format(device=device)) params['eventState'] = [STATUS_NEW, STATUS_ACKNOWLEDGED] params['eventClass'] = '/Status/Heartbeat' includeFilter, excludeFilter = self._buildRequestFilters( None, params, None, None) status, summaryUpdateResponse = self.zep.closeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, ) log.debug('Done clearing heartbeats for device: {device}'.format( device=device)) log.debug(summaryUpdateResponse) audit('UI.Device.ClearHeartbeats', device=device) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) def close(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None): """ Close event(s). @type evids: [string] @param evids: (optional) List of event IDs to close (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from close (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only close if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (default: None). @type timeout: int @param timeout: The time (in seconds) before the underlying saved search times out. @rtype: DirectResponse @return: Success message """ log.debug('Issuing a close request.') includeFilter, excludeFilter = self._buildRequestFilters( uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.closeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, timeout=timeout, ) log.debug('Done issuing close request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) def acknowledge(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None): """ Acknowledge event(s). @type evids: [string] @param evids: (optional) List of event IDs to acknowledge (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from acknowledgment (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only acknowledge if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (default: None). @type timeout: int @param timeout: The time (in seconds) before the underlying saved search times out. @rtype: DirectResponse @return: Success message """ log.debug('Issuing an acknowledge request.') includeFilter, excludeFilter = self._buildRequestFilters( uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.acknowledgeEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, timeout=timeout, ) log.debug('Done issuing acknowledge request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(manage_events) @deprecated def unacknowledge(self, *args, **kwargs): """ Deprecated, Use reopen """ return self.reopen(*args, **kwargs) @require(manage_events) def reopen(self, evids=None, excludeIds=None, params=None, uid=None, asof=None, limit=None, timeout=None): """ Reopen event(s). @type evids: [string] @param evids: (optional) List of event IDs to reopen (default: None) @type excludeIds: [string] @param excludeIds: (optional) List of event IDs to exclude from reopen (default: None) @type params: dictionary @param params: (optional) Key-value pair of filters for this search. (default: None) @type uid: string @param uid: (optional) Context for the query (default: None) @type asof: float @param asof: (optional) Only reopen if there has been no state change since this time (default: None) @type limit: The maximum number of events to update in this batch. @param limit: (optional) Maximum number of events to update (Default: None). @type timeout: int @param timeout: The time (in seconds) before the underlying saved search times out. @rtype: DirectResponse @return: Success message """ log.debug('Issuing a reopen request.') includeFilter, excludeFilter = self._buildRequestFilters( uid, params, evids, excludeIds) status, summaryUpdateResponse = self.zep.reopenEventSummaries( eventFilter=includeFilter, exclusionFilter=excludeFilter, limit=limit, timeout=timeout, ) log.debug('Done issuing reopen request.') log.debug(summaryUpdateResponse) return DirectResponse.succeed(data=summaryUpdateResponse) @require(ZEN_MANAGE_EVENTS) def updateEventSummaries(self, update, event_filter=None, exclusion_filter=None, limit=None, timeout=None): status, response = self.zep.updateEventSummaries(update, event_filter, exclusion_filter, limit, timeout=timeout) return DirectResponse.succeed(data=response) @require(ZEN_MANAGE_EVENTS) def add_event(self, summary, device, component, severity, evclasskey, evclass=None): """ Create a new event. @type summary: string @param summary: New event's summary @type device: string @param device: Device id to use for new event @type component: string @param component: Component uid to use for new event @type severity: string @param severity: Severity of new event. Can be one of the following: Critical, Error, Warning, Info, Debug, or Clear @type evclasskey: string @param evclasskey: The Event Class Key to assign to this event @type evclass: string @param evclass: Event class for the new event @rtype: DirectResponse """ device = device.strip() # ZEN-2479: support entries like "localhost " try: self.zep.create(summary, severity, device, component, eventClassKey=evclasskey, eventClass=evclass) return DirectResponse.succeed("Created event") except NoConsumersException: # This occurs if the event is queued but there are no consumers - i.e. zeneventd is not # currently running. msg = 'Queued event. Check zeneventd status on <a href="/zport/dmd/daemons">Services</a>' return DirectResponse.succeed(msg, sticky=True) except PublishException, e: # This occurs if there is a failure publishing the event to the queue. log.exception("Failed creating event") return DirectResponse.exception(e, "Failed to create event")
class EventCompatInfo(object): """ Takes a zep event and maps it to the format that the UI expects """ implements(IMarshallable) def __init__(self, dmd, event_summary): self._dmd = dmd self._event_summary = event_summary self._eventOccurrence = event_summary['occurrence'][0] self._eventActor = self._eventOccurrence['actor'] self._eventDetails = self._findDetails(self._eventOccurrence) self._catalog = IModelCatalogTool(dmd) self._manager = IGUIDManager(dmd) @property def id(self): return self._event_summary['uuid'] @property def evid(self): return self.id @property def dedupid(self): return self._eventOccurrence.get('fingerprint') @property def eventState(self): return EventStatus.getPrettyName(self._event_summary['status']) @property def severity(self): return self._eventOccurrence['severity'] @property def component(self): return { 'text': self._eventActor.get('element_sub_title'), 'uid': self._getPathFromUuid(self._eventActor.get('element_sub_uuid')), 'url': self._uuidUrl(self._eventActor.get('element_sub_uuid')), 'uuid': self._eventActor.get('element_sub_uuid') } @property def eventClass(self): eventClass = self._eventOccurrence['event_class'] return {"text": eventClass, "uid": "/zport/dmd/Events%s" % eventClass} @property def summary(self): return self._eventOccurrence['summary'] @property def firstTime(self): return self._event_summary['first_seen_time'] / 1000.0 @property def lastTime(self): return self._event_summary['last_seen_time'] / 1000.0 @property def count(self): return self._event_summary['count'] @property def stateChange(self): return self._event_summary['status_change_time'] / 1000.0 @property def eventClassKey(self): return self._eventOccurrence.get('event_class_key') @property def eventGroup(self): return self._eventOccurrence.get('event_group') @property def eventKey(self): return self._eventOccurrence.get('event_key') @property def agent(self): return self._eventOccurrence.get('agent') @property def monitor(self): return self._eventOccurrence.get('monitor') @property def ownerid(self): return self._event_summary.get('current_user_name') @property def facility(self): return self._eventOccurrence.get('syslog_facility') @property def priority(self): return self._eventOccurrence.get('syslog_priority') @property def eventClassMapping(self): return self._lookupEventClassMapping( self._eventOccurrence.get('event_class_mapping_uuid')) @property def clearid(self): return self._event_summary.get('cleared_by_event_uuid') @property def ntevid(self): return self._eventOccurrence.get('nt_event_code') @property def ipAddress(self): return self._eventDetails.get('zenoss.device.ip_address', '') @property def message(self): return self._eventOccurrence.get('message', '') @property def Location(self): return self._lookupDetailPath( '/zport/dmd/Locations', self._eventDetails.get(EventProxy.DEVICE_LOCATION_DETAIL_KEY)) @property def DeviceGroups(self): return self._lookupDetailPath( '/zport/dmd/Groups', self._eventDetails.get(EventProxy.DEVICE_GROUPS_DETAIL_KEY)) @property def Systems(self): return self._lookupDetailPath( '/zport/dmd/Systems', self._eventDetails.get(EventProxy.DEVICE_SYSTEMS_DETAIL_KEY)) @property def DeviceClass(self): return self._lookupDetailPath( '/zport/dmd/Devices', self._eventDetails.get(EventProxy.DEVICE_CLASS_DETAIL_KEY)) @property def device(self): device_url = self._get_device_url(self._eventDetails) if device_url is None: return dict(text=self._eventActor.get('element_title'), uid=self._getPathFromUuid( self._eventActor.get('element_uuid')), url=self._uuidUrl( self._eventActor.get('element_uuid')), uuid=self._eventActor.get('element_uuid')) else: return dict(text=self._eventActor.get('element_title'), url=device_url) @property def prodState(self): prodState = self._singleDetail( self._eventDetails.get('zenoss.device.production_state')) if prodState is not None: return self._dmd.convertProdState(prodState) @property def DevicePriority(self): DevicePriority = self._singleDetail( self._eventDetails.get('zenoss.device.priority')) if DevicePriority is not None: return self._dmd.convertPriority(DevicePriority) @property def details(self): return self._eventDetails def __getattr__(self, name): if self._eventDetails.get(name): return self._eventDetails.get(name) raise AttributeError(name) def _uuidUrl(self, uuid): if uuid: return '/zport/dmd/goto?guid=%s' % uuid def _get_device_url(self, eventDetails): url_and_path = [ self._singleDetail(eventDetails.get(k)) for k in ('zenoss.device.url', 'zenoss.device.path') ] if len(url_and_path) != 2: return None url, path = url_and_path try: self._dmd.findChild(path) except Exception: return None return url def _singleDetail(self, value): """ A convenience method for fetching a single detail from a property which correlates to a repeated field on the protobuf. """ if isinstance(value, (tuple, list, set)) and value: return value[0] def _findDetails(self, event): """ Event details are created as a dictionary like the following: detail = { 'name': 'zenoss.foo.bar', 'value': 'baz' } This method maps these detail items to a flat dictionary to facilitate looking up details by key easier. @rtype dict """ details = {} if 'details' in event: for d in event['details']: details[d['name']] = d.get('value', ()) return details def _lookupDetailPath(self, prefix, values): if not values: return () paths = [] for value in values: paths.append({'uid': prefix + value, 'name': value}) return paths def _getPathFromUuid(self, uuid): if uuid: path = self._manager.getPath(uuid) if path: return urllib.unquote(path) def _lookupEventClassMapping(self, mappingUuid): if not mappingUuid: return "" return { 'uuid': mappingUuid, 'name': self._getNameFromUuid(mappingUuid) } def _getNameFromUuid(self, uuid): """ Given a uuid this returns the objects name from the catalog, it does not wake the object up """ if uuid: path = self._getPathFromUuid(uuid) if path: brain = self._catalog.getBrain(path, fields="name") if brain: return brain.name
class EventCompatInfo(object): """ Takes a zep event and maps it to the format that the UI expects """ implements(IMarshallable) def __init__(self, dmd, event_summary): self._dmd = dmd self._event_summary = event_summary self._eventOccurrence = event_summary['occurrence'][0] self._eventActor = self._eventOccurrence['actor'] self._eventDetails = self._findDetails(self._eventOccurrence) self._catalog = ICatalogTool(dmd) self._manager = IGUIDManager(dmd) @property def id(self): return self._event_summary['uuid'] @property def evid(self): return self.id @property def dedupid(self): return self._eventOccurrence.get('fingerprint') @property def eventState(self): return EventStatus.getPrettyName(self._event_summary['status']) @property def severity(self): return self._eventOccurrence['severity'] @property def component(self): return { 'text': self._eventActor.get('element_sub_title'), 'uid': self._getPathFromUuid(self._eventActor.get('element_sub_uuid')), 'url' : self._uuidUrl(self._eventActor.get('element_sub_uuid')), 'uuid' : self._eventActor.get('element_sub_uuid') } @property def eventClass(self): eventClass = self._eventOccurrence['event_class'] return {"text": eventClass, "uid": "/zport/dmd/Events%s" % eventClass} @property def summary(self): return self._eventOccurrence['summary'] @property def firstTime(self): return isoDateTimeFromMilli(self._event_summary['first_seen_time']) @property def lastTime(self): return isoDateTimeFromMilli(self._event_summary['last_seen_time']) @property def count(self): return self._event_summary['count'] @property def stateChange(self): return isoDateTimeFromMilli(self._event_summary['status_change_time']) @property def eventClassKey(self): return self._eventOccurrence.get('event_class_key') @property def eventGroup(self): return self._eventOccurrence.get('event_group') @property def eventKey(self): return self._eventOccurrence.get('event_key') @property def agent(self): return self._eventOccurrence.get('agent') @property def monitor(self): return self._eventOccurrence.get('monitor') @property def ownerid(self): return self._event_summary.get('current_user_name') @property def facility(self): return self._eventOccurrence.get('syslog_facility') @property def priority(self): return self._eventOccurrence.get('syslog_priority') @property def eventClassMapping(self): return self._lookupEventClassMapping(self._eventOccurrence.get('event_class_mapping_uuid')) @property def clearid(self): return self._event_summary.get('cleared_by_event_uuid') @property def ntevid(self): return self._eventOccurrence.get('nt_event_code') @property def ipAddress(self): return self._eventDetails.get('zenoss.device.ip_address', '') @property def message(self): return self._eventOccurrence.get('message', '') @property def Location(self): return self._lookupDetailPath('/zport/dmd/Locations', self._eventDetails.get(EventProxy.DEVICE_LOCATION_DETAIL_KEY)) @property def DeviceGroups(self): return self._lookupDetailPath('/zport/dmd/Groups', self._eventDetails.get(EventProxy.DEVICE_GROUPS_DETAIL_KEY)) @property def Systems(self): return self._lookupDetailPath('/zport/dmd/Systems', self._eventDetails.get(EventProxy.DEVICE_SYSTEMS_DETAIL_KEY)) @property def DeviceClass(self): return self._lookupDetailPath('/zport/dmd/Devices', self._eventDetails.get(EventProxy.DEVICE_CLASS_DETAIL_KEY)) @property def device(self): device_url = self._get_device_url(self._eventDetails) if device_url is None: return dict(text=self._eventActor.get('element_title'), uid=self._getPathFromUuid(self._eventActor.get('element_uuid')), url=self._uuidUrl(self._eventActor.get('element_uuid')), uuid=self._eventActor.get('element_uuid')) else: return dict(text=self._eventActor.get('element_title'), url=device_url) @property def prodState(self): prodState = self._singleDetail(self._eventDetails.get('zenoss.device.production_state')) if prodState is not None: return self._dmd.convertProdState(prodState) @property def DevicePriority(self): DevicePriority = self._singleDetail(self._eventDetails.get('zenoss.device.priority')) if DevicePriority is not None: return self._dmd.convertPriority(DevicePriority) @property def details(self): return self._eventDetails def __getattr__(self, name): if self._eventDetails.get(name): return self._eventDetails.get(name) raise AttributeError(name) def _uuidUrl(self, uuid): if uuid: return '/zport/dmd/goto?guid=%s' % uuid def _get_device_url(self, eventDetails): url_and_path = [self._singleDetail(eventDetails.get(k)) for k in 'zenoss.device.url', 'zenoss.device.path'] if len(url_and_path) != 2: return None url, path = url_and_path try: self._dmd.findChild(path) except: return None return url def _singleDetail(self, value): """ A convenience method for fetching a single detail from a property which correlates to a repeated field on the protobuf. """ if isinstance(value, (tuple, list, set)) and value: return value[0] def _findDetails(self, event): """ Event details are created as a dictionary like the following: detail = { 'name': 'zenoss.foo.bar', 'value': 'baz' } This method maps these detail items to a flat dictionary to facilitate looking up details by key easier. @rtype dict """ details = {} if 'details' in event: for d in event['details']: details[d['name']] = d.get('value', ()) return details def _lookupDetailPath(self, prefix, values): if not values: return () paths = [] for value in values: paths.append({'uid': prefix + value, 'name': value}) return paths def _getPathFromUuid(self, uuid): if uuid: path = self._manager.getPath(uuid) if path: return urllib.unquote(path) def _lookupEventClassMapping(self, mappingUuid): if not mappingUuid: return "" return {'uuid': mappingUuid, 'name': self._getNameFromUuid(mappingUuid)} def _getNameFromUuid(self, uuid): """ Given a uuid this returns the objects name from the catalog, it does not wake the object up """ if uuid: path = self._getPathFromUuid(uuid) if path: brain = self._catalog.getBrain(path) if brain: return brain.name
class ZepFacade(ZuulFacade): implements(IZepFacade) AND = AND OR = OR DEFAULT_SORT_MAP = { 'eventstate': { 'field': EventSort.STATUS }, 'eventstatetext':{ 'field': EventSort.STATUS }, 'severity': { 'field': EventSort.SEVERITY }, 'firsttime': { 'field': EventSort.FIRST_SEEN }, 'lasttime': { 'field': EventSort.LAST_SEEN }, 'eventclass': { 'field': EventSort.EVENT_CLASS }, 'device': { 'field': EventSort.ELEMENT_TITLE }, 'component': { 'field': EventSort.ELEMENT_SUB_TITLE }, 'count': { 'field': EventSort.COUNT }, 'summary': { 'field': EventSort.EVENT_SUMMARY }, 'ownerid': { 'field': EventSort.CURRENT_USER_NAME }, 'agent': { 'field': EventSort.AGENT }, 'monitor': { 'field': EventSort.MONITOR }, 'eventkey': { 'field': EventSort.EVENT_KEY }, 'evid': { 'field': EventSort.UUID }, 'statechange': { 'field': EventSort.STATUS_CHANGE }, 'dedupid': { 'field': EventSort.FINGERPRINT }, 'eventclasskey': { 'field': EventSort.EVENT_CLASS_KEY }, 'eventgroup': { 'field': EventSort.EVENT_GROUP }, } SORT_DIRECTIONAL_MAP = { 'asc' : EventSort.ASCENDING, 'desc' : EventSort.DESCENDING, } ZENOSS_DETAIL_OLD_TO_NEW_MAPPING = { 'prodState': EventProxy.PRODUCTION_STATE_DETAIL_KEY, 'DevicePriority': EventProxy.DEVICE_PRIORITY_DETAIL_KEY, 'ipAddress': EventProxy.DEVICE_IP_ADDRESS_DETAIL_KEY, 'Location': EventProxy.DEVICE_LOCATION_DETAIL_KEY, 'DeviceGroups': EventProxy.DEVICE_GROUPS_DETAIL_KEY, 'Systems': EventProxy.DEVICE_SYSTEMS_DETAIL_KEY, 'DeviceClass': EventProxy.DEVICE_CLASS_DETAIL_KEY, } ZENOSS_DETAIL_NEW_TO_OLD_MAPPING = dict((new, old) for old, new in ZENOSS_DETAIL_OLD_TO_NEW_MAPPING.iteritems()) JAVA_MIN_INTEGER = -2147483648 # Values that zep uses to index details with no value ZENOSS_NULL_NUMERIC_DETAIL_INDEX_VALUE = JAVA_MIN_INTEGER ZENOSS_NULL_TEXT_DETAIL_INDEX_VALUE = '\x07' SEVERITIES_BATCH_SIZE = 400 COUNT_REGEX = re.compile(r'^(?P<from>\d+)?:?(?P<to>\d+)?$') def __init__(self, context): super(ZepFacade, self).__init__(context) config = getGlobalConfiguration() zep_url = config.get('zep-uri', 'http://localhost:8084') schema = getUtility(IQueueSchema) self.client = ZepServiceClient(zep_url, schema) self.configClient = ZepConfigClient(zep_url, schema) self.heartbeatClient = ZepHeartbeatClient(zep_url, schema) self._guidManager = IGUIDManager(context.dmd) self.statsClient = ZepStatsClient(zep_url, schema) def _create_identifier_filter(self, value): if not isinstance(value, (tuple, list, set)): value = (value,) return map(lambda s:str(s).strip(), value) def _createFullTextSearch(self, parameter): if not hasattr(parameter, '__iter__'): parameter = (parameter,) return map(lambda s:str(s).strip(), parameter) def createEventFilter(self, severity=(), status=(), event_class=(), first_seen=None, last_seen=None, status_change=None, update_time=None, count_range=None, element_identifier=(), element_title=(), element_sub_identifier=(), element_sub_title=(), uuid=(), event_summary=None, tags=(), fingerprint=(), agent=(), monitor=(), event_key=(), current_user_name=(), subfilter=(), operator=None, details=None, event_class_key=(), event_group=(), message=()): """ Creates a filter based on passed arguments. Caller is responsible for handling the include-zero-items case. For example, passing an empty uuid tuple won't filter by uuid so includes everything. """ filter = {} if uuid: filter['uuid'] = uuid if event_summary: filter['event_summary'] = self._createFullTextSearch(event_summary) if event_class: filter['event_class'] = event_class if status: filter['status'] = status if severity: filter['severity'] = severity if first_seen: filter['first_seen'] = self._timeRange(first_seen) if last_seen: filter['last_seen'] = self._timeRange(last_seen) if status_change: filter['status_change'] = self._timeRange(status_change) if update_time: filter['update_time'] = self._timeRange(update_time) # These tags come from params, which means for some reason someone is filtering manually on a tag. if tags: filter['tag_filter'] = {'tag_uuids': tags} if count_range: if not isinstance(count_range, (tuple, list)): try: count = int(count_range) count_range = (count, count) except ValueError: match = ZepFacade.COUNT_REGEX.match(count_range) if not match: raise ValueError('Invalid range: %s' % (count_range)) count_range = (match.group('from'), match.group('to')) filter['count_range'] = {} count_from, count_to = count_range if count_from is not None: filter['count_range']['from'] = int(count_from) if count_to is not None: filter['count_range']['to'] = int(count_to) if element_identifier: filter['element_identifier'] = self._create_identifier_filter(element_identifier) if element_title: filter['element_title'] = self._create_identifier_filter(element_title) if element_sub_identifier: filter['element_sub_identifier'] = self._create_identifier_filter(element_sub_identifier) if element_sub_title: filter['element_sub_title'] = self._create_identifier_filter(element_sub_title) if fingerprint: filter['fingerprint'] = fingerprint if agent: filter['agent'] = agent if monitor: filter['monitor'] = monitor if event_key: filter['event_key'] = event_key if current_user_name: filter['current_user_name'] = current_user_name if subfilter: filter['subfilter'] = subfilter if details: filter['details'] = self._createEventDetailFilter(details) if event_class_key: filter['event_class_key'] = event_class_key if event_group: filter['event_group'] = event_group if message: filter['message'] = self._createFullTextSearch(message) # Everything's repeated on the protobuf, so listify result = dict((k, listify(v)) for k,v in filter.iteritems()) if operator: result['operator'] = operator return result def _createEventDetailFilter(self, details): """ @param details: All details present in this filter request. Example: { 'zenoss.device.production_state' = 4, 'zenoss.device.priority' : 2 } @type details: dict """ detailFilterItems = [] for key, val in details.iteritems(): detailFilterItems.append({ 'key': key, 'value': val, }) log.debug('Final detail filter: %r' % detailFilterItems) return detailFilterItems def _timeRange(self, timeRange): d = { 'start_time' : timeRange[0], } if len(timeRange) == 2: d['end_time'] = timeRange[1] return d def _getEventSort(self, sortParam): eventSort = {} if isinstance(sortParam, (list, tuple)): field, direction = sortParam eventSort['direction'] = self.SORT_DIRECTIONAL_MAP[direction.lower()] else: field = sortParam eventSort.update(getDetailsInfo().getSortMap()[field.lower()]) return from_dict(EventSort, eventSort) def _getUserUuid(self, userName): # We have to call _getOb instead of getUserSettings here because the # latter will create a new user settings object even if the user is # not known. try: user = self._dmd.ZenUsers._getOb(userName) return IGlobalIdentifier(user).getGUID() except AttributeError: raise Exception('Could not find user "%s"' % userName) def _findUserInfo(self): userName = getSecurityManager().getUser().getId() return self._getUserUuid(userName), userName def addNote(self, uuid, message, userName, userUuid=None): if userName and not userUuid: userUuid = self._getUserUuid(userName) self.client.addNote(uuid, message, userUuid, userName) def addNoteBulkAsync(self, uuids, message, userName, userUuid=None): if userName and not userUuid: userUuid = self._getUserUuid(userName) self.client.addNoteBulkAsync(uuids, message, userUuid, userName) def postNote(self, uuid, note): self.client.postNote(uuid, from_dict(EventNote, note)) def _getEventSummaries(self, source, offset, limit=1000): response, content = source(offset=offset, limit=limit) return { 'total' : content.total, 'limit' : content.limit, 'next_offset' : content.next_offset if content.HasField('next_offset') else None, 'events' : (to_dict(event) for event in content.events), } def getEventSummariesFromArchive(self, offset, limit=1000, sort=None, filter=None, exclusion_filter=None): return self.getEventSummaries(offset, limit, sort, filter, client_fn=self.client.getEventSummariesFromArchive, exclusion_filter=exclusion_filter) def getEventSummaries(self, offset, limit=1000, sort=None, filter=None, exclusion_filter=None, client_fn=None, use_permissions=False): if client_fn is None: client_fn = self.client.getEventSummaries if filter is not None and isinstance(filter,dict): filter = from_dict(EventFilter, filter) if exclusion_filter is not None and isinstance(exclusion_filter, dict): exclusion_filter['operator'] = 1 # Set operator to OR exclusion_filter = from_dict(EventFilter, exclusion_filter) if sort is not None: sort = tuple(self._getEventSort(s) for s in safeTuple(sort)) result = None if use_permissions: user = getSecurityManager().getUser() userSettings = self._dmd.ZenUsers._getOb(user.getId()) hasGlobalRoles = not userSettings.hasNoGlobalRoles() if not hasGlobalRoles: # get guids for the objects user has permission to access # and add to filter guids = userSettings.getAllAdminGuids(returnChildrenForRootObj=True) if guids: if filter is None: filter = EventFilter() tf = filter.tag_filter.add() tf.tag_uuids.extend(guids) else: # no permission to see events, return 0 result = { 'total' : 0, 'events' : [], } if not result: result = self._getEventSummaries(source=partial(client_fn, filter=filter, exclusion_filter=exclusion_filter, sort=sort ), offset=offset, limit=limit ) return result def getEventSummariesGenerator(self, filter=None, exclude=None, sort=None, archive=False, timeout=None): if isinstance(exclude, dict): exclude = from_dict(EventFilter, exclude) if isinstance(filter, dict): filter = from_dict(EventFilter, filter) if sort is not None: sort = tuple(self._getEventSort(s) for s in safeTuple(sort)) searchid = self.client.createSavedSearch(event_filter=filter, exclusion_filter=exclude, sort=sort, archive=archive, timeout=timeout) log.debug("created saved search %s", searchid) eventSearchFn = partial(self.client.savedSearch, searchid, archive=archive) offset = 0 limit = 1000 try: while offset is not None: result = self._getEventSummaries(eventSearchFn, offset, limit) for evt in result['events']: yield evt offset = result['next_offset'] finally: try: log.debug("closing saved search %s", searchid) self.client.deleteSavedSearch(searchid, archive=archive) except Exception as e: log.debug("error closing saved search %s (%s) - %s", searchid, type(e), e) def getEventSummary(self, uuid): response, content = self.client.getEventSummary(uuid) return to_dict(content) def nextEventSummaryUpdate(self, next_request): status, response = self.client.nextEventSummaryUpdate(from_dict(EventSummaryUpdateRequest, next_request)) return status, to_dict(response) def _processArgs(self, eventFilter, exclusionFilter, userName): if eventFilter: eventFilter = from_dict(EventFilter, eventFilter) if exclusionFilter: exclusionFilter = from_dict(EventFilter, exclusionFilter) if not userName: userUuid, userName = self._findUserInfo() else: userUuid = self._getUserUuid(userName) if eventFilter is None and exclusionFilter is None: raise NoFiltersException("Cannot modify event summaries without at least one filter specified.") return {'eventFilter': eventFilter, 'exclusionFilter': exclusionFilter, 'userName': userName, 'userUuid': userUuid} def closeEventSummaries(self, eventFilter=None, exclusionFilter=None, limit=None, userName=None, timeout=None): arguments = self._processArgs(eventFilter, exclusionFilter, userName) eventFilter = arguments.get('eventFilter') exclusionFilter = arguments.get('exclusionFilter') userName = arguments.get('userName') userUuid = arguments.get('userUuid') status, response = self.client.closeEventSummaries( userUuid, userName, eventFilter, exclusionFilter, limit, timeout=timeout) return status, to_dict(response) def acknowledgeEventSummaries(self, eventFilter=None, exclusionFilter=None, limit=None, userName=None, timeout=None): arguments = self._processArgs(eventFilter, exclusionFilter, userName) eventFilter = arguments.get('eventFilter') exclusionFilter = arguments.get('exclusionFilter') userName = arguments.get('userName') userUuid = arguments.get('userUuid') status, response = self.client.acknowledgeEventSummaries(userUuid, userName, eventFilter, exclusionFilter, limit, timeout=timeout) return status, to_dict(response) def reopenEventSummaries(self, eventFilter=None, exclusionFilter=None, limit=None, userName=None, timeout=None): arguments = self._processArgs(eventFilter, exclusionFilter, userName) eventFilter = arguments.get('eventFilter') exclusionFilter = arguments.get('exclusionFilter') userName = arguments.get('userName') userUuid = arguments.get('userUuid') status, response = self.client.reopenEventSummaries( userUuid, userName, eventFilter, exclusionFilter, limit, timeout=timeout) return status, to_dict(response) def updateEventSummaries(self, update, eventFilter=None, exclusionFilter=None, limit=None, timeout=None): update_pb = from_dict(EventSummaryUpdate, update) event_filter_pb = None if (eventFilter is None) else from_dict(EventFilter, eventFilter) exclusion_filter_pb = None if (exclusionFilter is None) else from_dict(EventFilter, exclusionFilter) status, response = self.client.updateEventSummaries(update_pb, event_filter_pb, exclusion_filter_pb, limit=limit, timeout=timeout) return status, to_dict(response) def getConfig(self): # the config client doesn't return a ZepConfig. It merges the ZepConfig # with some other properties to create a config structure. config = self.configClient.getConfig() return config def setConfigValues(self, values): """ @type values: Dictionary @param values: Key Value pairs of config values """ zepConfigProtobuf = from_dict(ZepConfig, values) self.configClient.setConfigValues(zepConfigProtobuf) def setConfigValue(self, name, value): self.configClient.setConfigValue(name, value) def removeConfigValue(self, name): self.configClient.removeConfigValue(name) def _getTopLevelOrganizerUuids(self, tagUuid): """ Returns a list of child UUIDs if the specified tagUuid is a top-level organizer. Otherwise returns None. This is needed because several operations in ZEP are performed on UUIDs tagged in the events, however the top-level organizers are not tagged on events as an optimization. @type tagUuid: string @param tagUuid: UUID of potential top-level organizer """ obj = self._guidManager.getObject(tagUuid) uuids = None if obj and obj.getDmdKey() == '/': uuids = [IGlobalIdentifier(n).getGUID() for n in obj.children()] return uuids def getEventSeveritiesByUuid(self, tagUuid, severities=(), status=()): """ returns a dict of severities for the element tagUuid """ uuids = [ tagUuid ] return self.getEventSeveritiesByUuids(uuids , severities=severities, status=status)[tagUuid] def getEventSeveritiesByUuids(self, tagUuids, severities=(), status=()): """ returns a dict whose keys are each uuid in tagUuids and values the dict of severities per uuid """ uuids = [] requested_uuids = {} for uuid in tagUuids: children_uuids = self._getTopLevelOrganizerUuids(uuid) if children_uuids: requested_uuids[uuid] = children_uuids uuids.extend(children_uuids) else: requested_uuids[uuid] = [ uuid ] uuids.append(uuid) uuids = list(set(uuids)) severities = self.getEventSeverities(uuids, severities=severities, status=status) severities_to_return = {} for requested_uuid in requested_uuids.keys(): children_uuids = requested_uuids[requested_uuid] sevmap = {} for uuid in children_uuids: sevs = severities[uuid] for sev, counts in sevs.iteritems(): counts_dict = sevmap.get(sev) if counts_dict: # Condense counts of child organizers into a flattened out count counts_dict['count'] += counts['count'] counts_dict['acknowledged_count'] += counts['acknowledged_count'] else: sevmap[sev] = counts severities_to_return[requested_uuid] = sevmap return severities_to_return def _createSeveritiesDict(self, eventTagSeverities): severities = {} for tag in eventTagSeverities: severities[tag.tag_uuid] = {} for sev in tag.severities: severities[tag.tag_uuid][sev.severity] = dict(count=sev.count, acknowledged_count=sev.acknowledged_count) for sev in EventSeverity.numbers: if not sev in severities[tag.tag_uuid]: severities[tag.tag_uuid][sev] = dict(count=0, acknowledged_count=0) return severities def getEventSeverities(self, tagUuids, severities=(), status=(), eventClass=()): """ Get a dictionary of the event severity counts for each UUID. @param tagUuids: A sequence of element UUIDs @param severities: A sequence of severities to include. Default is CRITICAL/ERROR/WARNING. @type severities: Sequence of severities. @param status: A sequence of event statuses to include. Default is NEW/ACKNOWLEDGED. @type status: Sequence of event severities. @rtype: dict @return: A dictionary of UUID -> { C{EventSeverity} -> { count, acknowledged_count } } """ objects_severities = {} tagUuids = list(tagUuids) tagUuids.sort() number_of_uuids = len(tagUuids) batch_size = ZepFacade.SEVERITIES_BATCH_SIZE number_of_batches = number_of_uuids / batch_size if (number_of_uuids % batch_size) > 0: number_of_batches = number_of_batches + 1 if number_of_batches > 1: log.info("Retrieving severities for {0} uuids.".format(number_of_uuids)) for batch in range(0, number_of_batches): start = batch*batch_size end = (start + batch_size) if end > number_of_uuids: end = number_of_uuids uuids = tagUuids[start:end] eventTagSeverities = self._getEventTagSeverities(severity=severities, status=status, tags=uuids, eventClass=eventClass) objects_severities.update(self._createSeveritiesDict(eventTagSeverities)) return objects_severities def getWorstSeverityByUuid(self, tagUuid, default=SEVERITY_CLEAR, ignore=()): return self.getWorstSeverity([tagUuid], default=default, ignore=ignore)[tagUuid] def getWorstSeverity(self, tagUuids, default=SEVERITY_CLEAR, ignore=()): """ Get a dictionary of the worst event severity for each UUID. @param tagUuids: A sequence of element UUIDs @param default: The default severity to use if there are no results or if a severity is ignored @type default: An C{EventSeverity} enum value @param ignore: Severities to not include as worst, use the default instead. @type ignore: A list of C{EventSeverity} enum values @rtype: dict @return: A dictionary of UUID -> C{EventSeverity} """ if not tagUuids: return {} # Prepopulate the list with defaults severities = dict.fromkeys(tagUuids, default) eventTagSeverities = self._getEventTagSeverities(tags=tagUuids) for tag in eventTagSeverities: for sev in tag.severities: if sev.severity not in ignore: severities[tag.tag_uuid] = max(sev.severity, severities.get(tag.tag_uuid, 0)) return severities def getSeverityName(self, severity): return EventSeverity.getPrettyName(severity) def createEventMapping(self, evdata, eventClassId): """ Associates event(s) with an event class. """ evmap = None evclass = self._dmd.Events.getOrganizer(eventClassId) numCreated = 0 numNotUnknown = 0 numNoKey = 0 for data in evdata: evclasskey = data.get('eventClassKey') if data.get('eventClass'): curevclass = data.get('eventClass')['text'] else: curevclass = Unknown example = data.get('message') if curevclass != Unknown: numNotUnknown += 1 continue if not evclasskey: numNoKey += 1 continue evmap = evclass.createInstance(evclasskey) evmap.eventClassKey = evclasskey evmap.example = example evmap.index_object() numCreated += 1 # message msg = '' if numNotUnknown: msg += ((msg and ' ') + '%s event%s %s not of the class Unknown.' % ( numNotUnknown, (numNotUnknown != 1 and 's') or '', (numNotUnknown != 1 and 'are') or 'is')) if numNoKey: msg += ((msg and ' ') + '%s event%s %s not have an event class key.' % ( numNoKey, (numNoKey != 1 and 's') or '', (numNoKey != 1 and 'do') or 'does')) msg += (msg and ' ') + 'Created %s event mapping%s.' % ( numCreated, (numCreated != 1 and 's') or '') # redirect url = None if len(evdata) == 1 and evmap: url = evmap.getPrimaryId() elif evclass and evmap: url = evclass.getPrimaryId() return msg, url def _getEventTagSeverities(self, eventClass=(), severity=(), status=(), tags=(), deviceOnly=False): if not severity: severity = (SEVERITY_CRITICAL, SEVERITY_ERROR, SEVERITY_WARNING) if not status: status = (STATUS_NEW, STATUS_ACKNOWLEDGED) if deviceOnly: eventFilter = self.createEventFilter( severity=severity, status=status, event_class=eventClass, tags=tags, element_sub_identifier=[""], ) else: eventFilter = self.createEventFilter( severity=severity, status=status, event_class=eventClass, tags=tags, ) response, content = self.client.getEventTagSeverities(from_dict(EventFilter, eventFilter)) return content.severities def getDevicePingIssues(self): return self.getDeviceIssues(eventClass=[Status_Ping], severity=[SEVERITY_WARNING,SEVERITY_ERROR,SEVERITY_CRITICAL], status=[STATUS_NEW,STATUS_ACKNOWLEDGED,STATUS_SUPPRESSED]) def getDeviceStatusIssues(self): return self.getDeviceIssues(eventClass=["/Status/"], severity=[SEVERITY_ERROR,SEVERITY_CRITICAL], status=[STATUS_NEW,STATUS_ACKNOWLEDGED]) def getDeviceIssues(self, eventClass=(), severity=(SEVERITY_DEBUG, SEVERITY_INFO, SEVERITY_WARNING, SEVERITY_ERROR, SEVERITY_CRITICAL), status=(STATUS_NEW,)): tagSeverities = self._getEventTagSeverities( eventClass=eventClass, severity=severity, status=status, deviceOnly=True) issues = [] for eventTagSeverity in tagSeverities: device = self._guidManager.getObject(eventTagSeverity.tag_uuid) if device and isinstance(device, Device): count = 0 for severity in eventTagSeverity.severities: count += severity.count total = eventTagSeverity.total issues.append((device.id, count, total)) return issues def getDeviceIssuesDict(self, eventClass=(), severity=(), status=()): severities = self._getEventTagSeverities(eventClass, severity, status) return self._createSeveritiesDict(severities) def getDetails(self): """ Retrieve all of the indexed detail items. @rtype list of EventDetailItem dicts """ return getDetailsInfo().getDetails() def getUnmappedDetails(self): """ Return only non-zenoss details. This is used to get details that will not be mapped to another key. (zenoss.device.production_state maps back to prodState, so will be excluded here) """ return getDetailsInfo().getUnmappedDetails() def getDetailsMap(self): """ Return a mapping of detail keys to dicts of detail items """ return getDetailsInfo().getDetailsMap() def parseParameterDetails(self, parameters): """ Given grid parameters, split into keys that are details and keys that are other parameters. """ params = {} details = {} detail_keys = self.getDetailsMap().keys() for k, v in parameters.iteritems(): if k in self.ZENOSS_DETAIL_OLD_TO_NEW_MAPPING: k = self.ZENOSS_DETAIL_OLD_TO_NEW_MAPPING[k] if k in detail_keys: details[k] = v else: params[k] = v # verify parameters against known valid ones # If there's an extra, it either needs to be added or # is an invalid detail that can't be searched. leftovers = set(s.lower() for s in params) - set(self.DEFAULT_SORT_MAP) - set(('tags', 'deviceclass', 'systems', 'location', 'devicegroups', 'message', 'excludenonactionables' )) if leftovers: raise InvalidQueryParameterException("Invalid query parameters specified: %s" % ', '.join(leftovers)) return params, details def addIndexedDetails(self, detailItemSet): """ @type detailItemSet: zenoss.protocols.protobufs.zep_pb2.EventDetailItemSet """ _ZEP_DETAILS_INFO = [] return self.configClient.addIndexedDetails(detailItemSet) def updateIndexedDetail(self, item): """ @type item: zenoss.protocols.protobufs.zep_pb2.EventDetailItem """ _ZEP_DETAILS_INFO = [] return self.configClient.updateIndexedDetail(item) def removeIndexedDetail(self, key): """ @type key: string """ # Gather the new details information _ZEP_DETAILS_INFO = [] return self.configClient.removeIndexedDetail(key) def countEventsSince(self, since): """ Returns the total number of events in summary and archive that have been seen since the specified time (in seconds). @type since: int @param since: Time (in seconds) from the epoch. @rtype: int @return: The number of events in summary and archive that have been seen since the specified time. """ sinceInMillis = int(since * 1000) eventFilter = self.createEventFilter(last_seen=[sinceInMillis]) total = self.getEventSummaries(0, filter=eventFilter, limit=0)['total'] total += self.getEventSummariesFromArchive(0, filter=eventFilter, limit=0)['total'] return total def getHeartbeats(self, monitor=None): response, heartbeats = self.heartbeatClient.getHeartbeats(monitor=monitor) heartbeats_dict = to_dict(heartbeats) return heartbeats_dict.get('heartbeats', []) def deleteHeartbeats(self, monitor=None): self.heartbeatClient.deleteHeartbeats(monitor=monitor) def deleteHeartbeat(self, monitor, daemon): """ Removes the heartbeat record for the specified monitor and daemon. @param monitor: The heartbeat monitor (i.e. 'localhost'). @type monitor: basestring @param daemon: The heartbeat daemon (i.e. 'zenhub'). @type daemon: basestring """ self.heartbeatClient.deleteHeartbeat(monitor, daemon) def create(self, summary, severity, device, component=None, mandatory=True, **kwargs): """ Create an event. @param summary: Summary message of the event. This variable gets mapped to an C{Event} protobuf field of the same name. @type summary: string @param severity: Severity name of the event. This variable gets mapped to the C{Event} protobuf using an C{EventProtobufSeverityMapper} (C{Products.ZenMessaging.queuemessaging.adapters.EventProtobufSeverityMapper}). This value can be an integer-like string value 0-5 or the string of the severity (clear, debug, info, warning, error, critical). The casing of the string does not matter. An empty severity value will be mapped to CLEAR. @type severity: string @param device: Device string. This variable gets set as the element on the C{Event} protobuf using an C{EventProtobufDeviceMapper} (C{Products.ZenMessaging.queuemessaging.adapters.EventProtobufDeviceMapper}). The value for this variable will always be set as a DEVICE element type. This value is later interpreted by zeneventd during the C{IdentifierPipe} (C{Products.ZenEvents.events2.processing.IdentifierPipe}) segment of the event processing pipeline. If an ipAddress is also provided in kwargs, the ipAddress is also used to help identify the device. If the ipAddress is provided, it is used first. If the ipAddress is not provided, the IP for a device is attempted to be discerned from the value of this variable. If the IP cannot be inferred, identification falls back to the value of this variable as either the ID of the device, or the title of the device as stored in the device catalog (as the NAME field). @type device: string @param component: ID of the component. This variable is used to lookup components by their ID or the title of the component (also stored as the NAME field in the catalog). See more info on how these objects are cataloged here: C{Products.Zuul.catalog.global_catalog.IndexableWrapper}. @type component: string @param mandatory: If True, message will be returned unless it can be routed to a queue. @type mandatory: boolean @param eventClass: Name of the event class to fall under. @type eventClass: string For other parameters see class Event. """ occurrence_uuid = str(uuid4()) rcvtime = time() args = dict(evid=occurrence_uuid, summary=summary, severity=severity, device=device) if component: args['component'] = component args.update(kwargs) event = ZenEvent(rcvtime=rcvtime, **args) publisher = getUtility(IEventPublisher) publisher.publish(event, mandatory=mandatory) def updateDetails(self, evid, **detailInfo): """ Given an evid, update the detail key/value pairs in ZEP. """ if len(detailInfo) == 1 and isinstance(detailInfo.values()[0], EventDetailSet): return self.client.updateDetails(evid, detailInfo.values()[0]) detailSet = EventDetailSet() for key, value in detailInfo.items(): detailSet.details.add(name=key, value=(value,)) return self.client.updateDetails(evid, detailSet) def getStats(self): response, stats = self.statsClient.get() statsList = [] for stat in stats.stats: myst = {} myst['name'] = stat.name myst['description'] = stat.description myst['value'] = stat.value statsList.append(myst) return statsList
def __init__(self, context, request): super(EventsRouter, self).__init__(context, request) self.zep = Zuul.getFacade('zep', context) self.catalog = ICatalogTool(context) self.manager = IGUIDManager(context.dmd)
class TriggersFacade(ZuulFacade): def __init__(self, context): super(TriggersFacade, self).__init__(context) self._guidManager = IGUIDManager(self._dmd) config = getGlobalConfiguration() schema = getUtility(IQueueSchema) self.triggers_service = TriggerServiceClient(config.get('zep_uri', 'http://localhost:8084'), schema) self.notificationPermissions = NotificationPermissionManager() self.triggerPermissions = TriggerPermissionManager() def _removeNode(self, obj): """ Remove an object in ZODB. This method was created to provide a hook for unit tests. """ context = aq_parent(obj) return context._delObject(obj.id) def _removeTriggerFromZep(self, uuid): """ Remove a trigger from ZEP. This method was created to provide a hook for unit tests. """ return self.triggers_service.removeTrigger(uuid) def removeNode(self, uid): obj = self._getObject(uid) return self._removeNode(obj) def _setTriggerGuid(self, trigger, guid): """ @param trigger: The trigger object to set the guid on. @type trigger: Products.ZenModel.Trigger.Trigger @param guid: The guid @type guid: str This method was created to provide a hook for unit tests. """ IGlobalIdentifier(trigger).guid = guid def _getTriggerGuid(self, trigger): """ @param trigger: The trigger object in zodb. @type trigger: Products.ZenModel.Trigger.Trigger This method was created to provide a hook for unit tests. """ return IGlobalIdentifier(trigger).guid def _setupTriggerPermissions(self, trigger): """ This method was created to provide a hook for unit tests. """ self.triggerPermissions.setupTrigger(trigger) def synchronize(self): """ This method will first synchronize all triggers that exist in ZEP to their corresponding objects in ZODB. Then, it will clean up notifications and remove any subscriptions to triggers that no longer exist. """ log.debug('SYNC: Starting trigger and notification synchronization.') _, trigger_set = self.triggers_service.getTriggers() zep_uuids = set(t.uuid for t in trigger_set.triggers) zodb_triggers = self._getTriggerManager().objectValues() # delete all triggers in zodb that do not exist in zep. for t in zodb_triggers: if not self._getTriggerGuid(t) in zep_uuids: log.info('SYNC: Found trigger in zodb that does not exist in zep, removing: %s' % t.id) self._removeNode(t) zodb_triggers = self._getTriggerManager().objectValues() zodb_uuids = set(self._getTriggerGuid(t) for t in zodb_triggers) # create all triggers in zodb that do not exist in zep. for t in trigger_set.triggers: if not t.uuid in zodb_uuids: log.info('SYNC: Found trigger uuid in zep that does not seem to exist in zodb, creating: %s' % t.name) triggerObject = Trigger(str(t.name)) try: self._getTriggerManager()._setObject(triggerObject.id, triggerObject) except BadRequest: # looks like the id already exists, remove this specific # trigger from zep. This can happen if multiple createTrigger # requests are sent from the browser at once - the transaction # will not abort until after the requests to create a trigger # have already been sent to zep. # See https://dev.zenoss.com/tracint/ticket/28272 log.info('SYNC: Found trigger with duplicate id in zodb, deleting from zep: %s (%s)' % (triggerObject.id, t.uuid)) self._removeTriggerFromZep(t.uuid) else: # setting a guid fires off events, we have to acquire the object # before we adapt it, otherwise adapters responding to the event # will get the 'regular' Trigger object and not be able to handle # it. self._setTriggerGuid(self._getTriggerManager().findChild(triggerObject.id), str(t.uuid)) self._setupTriggerPermissions(self._getTriggerManager().findChild(t.name)) # sync notifications for n in self._getNotificationManager().getChildNodes(): is_changed = False subs = list(n.subscriptions) for s in subs: if s not in zep_uuids: # this trigger no longer exists in zep, remove it from # this notification's subscriptions. log.info('SYNC: Notification subscription no longer valid: %s' % s) is_changed = True n.subscriptions.remove(s) if is_changed: log.debug('SYNC: Updating notification subscriptions: %s' % n.id) self.updateNotificationSubscriptions(n) log.debug('SYNC: Trigger and notification synchronization complete.') def getTriggers(self): self.synchronize() user = getSecurityManager().getUser() response, trigger_set = self.triggers_service.getTriggers() trigger_set = to_dict(trigger_set) if 'triggers' in trigger_set: return self.triggerPermissions.findTriggers(user, self._guidManager, trigger_set['triggers']) else: return [] def parseFilter(self, source): """ Parse a filter to make sure it's sane. @param source: The python expression to test. @type source: string @todo: make this not allow nasty python. """ if isinstance(source, basestring): if source: tree = parser.expr(source) if parser.isexpr(tree): return source else: raise Exception('Invalid filter expression.') else: return True # source is empty string def addTrigger(self, newId): return self.createTrigger( name = newId, uuid = None, rule = dict( source = '' ) ) def createTrigger(self, name, uuid=None, rule=None): name = str(name) zodb_triggers = self._getTriggerManager().objectValues() zodb_trigger_names = set(t.id for t in zodb_triggers) if name in zodb_trigger_names: raise DuplicateTriggerName, ('The id "%s" is invalid - it is already in use.' % name) triggerObject = Trigger(name) self._getTriggerManager()._setObject(name, triggerObject) acquired_trigger = self._getTriggerManager().findChild(name) if uuid: IGlobalIdentifier(acquired_trigger).guid = str(uuid) else: IGlobalIdentifier(acquired_trigger).create() self.triggerPermissions.setupTrigger(acquired_trigger) trigger = zep.EventTrigger() trigger.uuid = IGlobalIdentifier(acquired_trigger).guid trigger.name = name trigger.rule.api_version = 1 trigger.rule.type = zep.RULE_TYPE_JYTHON if rule and 'source' in rule: trigger.rule.source = rule['source'] else: trigger.rule.source = '' self.triggers_service.addTrigger(trigger) log.debug('Created trigger with uuid: %s ' % trigger.uuid) return trigger.uuid def removeTrigger(self, uuid): user = getSecurityManager().getUser() trigger = self._guidManager.getObject(uuid) if self.triggerPermissions.userCanUpdateTrigger(user, trigger): # If a user has the ability to update (remove) a trigger, it is # presumed that they will be consciously deleting triggers that # may have subscribers. # # Consider that that trigger may be subscribed to by notifications # that the current user cannot read/edit. # if there was an error, the triggers service will throw an exception self._removeTriggerFromZep(uuid) context = aq_parent(trigger) context._delObject(trigger.id) relevant_notifications = self.getNotificationsBySubscription(uuid) updated_count = 0 for n in relevant_notifications: n.subscriptions.remove(uuid) log.debug('Removing trigger uuid %s from notification: %s' % (uuid, n.id)) self.updateNotificationSubscriptions(n) updated_count += 1 return updated_count else: log.warning('User not authorized to remove trigger: User: %s, Trigger: %s' % (user.getId(), trigger.id)) raise Exception('User not authorized to remove trigger: User: %s, Trigger: %s' % (user.getId(), trigger.id)) def getNotificationsBySubscription(self, trigger_uuid): for n in self._getNotificationManager().getChildNodes(): if trigger_uuid in n.subscriptions: yield n def getTrigger(self, uuid): user = getSecurityManager().getUser() trigger = self._guidManager.getObject(uuid) log.debug('Trying to fetch trigger: %s' % trigger.id) if self.triggerPermissions.userCanViewTrigger(user, trigger): response, trigger = self.triggers_service.getTrigger(uuid) return to_dict(trigger) else: log.warning('User not authorized to view this trigger: %s' % trigger.id) raise Exception('User not authorized to view this trigger: %s' % trigger.id) def getTriggerList(self): """ Retrieve a list of all triggers by uuid and name. This is used by the UI to render triggers that a user may not have permission to otherwise view, edit or manage. """ response, trigger_set = self.triggers_service.getTriggers() trigger_set = to_dict(trigger_set) triggerList = [] if 'triggers' in trigger_set: for t in trigger_set['triggers']: triggerList.append(dict( uuid = t['uuid'], name = t['name'] )) return sorted(triggerList, key=lambda k: k['name']) def updateTrigger(self, **data): user = getSecurityManager().getUser() triggerObj = self._guidManager.getObject(data['uuid']) log.debug('Trying to update trigger: %s' % triggerObj.id) if self.triggerPermissions.userCanManageTrigger(user, triggerObj): if 'globalRead' in data: triggerObj.globalRead = data.get('globalRead', False) log.debug('setting globalRead %s' % triggerObj.globalRead) if 'globalWrite' in data: triggerObj.globalWrite = data.get('globalWrite', False) log.debug('setting globalWrite %s' % triggerObj.globalWrite) if 'globalManage' in data: triggerObj.globalManage = data.get('globalManage', False) log.debug('setting globalManage %s' % triggerObj.globalManage) triggerObj.users = data.get('users', []) self.triggerPermissions.clearPermissions(triggerObj) self.triggerPermissions.updatePermissions(self._guidManager, triggerObj) if self.triggerPermissions.userCanUpdateTrigger(user, triggerObj): if "name" in data: triggerObj.setTitle(data["name"]) trigger = from_dict(zep.EventTrigger, data) response, content = self.triggers_service.updateTrigger(trigger) return content def _getTriggerManager(self): return self._dmd.findChild('Triggers') def _getNotificationManager(self): return self._dmd.findChild('NotificationSubscriptions') def getNotifications(self): self.synchronize() user = getSecurityManager().getUser() for n in self.notificationPermissions.findNotifications(user, self._getNotificationManager().getChildNodes()): yield IInfo(n) def _updateContent(self, notification, data=None): try: util = getUtility(IAction, notification.action) except ComponentLookupError: raise InvalidTriggerActionType("Invalid action type specified: %s" % notification.action) fields = {} for iface in providedBy(util.getInfo(notification)): f = getFields(iface) if f: fields.update(f) data = util.getDefaultData(self._dmd) for k, v in fields.iteritems(): if k not in data: data[k] = v.default util.updateContent(notification.content, data) def addNotification(self, newId, action): notification = self.createNotification(newId, action) return IInfo(notification) def createNotification(self, id, action, guid=None): notification = NotificationSubscription(id) notification.action = action self._updateContent(notification) self._getNotificationManager()._setObject(id, notification) acquired_notification = self._getNotificationManager().findChild(id) self.notificationPermissions.setupNotification(acquired_notification) self.updateNotificationSubscriptions(notification) notification = self._getNotificationManager().findChild(id) notification.userRead = True notification.userWrite = True notification.userManage = True if guid: IGlobalIdentifier(notification).guid = guid return notification def removeNotification(self, uid): user = getSecurityManager().getUser() notification = self._getObject(uid) if self.notificationPermissions.userCanUpdateNotification(user, notification): return self.removeNode(uid) else: log.warning('User not authorized to remove notification: User: %s, Notification: %s' % (user.getId(), notification.id)) raise Exception('User not authorized to remove notification.') def getNotification(self, uid): user = getSecurityManager().getUser() notification = self._getObject(uid) if self.notificationPermissions.userCanViewNotification(user, notification): log.debug('getting notification: %s' % notification) return IInfo(notification) else: log.warning('User not authorized to view this notification: %s' % uid) raise Exception('User not authorized to view this notification: %s' % uid) def updateNotificationSubscriptions(self, notification): triggerSubscriptions = [] notification_guid = IGlobalIdentifier(notification).getGUID() for subscription in notification.subscriptions: triggerSubscriptions.append(dict( delay_seconds = notification.delay_seconds, repeat_seconds = notification.repeat_seconds, subscriber_uuid = notification_guid, send_initial_occurrence = notification.send_initial_occurrence, trigger_uuid = subscription, )) subscriptionSet = from_dict(zep.EventTriggerSubscriptionSet, dict( subscriptions = triggerSubscriptions )) self.triggers_service.updateSubscriptions(notification_guid, subscriptionSet) def updateNotification(self, **data): log.debug(data) uid = data['uid'] # can't change action type after creation if 'action' in data: del data['action'] notification = self._getObject(uid) if not notification: raise Exception('Could not find notification to update: %s' % uid) # don't update any properties unless the current user has the correct # permission. user = getSecurityManager().getUser() if self.notificationPermissions.userCanUpdateNotification(user, notification): # update the action content data action = getUtility(IAction, notification.action) action.updateContent(notification.content, data) if self.notificationPermissions.userCanManageNotification(user, notification): # if these values are not sent (in the case that the fields have been # disabled, do not set the value. if 'notification_globalRead' in data: notification.globalRead = data.get('notification_globalRead') log.debug('setting globalRead') if 'notification_globalWrite' in data: notification.globalWrite = data.get('notification_globalWrite') log.debug('setting globalWrite') if 'notification_globalManage' in data: notification.globalManage = data.get('notification_globalManage') log.debug('setting globalManage') for field in notification._properties: notification._updateProperty(field['id'], data.get(field['id'])) notification.subscriptions = data.get('subscriptions') self.updateNotificationSubscriptions(notification) notification.recipients = data.get('recipients', []) self.notificationPermissions.clearPermissions(notification) self.notificationPermissions.updatePermissions(self._guidManager, notification) log.debug('updated notification: %s' % notification) def getRecipientOptions(self): users = self._dmd.ZenUsers.getAllUserSettings() groups = self._dmd.ZenUsers.getAllGroupSettings() data = [] for u in users: data.append(self.fetchRecipientOption(u)) for g in groups: data.append(self.fetchRecipientOption(g)) return data def fetchRecipientOption(self, recipient): my_type = 'group' if isinstance(recipient, GroupSettings) else 'user' return dict( type = my_type, label = '%s (%s)' % (recipient.getId(), my_type.capitalize()), value = IGlobalIdentifier(recipient).getGUID(), ) def getWindows(self, uid): notification = self._getObject(uid) for window in notification.windows(): yield IInfo(window) def addWindow(self, contextUid, newId): notification = self._getObject(contextUid) window = NotificationSubscriptionWindow(newId) notification.windows._setObject(newId, window) new_window = notification.windows._getOb(newId) return IInfo(new_window) def removeWindow(self, uid): return self.removeNode(uid) def getWindow(self, uid): window = self._getObject(uid) return IInfo(window) def updateWindow(self, data): uid = data['uid'] window = self._getObject(uid) if not window: raise Exception('Could not find window to update: %s' % uid) for field in window._properties: if field['id'] == 'start': start = data['start'] start = start + " T" + data['starttime'] startDT = datetime.strptime(start, "%m-%d-%Y T%H:%M") setattr(window, 'start', int(startDT.strftime('%s'))) elif field['id'] == 'duration': setattr(window, 'duration', int(data['duration'])) elif field['id'] == 'skip': skip = data.get('skip') if skip is not None: window.skip = skip else: setattr(window, field['id'], data.get(field['id'])) log.debug('updated window') def exportConfiguration(self, triggerIds=None, notificationIds=None): notifications = [] if notificationIds is None: notificationIds = [] notifications = list(self.getNotifications()) elif isinstance(notificationIds, str): notificationIds = [notificationIds] triggers = self.getTriggers() if triggerIds is not None: names = [triggerIds] if isinstance(triggerIds, str) else triggerIds triggers = [x for x in triggers if x['name'] in names] for trigger in triggers: uid = trigger['uuid'] nsIds = [x.id for x in self.getNotificationsBySubscription(uid)] notificationIds.extend(nsIds) triggerData = self.exportTriggers(triggers) if notificationIds: notifications = [x for x in notifications if x.id in notificationIds] notificationData = self.exportNotifications(notifications) return triggerData, notificationData def exportTriggers(self, triggers): configs = [] junkColumns = ('id', 'newId') for config in triggers: for item in junkColumns: if item in config: del config[item] configs.append(config) return configs def exportNotifications(self, notifications): configs = [] junkColumns = ('id', 'newId', 'uid', 'inspector_type', 'meta_type') for notificationInfo in notifications: config = marshal(notificationInfo) for item in junkColumns: if item in config: del config[item] contentsTab = self._extractNotificationContentInfo(config) del config['content'] config.update(contentsTab) config['recipients'] = [r['label'] for r in config['recipients']] config['subscriptions'] = [x['name'] for x in config['subscriptions']] windows = [] for window in notificationInfo._object.windows(): winconfig = marshal(IInfo(window)) for witem in ('meta_type', 'newId', 'id', 'inspector_type', 'uid'): del winconfig[witem] windows.append(winconfig) config['windows'] = windows configs.append(config) return configs def _extractNotificationContentInfo(self, notification): contents = {} try: for itemInfo in notification['content']['items'][0]['items']: key = itemInfo['name'] contents[key] = itemInfo['value'] except Exception: log.exception("Unable to extract data from notifcation: %s", notification) return contents def importConfiguration(self, triggers=None, notifications=None): itcount, incount = 0, 0 if triggers: itcount = self.importTriggers(triggers) if notifications: incount = self.importNotifications(notifications) return itcount, incount def importTriggers(self, triggers): """ Add any new trigger definitions to the system. Note: modifies the triggers argument to add 'new_uuid' to the definition. Does not attempt to link a trigger to a notification. """ existingTriggers = [x['name'] for x in self.getTriggerList()] existingUsers = [x.id for x in self._dmd.ZenUsers.getAllUserSettings()] removeDataList = [ 'subscriptions' ] imported = 0 for trigger in triggers: name = trigger.get('name') if name is None: log.warn("Missing name in trigger definition: %s", trigger) continue if name in existingTriggers: log.warn("Skipping existing trigger '%s'", name) continue data = deepcopy(trigger) trigger['new_uuid'] = data['uuid'] = self.addTrigger(name) # Cleanup for key in removeDataList: if key in data: del data[key] # Don't delete data from list you're looping through for user in trigger.get('users', []): if user not in existingUsers: log.warning("Unable to find trigger %s user '%s' on this server -- skipping", name, user) data['users'].remove(user) # Make changes to the definition self.updateTrigger(**data) imported += 1 return imported def importNotifications(self, notifications): """ Add new notification definitions to the system. """ existingNotifications = [x.id for x in self.getNotifications()] existingTypes = [x.action for x in self.getNotifications()] usersGroups = dict( (x['label'], x) for x in self.getRecipientOptions()) trigerToUuid = dict( (x['name'], x['uuid']) for x in self.getTriggers()) imported = 0 for notification in notifications: name = notification.get('name') if name is None: log.warn("Missing name in notification definition: %s", notification) continue if name in existingNotifications: log.warn("Skipping existing notification '%s'", name) continue ntype = notification.get('action') if ntype is None: log.warn("Missing 'action' in notification definition: %s", notification) continue if ntype not in existingTypes: log.warn("The notification %s references an unknown action type: %s", name, ntype) continue data = deepcopy(notification) obj = self.addNotification(name, ntype) notification['uid'] = data['uid'] = obj.getPrimaryUrlPath() self.getRecipientsToImport(name, data, usersGroups) if 'action' in data: del data['action'] self.linkImportedNotificationToTriggers(data, trigerToUuid) windows = data.get('windows', []) if windows: for window in windows: iwindow = self.addWindow(data['uid'], window['name']) del window['name'] window['uid'] = iwindow.uid self.updateWindow(window) del data['windows'] # Make changes to the definition self.updateNotification(**data) imported += 1 return imported def getRecipientsToImport(self, name, data, usersGroups): recipients = [] for label in data.get('recipients', []): if label in usersGroups: recipients.append(usersGroups[label]) else: log.warn("Unable to find %s for recipients for notification %s", label, name) data['recipients'] = recipients def linkImportedNotificationToTriggers(self, notification, trigerToUuid): subscriptions = [] for subscription in notification.get('subscriptions', []): uuid = trigerToUuid.get(subscription['name']) if uuid is not None: subscriptions.append(uuid) else: log.warn("Unable to link notification %s to missing trigger '%s'", notification['name'], subscription['name']) notification['subscriptions'] = subscriptions
def __init__(self, context, request): super(EventsRouter, self).__init__(context, request) self.zep = Zuul.getFacade('zep', context) self.catalog = ICatalogTool(context) self.manager = IGUIDManager(context.dmd) self._filterParser = _FilterParser(self.zep)
def load(self, pack, app): """ Load Notifications and Triggers from an actions.json file Given a JSON-formatted configuration located at {zenpack}/actions/actions.json, create or update triggers and notifications specific to this zenpack. When creating or updating, the object is first checked to see whether or not an object exists with the configured guid for notifications or uuid for triggers. If an object is not found, one will be created. During creation, care is taken with regard to the id - integer suffixes will be appended to try to create a unique id. If we do not find a unique id after 100 tries, an error will occur. When updating an object, care is taken to not change the name as it may have since been altered by the user (or by this loader adding a suffix). """ log.debug("ZPTriggerAction: load") import Products.Zuul as Zuul from Products.Zuul.facades import ObjectNotFoundException tf = Zuul.getFacade('triggers', app.dmd) guidManager = IGUIDManager(app) for conf in findFiles(pack, 'zep', lambda f: f == 'actions.json'): import json data = json.load(open(conf, "r")) log.debug("DATA IS: %s" % data) triggers = data.get('triggers', []) notifications = data.get('notifications', []) tf.synchronize() all_names = set(t['name'] for t in tf.getTriggerList()) for trigger_conf in triggers: existing_trigger = guidManager.getObject(trigger_conf['uuid']) if existing_trigger: trigger_data = tf.getTrigger(trigger_conf['uuid']) trigger_conf['name'] = trigger_data['name'] log.info('Existing trigger found, updating: %s' % trigger_conf['name']) tf.updateTrigger(**trigger_conf) else: test_name = trigger_conf['name'] for x in xrange(1, 101): if test_name in all_names: test_name = '%s_%d' % (trigger_conf['name'], x) else: log.debug('Found unused trigger name: %s' % test_name) break else: # if we didn't find a unique name raise Exception( 'Could not find unique name for trigger: "%s".' % trigger_conf['name']) log.info('Creating trigger: %s' % test_name) tf.createTrigger(test_name, uuid=trigger_conf['uuid'], rule=trigger_conf['rule']) for notification_conf in notifications: existing_notification = guidManager.getObject( str(notification_conf['guid'])) if existing_notification: log.info("Existing notification found, updating: %s" % existing_notification.id) subscriptions = set(existing_notification.subscriptions + notification_conf['subscriptions']) notification_conf[ 'uid'] = '/zport/dmd/NotificationSubscriptions/%s' % existing_notification.id notification_conf['subscriptions'] = list(subscriptions) notification_conf['name'] = existing_notification.id tf.updateNotification(**notification_conf) else: test_id = notification_conf['id'] for x in xrange(1, 101): test_uid = '/zport/dmd/NotificationSubscriptions/%s' % test_id try: tf.getNotification(test_uid) except ObjectNotFoundException: break test_id = '%s_%d' % (notification_conf['id'], x) else: # if we didn't find a unique name raise Exception( 'Could not find unique name for notification: "%s".' % notification_conf['id']) log.info('Creating notification: %s' % test_id) tf.createNotification(str(test_id), notification_conf['action'], notification_conf['guid']) notification_conf[ 'uid'] = '/zport/dmd/NotificationSubscriptions/%s' % test_id tf.updateNotification(**notification_conf)
class Manager(object): """ Provides lookup access to processing pipes and performs caching. """ ELEMENT_TYPE_MAP = { DEVICE: Device, COMPONENT: DeviceComponent, } def __init__(self, dmd): self.dmd = dmd self._initCatalogs() def _initCatalogs(self): self._guidManager = IGUIDManager(self.dmd) self._devices = self.dmd._getOb('Devices') self._networks = self.dmd._getOb('Networks') self._events = self.dmd._getOb('Events') self._catalogs = { DEVICE: self._devices, } def reset(self): self._initCatalogs() def getEventClassOrganizer(self, eventClassName): try: return self._events.getOrganizer(eventClassName) except KeyError: # Unknown organizer return None def lookupEventClass(self, eventContext): """ Find a Device's EventClass """ return self._events.lookup(eventContext.eventProxy, eventContext.deviceObject) def getElementByUuid(self, uuid): """ Get a Device/Component by UUID """ if uuid: return self._guidManager.getObject(uuid) def uuidFromBrain(self, brain): """ Helper method to deal with catalog brains which are out of date. If the uuid is not set on the brain, we attempt to load it from the object. """ uuid = brain.uuid return uuid if uuid else IGlobalIdentifier(brain.getObject()).getGUID() @FunctionCache("getElementUuidById", cache_miss_marker=-1, default_timeout=300) def getElementUuidById(self, catalog, element_type_id, id): """ Find element by ID but only cache UUID. This forces us to lookup elements each time by UUID (pretty fast) which gives us a chance to see if the element has been deleted. """ cls = self.ELEMENT_TYPE_MAP.get(element_type_id) if cls: catalog = catalog or self._catalogs.get(element_type_id) if catalog: results = ICatalogTool(catalog).search(cls, query=Or(Eq('id', id), Eq('name', id)), filterPermissions=False, limit=1) if results.total: try: result = results.results.next() except StopIteration: pass else: return self.uuidFromBrain(result) def getElementById(self, catalog, element_type_id, id): """ Find element by ID, first checking a cache for UUIDs then using that UUID to load the element. If the element can't be found by UUID, the UUID cache is cleared and lookup tried again. """ uuid = self.getElementUuidById(catalog, element_type_id, id) if uuid: element = self.getElementByUuid(uuid) if not element: # Lookup cache must be invalid, try looking up again self.getElementUuidById.clear() log.warning( 'Clearing ElementUuidById cache becase we could not find %s' % uuid) uuid = self.getElementUuidById(catalog, element_type_id, id) element = self.getElementByUuid(uuid) return element def getElementUuid(self, obj): if obj: return IGlobalIdentifier(obj).getGUID() def _findDevices(self, identifier, ipAddress, limit=None): """ Returns a tuple ([device brains], [devices]) searching manage IP and interface IPs. limit is the maximum total number in both lists. """ dev_cat = ICatalogTool(self._devices) try: ip_address = next(i for i in (ipAddress, identifier) if isip(i)) ip_decimal = ipToDecimal(ip_address) except Exception: ip_address = None ip_decimal = None query_set = Or(Eq('id', identifier), Eq('name', identifier)) if ip_decimal is not None: query_set.addSubquery(Eq('ipAddress', str(ip_decimal))) device_brains = list(dev_cat.search(types=Device, query=query_set, limit=limit, filterPermissions=False)) limit = None if limit is None else limit - len(device_brains) if not limit: return device_brains, [] if ip_decimal is not None: # don't search interfaces for 127.x.x.x IPv4 addresses if ipToDecimal('126.255.255.255') < ip_decimal < ipToDecimal('128.0.0.0'): ip_decimal = None # don't search interfaces for the ::1 IPv6 address elif ipToDecimal('::1') == ip_decimal: ip_decimal = None if ip_decimal is None and not device_brains: return [], [] net_cat = ICatalogTool(self._networks) results = net_cat.search(types=IpAddress, query=(Eq('name', ip_address)), limit = limit, filterPermissions = False) devices = [brain.getObject().device() for brain in results] return device_brains, devices @FunctionCache("findDeviceUuid", cache_miss_marker=-1, default_timeout=300) def findDeviceUuid(self, identifier, ipAddress): """ This will return the device's @type identifier: string @param identifier: The IP address or id of a device @type ipaddress: string @param ipaddress: The known ipaddress of the device """ device_brains, devices = self._findDevices(identifier, ipAddress, limit=1) if device_brains: return self.uuidFromBrain(device_brains[0]) if devices: return self.getElementUuid(devices[0]) return None def findDevice(self, identifier, ipAddress): uuid = self.findDeviceUuid(identifier, ipAddress) if uuid: return self.getElementByUuid(uuid) def getUuidsOfPath(self, node): """ Looks up all the UUIDs in the tree path of an Organizer """ uuids = set() acquisition_chain = [] for n in aq_chain(node.primaryAq()): if isinstance(n, DataRoot): acquisition_chain.pop() break acquisition_chain.append(n) if acquisition_chain: for obj in filter(None, acquisition_chain): try: uuids.add(self.getElementUuid(obj)) except TypeError: log.debug("Unable to get a uuid for %s " % obj) return filter(None, uuids)