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")
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 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)
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)