class ThresholdNotifier(object):
    """
    Encapsulates the logic necessary to evaluate a datapoint value
    against thresholds and send any events that are generated from
    threshold evaluation. Used by CollectorDaemon and DaemonStats.
    """
    def __init__(self, send_callback, thresholds):
        self._send_callback = send_callback
        if isinstance(thresholds, list):
            self._thresholds = Thresholds()
            self._thresholds.updateList(thresholds)
        elif isinstance(thresholds, Thresholds):
            self._thresholds = thresholds
        else:
            self._thresholds = Thresholds()

    def updateThresholds(self, thresholds):
        self._thresholds.updateList(thresholds)

    @defer.inlineCallbacks
    def notify(self,
               context_uuid,
               context_id,
               metric,
               timestamp,
               value,
               thresh_event_data=None):
        """
        Check the specified value against thresholds and send any generated
        events

        @param context_uuid: context name used to check thresholds
        @param context_id: can be used for event key prefix
        @param metric: name of the metric
        @param timestamp: timestamp for the value
        @param value: the value to check
        @param thresh_event_data: additional data to send with any events
        @return:
        """
        if self._thresholds and value is not None:
            thresh_event_data = thresh_event_data or {}
            if 'eventKey' in thresh_event_data:
                eventKeyPrefix = [thresh_event_data['eventKey']]
            else:
                eventKeyPrefix = [metric]
            for ev in self._thresholds.check(context_uuid, metric, timestamp,
                                             value):
                parts = eventKeyPrefix[:]
                if 'eventKey' in ev:
                    parts.append(ev['eventKey'])
                ev['eventKey'] = '|'.join(parts)
                # add any additional values for this threshold
                # (only update if key is not in event, or if
                # the event's value is blank or None)
                for key, value in thresh_event_data.items():
                    if ev.get(key, None) in ('', None):
                        ev[key] = value
                if ev.get("component", None):
                    ev['component_guid'] = context_uuid
                yield defer.maybeDeferred(self._send_callback, ev)
class ThresholdNotifier(object):
    """
    Encapsulates the logic necessary to evaluate a datapoint value
    against thresholds and send any events that are generated from
    threshold evaluation. Used by CollectorDaemon and DaemonStats.
    """

    def __init__(self, send_callback, thresholds):
        self._send_callback = send_callback
        if isinstance(thresholds, list):
            self._thresholds = Thresholds()
            self._thresholds.updateList(thresholds)
        elif isinstance(thresholds, Thresholds):
            self._thresholds = thresholds
        else:
            self._thresholds = Thresholds()

    def updateThresholds(self, thresholds):
        self._thresholds.updateList(thresholds)

    @defer.inlineCallbacks
    def notify(self, context_uuid, context_id, metric, timestamp, value, thresh_event_data=None):
        """
        Check the specified value against thresholds and send any generated
        events

        @param context_uuid: context name used to check thresholds
        @param context_id: can be used for event key prefix
        @param metric: name of the metric
        @param timestamp: timestamp for the value
        @param value: the value to check
        @param thresh_event_data: additional data to send with any events
        @return:
        """
        if self._thresholds and value is not None:
            thresh_event_data = thresh_event_data or {}
            if 'eventKey' in thresh_event_data:
                eventKeyPrefix = [thresh_event_data['eventKey']]
            else:
                eventKeyPrefix = [metric]
            for ev in self._thresholds.check(context_uuid, metric, timestamp, value):
                parts = eventKeyPrefix[:]
                if 'eventKey' in ev:
                    parts.append(ev['eventKey'])
                ev['eventKey'] = '|'.join(parts)
                # add any additional values for this threshold
                # (only update if key is not in event, or if
                # the event's value is blank or None)
                for key, value in thresh_event_data.items():
                    if ev.get(key, None) in ('', None):
                        ev[key] = value
                if ev.get("component", None):
                    ev['component_guid'] = context_uuid
                yield defer.maybeDeferred(self._send_callback, ev)
