Example #1
0
    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]]
Example #2
0
    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)
Example #3
0
 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
         }
Example #4
0
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)
Example #5
0
 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)
Example #7
0
 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}
Example #8
0
 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}
Example #9
0
    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)
Example #10
0
 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)
Example #11
0
    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]]
Example #12
0
 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
Example #18
0
 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
Example #19
0
 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
Example #20
0
 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
Example #21
0
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
Example #22
0
    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
Example #23
0
 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 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
Example #26
0
    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,
        }
Example #27
0
    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
Example #28
0
    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 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)
Example #30
0
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
Example #31
0
    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)
Example #32
0
    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)
Example #34
0
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")
Example #35
0
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")
Example #36
0
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
Example #37
0
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
Example #38
0
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
Example #39
0
 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)
Example #40
0
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
Example #41
0
 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)
Example #42
0
    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)
Example #43
0
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)