Beispiel #1
0
class ActorBase(object):
    """
    Base class for Decision object. Defines common methods
    and properties, needed for a Decision class to operate
    with data, coming from the sources

    Arguments:

    .. interval::    Time in seconds between pushing data
                in the exchange/queue
    """

    exchange_type = 'topic'
    transport_class = ChannelWrapper

    def __init__(self, interval=5, **kwargs):
        self.interval = float(interval)
        assert self.id, "Actor 'id' property not set"
        self.channel = self.transport_class(self.id,
                                      self.exchange_type,
                                      publish=True,
                                      manual_ack=False,
                                      **kwargs)
        self._kwargs = kwargs
        self._create()

    def _create(self):
        """
        Called in the constructor, useful to be re-declared
        in a child classes to apply additional actions upon
        instance creation
        """
        pass

    def connect(self, **kwargs):
        """
        Connect to queue and start publishing data
        on the specified interval
        """
        signal.signal(signal.SIGTERM, self._on_signal_term)

        LOG.info("Starting ")
        self.timer = RepeatingTimer(self.interval, self.on_update)
        self.timer.start()
        self.channel.connect(**kwargs)

    def _on_signal_term(self, signum, frame):
        LOG.info('Received signal: %s' % signum)
        self.disconnect()

    def disconnect(self, **kwargs):
        if (hasattr(self, "timer") and self.timer):
            self.timer.stop()
        if (hasattr(self, "channel") and self.channel):
            if "reason" in kwargs.keys():
                LOG.info("Disconnected with reason: %s" % kwargs['reason'])
            self.channel.stop(**kwargs)

    def on_update(self):
        raise NotImplementedError("This should be implemented in parent class")
Beispiel #2
0
 def connect(self, **kwargs):
     """
     Connect to queue and start consuming
     """
     # Set the signal handler to gracefully disconnect
     signal.signal(signal.SIGTERM, self._on_signal_term)
     if (hasattr(self, "channel") and self.channel):
         self.timer = RepeatingTimer(self.period, lambda: self.evaluate(**kwargs))
         self.timer.start()
         self.channel.connect(**kwargs)
Beispiel #3
0
    def connect(self, **kwargs):
        """
        Connect to queue and start publishing data
        on the specified interval
        """
        signal.signal(signal.SIGTERM, self._on_signal_term)

        LOG.info("Starting ")
        self.timer = RepeatingTimer(self.interval, self.on_update)
        self.timer.start()
        self.channel.connect(**kwargs)
Beispiel #4
0
class DecisionBase(object):
    """
    Base class for Decision object. Defines common methods
    and properties, needed for a Decision class to operate
    with data, coming from the sources
    
    Arguments:
    
    .. period:: Integer, the period to collect statistics
            in seconds

    .. threshold::    Threshold value to fire an event. 
            Depends on the aggregate_type

    .. eval_func:: Function for evaluation logic. 
            Should accept iterable values and return
            threshold-comparable value 
    """
    
    exchange_type = 'topic'
     
    def __init__(self, period, threshold, eval_func,
                 **kwargs):
        self.period = int(period)
        self.threshold = float(threshold)
        self.eval_func = eval_func
        self.queue = dict()
        self.fired = False
        assert self.id, "Decision id not defined"
        self.channel = ChannelWrapper(self.id, 
                                      self.exchange_type, 
                                      self.on_update,
                                      publish=False,
                                      manual_ack=False,
                                      **kwargs)
        
    def connect(self, **kwargs):
        """
        Connect to queue and start consuming
        """
        # Set the signal handler to gracefully disconnect
        signal.signal(signal.SIGTERM, self._on_signal_term)
        if (hasattr(self, "channel") and self.channel):
            self.timer = RepeatingTimer(self.period, lambda: self.evaluate(**kwargs))
            self.timer.start()
            self.channel.connect(**kwargs)

    def _on_signal_term(self, signum, frame):
        LOG.info('Received signal: %s' % signum)
        self.disconnect()
        
    def disconnect(self, **kwargs):
        LOG.info('Disconnecting %s' % self.id)
        if (hasattr(self, "timer") and self.timer):
            self.timer.stop()
        if (hasattr(self, "channel") and self.channel):
            if "reason" in kwargs.keys():
                LOG.info("Disconnected with reason: %s" % kwargs['reason'])
            self.channel.stop(**kwargs)
        if (hasattr(self, "clean") and callable(self.clean)):
            self.clean()
        
    def evaluate(self, *args, **kwargs):
        if not self.eval_func:
            return

        for (sender, values) in self.queue.items():
            self.clean_queue_values(values, **kwargs)
            eval_value = self.eval_func(values.values())
            if eval_value >= self.threshold:
                if not self.fired:
                    # Fire if not already fired
                    self.fired = True
                    self.fire(sender, eval_value)
        else:
            self.fired = False

    def preprocess(self, data, **kwargs):
        return data

    def on_update(self, sender, data, **kwargs):
        LOG.info("Received data from %s:[%s] : %s" % (self.id,
                                                   sender,
                                                   data))
        self.queue.setdefault(sender, {})[datetime.now()] = self.preprocess(data, **kwargs)

    def fire(self, sender, eval_value, *args, **kwargs):
        raise NotImplementedError("This should be implemented in parent class")

    def clean_queue_values(self, values, **kwargs):
        """
        Performs a default clean, assuming the keys are timestamp values
        """
        keys = sorted(values.keys())
        for timestamp in keys:
            if (datetime.now() - timestamp).seconds > self.period:
                values.pop(timestamp)
            else:
                break

    def escalate(self, sender, receiver, data):
        # Used to escalate decision result to another queue
        try:
            if self.channel.queue:
                self.channel.queue.escalate(sender, receiver, unicode(data))
            else:
                LOG.warn("No queue found to escalate %s %s %s" % (sender,
                                                                  receiver,
                                                                  data))
        except Exception, ex:
            LOG.exception(ex)