class DaemonStats(object):
    "Utility for a daemon to write out internal performance statistics"

    def __init__(self):
        self.name = ""
        self.monitor = ""
        self.rrdCreateCommand = ""

        self.thresholds = Thresholds()

    def config(self, name, monitor, thresholds, rrdCreateCommand=None):
        """Initialize the object.  We could do this in __init__, but
        that would delay creation to after configuration time, which
        may run asynchronously with collection or heartbeats.  By
        deferring initialization, this object implements the Null
        Object pattern until the application is ready to start writing
        real statistics.
        """
        self.name = name
        self.monitor = monitor
        if not rrdCreateCommand:
            from Products.ZenModel.PerformanceConf import PerformanceConf
            rrdCreateCommand = PerformanceConf.defaultRRDCreateCommand
        if not isinstance(rrdCreateCommand, basestring):
            self.createCommand = rrdCreateCommand
        else:
            self.createCommand = rrdCreateCommand.split('\n')
        self.thresholds = Thresholds()
        self.thresholds.updateList(thresholds)

    def rrdFile(self, type, cycleTime, name, minVal='U', maxVal='U'):
        """Create an RRD file if it does not exist.
        Returns the basename of the rrdFile, suitable for checking thresholds.
        """
        if not self.name: return None
        base = os.path.join('Daemons', self.name)
        directory = zenPath('perf', base)
        if not os.path.exists(directory):
            os.makedirs(directory)
        base = os.path.join(base, '%s_%s' % (self.monitor, name))
        fileName = fullname(base)
        if not os.path.exists(fileName):
            rrdtool.create(
                fileName, '--step', "%d" % cycleTime,
                'DS:ds0:%s:%s:%s:%s' % (type, cycleTime * 3, minVal, maxVal),
                *self.createCommand)
        return base

    def derive(self, name, cycleTime, value):
        "Write a DERIVE value, return threshold events"
        return self.counter(name, cycleTime, value)

    def counter(self, name, cycleTime, value):
        "Write a DERIVE(! NOT COUNTER!) value, return threshold events"
        fileName = self.rrdFile('DERIVE', cycleTime, name, 0)
        if fileName:
            full = fullname(fileName)
            try:
                rrdtool.update(full, 'N:%s' % int(value))
                startStop, names, values = \
                    rrdtool.fetch(full, 'AVERAGE',
                        '-s', 'now-%d' % (cycleTime*2),
                        '-e', 'now')
                value = values[0][0]
                if value is not None:
                    return self.thresholds.check(fileName, time.time(), value)
            except rrdtool.error, err:
                log.error('rrdtool reported error %s %s', err, full)
        return []
class DaemonStats(object):
    "Utility for a daemon to write out internal performance statistics"

    def __init__(self):
        self.name = ""
        self.monitor = ""
        self.rrdCreateCommand = ""

        self.thresholds = Thresholds()


    def config(self, name, monitor, thresholds, rrdCreateCommand = None):
        """Initialize the object.  We could do this in __init__, but
        that would delay creation to after configuration time, which
        may run asynchronously with collection or heartbeats.  By
        deferring initialization, this object implements the Null
        Object pattern until the application is ready to start writing
        real statistics.
        """
        self.name = name
        self.monitor = monitor
        if not rrdCreateCommand:
            from Products.ZenModel.PerformanceConf import PerformanceConf
            rrdCreateCommand = PerformanceConf.defaultRRDCreateCommand
        if not isinstance(rrdCreateCommand, basestring):
            self.createCommand = rrdCreateCommand
        else:
            self.createCommand = rrdCreateCommand.split('\n')
        self.thresholds = Thresholds()
        self.thresholds.updateList(thresholds)


    def rrdFile(self, type, cycleTime, name, minVal = 'U', maxVal = 'U'):
        """Create an RRD file if it does not exist.
        Returns the basename of the rrdFile, suitable for checking thresholds.
        """
        if not self.name: return None
        base = os.path.join('Daemons', self.name)
        directory = zenPath('perf', base)
        if not os.path.exists(directory):
            os.makedirs(directory)
        base = os.path.join(base, '%s_%s' % (self.monitor, name))
        fileName = fullname(base)
        if not os.path.exists(fileName):
            rrdtool.create(fileName,
                           '--step', "%d" % cycleTime,
                           'DS:ds0:%s:%s:%s:%s' % (type,
                                                   cycleTime * 3,
                                                   minVal,
                                                   maxVal),
                           *self.createCommand)
        return base


    def derive(self, name, cycleTime, value):
        "Write a DERIVE value, return threshold events"
        return self.counter(name, cycleTime, value)

    def counter(self, name, cycleTime, value):
        "Write a DERIVE(! NOT COUNTER!) value, return threshold events"
        fileName = self.rrdFile('DERIVE', cycleTime, name, 0)
        if fileName:
            full = fullname(fileName)
            try:
                rrdtool.update(full, 'N:%s' % int(value))
                startStop, names, values = \
                    rrdtool.fetch(full, 'AVERAGE',
                        '-s', 'now-%d' % (cycleTime*2),
                        '-e', 'now')
                value = values[0][0]
                if value is not None:
                    return self.thresholds.check(fileName, time.time(), value)
            except rrdtool.error, err:
                log.error('rrdtool reported error %s %s', err, full)
        return []