def persistent(self, interval): timer = Timer(interval, single(), persist=True) timer.register(self) yield sleep(interval * 10) timer.unregister()
def test_datetime(app): now = datetime.now() d = now + timedelta(seconds=0.1) timer = Timer(d, test(), "timer") timer.register(app) assert pytest.wait_for(app, "flag") app.reset()
def _start_auto_refresh(self, incident): # Get the interval from the avalon_auto_refresh_time field refresh_interval_minutes = self.actions.res.incident_get_avalon_auto_refresh_time( incident) # default to 60 minutes if not set in IBM Resilient if not refresh_interval_minutes: refresh_interval_minutes = 60 # create timer incident_id = incident["id"] auto_refresh_event = auto_refresh(incident_id) timer = Timer(60 * refresh_interval_minutes, auto_refresh_event, self.channel, persist=True) # keep timer in the incident map self.incident_timer[incident_id] = timer # start ticking timer.register(self) # fire event once to refresh nodes now self.fire(auto_refresh_event)
def test_persistentTimer(app): app.timestamps.append(time.time()) timer = Timer(0.2, test(), "timer", persist=True) timer.register(app) wait_res = pytest.wait_for(app, "count", 2) assert app.count >= 2 assert wait_res delta = app.timestamps[1] - app.timestamps[0] # Should be 0.1, but varies depending on timer precision and load assert delta >= 0.08 and delta < 0.5 delta = app.timestamps[2] - app.timestamps[1] assert delta >= 0.08 and delta < 0.5 app.reset() timer.unregister()
def decorated(itself, event, *args, **kwargs): """the decorated function""" LOG.debug("decorated") # De-bounce messages for this event and the same key: # (key is the incident-id, by default): # - Don't handle the message immediately. # - Note that we have a deferred event. # - Defer it for <<delay>>. # - If an event arrives and there is any deferred message, # - Reset the timer interval to <<delay>> # - Optionally: throw away the new message. # Otherwise: defer this one too (to be processed # immediately after the first deferred message). key = self.get_key(event) if event.deferred: # We deferred this event earlier, # and now it has fired without being reset in the meantime. # All the pending events are OK to go! Forget their timers! LOG.info("Handling deferred %s", key) event.deferred = False self.debouncedata.pop(key, None) else: # This is a new event. # Are there any other deferred events for this [action+incident]? if key not in self.debouncedata: # We'll keep a list of all the timers self.debouncedata[key] = [] else: # Duplicate event if self.discard: # Unregister all the previous timers so they don't fire for timer in self.debouncedata[key]: evt = timer.event LOG.debug("Unregister timer") timer.unregister() if evt: # The timer's event will not fire now. # Mark it as not 'deferred' and fire a 'success' child event # so that it gets ack'd to the message queue. LOG.debug("Fire success") evt.deferred = False channels = getattr(evt, "success_channels", evt.channels) itself.fire( evt.child("success", evt, evt.value.value), *channels) # Now we can get rid of the list of timers self.debouncedata[key] = [] else: # Reset all the pending timers for timer in self.debouncedata[key]: timer.reset(interval=self.delay) # Defer this new event with a timer. LOG.info("Deferring %s", key) timer = Timer(self.delay, event) timer.register(itself) event.deferred = True # Remember the new timer so that we can reset it if necessary self.debouncedata[key].append(timer) # We're done until the timer fires return return func(itself, event, *args, **kwargs)
class StompClient(BaseComponent): """ Send and Receive messages from a STOMP queue """ channel = "stomp" def init(self, host, port, username=None, password=None, connect_timeout=3, connected_timeout=3, version=StompSpec.VERSION_1_2, accept_versions=["1.0", "1.1", "1.2"], heartbeats=(0, 0), ssl_context=None, use_ssl=True, key_file=None, cert_file=None, ca_certs=None, ssl_version=ssl.PROTOCOL_SSLv23, key_file_password=None, proxy_host=None, proxy_port=None, proxy_user=None, proxy_password=None, channel=channel): """ Initialize StompClient. Called after __init__ """ self.channel = channel if proxy_host: LOG.info("Connect to %s:%s through proxy %s:%d", host, port, proxy_host, proxy_port) else: LOG.info("Connect to %s:%s", host, port) if use_ssl and not ssl_context: ssl_params = dict(key_file=key_file, cert_file=cert_file, ca_certs=ca_certs, ssl_version=ssl_version, password=key_file_password) LOG.info("Request to use old-style socket wrapper: %s", ssl_params) ssl_context = ssl_params if use_ssl: uri = "ssl://%s:%s" % (host, port) else: uri = "tcp://%s:%s" % (host, port) # Configure failover options so it only tries to connect once self._stomp_server = "failover:(%s)?maxReconnectAttempts=1,startupMaxReconnectAttempts=1" % uri self._stomp_config = StompConfig(uri=self._stomp_server, sslContext=ssl_context, version=version, login=username, passcode=password) self._heartbeats = heartbeats self._accept_versions = accept_versions self._connect_timeout = connect_timeout self._connected_timeout = connected_timeout Stomp._transportFactory = EnhancedStompFrameTransport Stomp._transportFactory.proxy_host = proxy_host Stomp._transportFactory.proxy_port = proxy_port Stomp._transportFactory.proxy_user = proxy_user Stomp._transportFactory.proxy_password = proxy_password self._client = Stomp(self._stomp_config) self._subscribed = {} self.server_heartbeat = None self.client_heartbeat = None self.ALLOWANCE = 2 # multiplier for heartbeat timeouts @property def connected(self): if self._client.session: return self._client.session.state == StompSession.CONNECTED else: return False @property def subscribed(self): return self._subscribed.keys() @property def stomp_logger(self): return LOG_CATEGORY @handler("disconnect") def _disconnect(self, receipt=None): if self.connected: self._client.disconnect(receipt=receipt) self._client.close(flush=True) self.fire(disconnected(reconnect=False)) self._subscribed = {} return "disconnected" def start_heartbeats(self): LOG.info("Client HB: %s Server HB: %s", self._client.clientHeartBeat, self._client.serverHeartBeat) if self._client.clientHeartBeat: if self.client_heartbeat: # Timer already exists, just reset it self.client_heartbeat.reset() else: LOG.info("Client will send heartbeats to server") # Send heartbeats at 80% of agreed rate self.client_heartbeat = Timer((self._client.clientHeartBeat / 1000.0) * 0.8, client_heartbeat(), persist=True) self.client_heartbeat.register(self) else: LOG.info("No Client heartbeats will be sent") if self._client.serverHeartBeat: if self.server_heartbeat: # Timer already exists, just reset it self.server_heartbeat.reset() else: LOG.info("Requested heartbeats from server.") # Allow a grace period on server heartbeats self.server_heartbeat = Timer((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE, server_heartbeat(), persist=True) self.server_heartbeat.register(self) else: LOG.info("Expecting no heartbeats from Server") @handler("connect") def connect(self, event, host=None, *args, **kwargs): """ connect to Stomp server """ LOG.info("Connect to Stomp...") try: self._client.connect(heartBeats=self._heartbeats, host=host, versions=self._accept_versions, connectTimeout=self._connect_timeout, connectedTimeout=self._connected_timeout) LOG.info("State after Connection Attempt: %s", self._client.session.state) if self.connected: LOG.info("Connected to %s", self._stomp_server) self.fire(connected()) self.start_heartbeats() return "success" except StompConnectionError: LOG.debug(traceback.format_exc()) self.fire(connection_failed(self._stomp_server)) event.success = False return "fail" @handler("server_heartbeat") def check_server_heartbeat(self, event): """ Confirm that heartbeat from server hasn't timed out """ now = time.time() last = self._client.lastReceived or 0 if last: elapsed = now - last else: elapsed = -1 LOG.debug("Last received data %d seconds ago", elapsed) if ((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE + last) < now: LOG.error("Server heartbeat timeout. %d seconds since last heartbeat. Disconnecting.", elapsed) event.success = False self.fire(heartbeat_timeout()) if self.connected: self._client.disconnect() # TODO: Try to auto-reconnect? @handler("client_heartbeat") def send_heartbeat(self, event): if self.connected: LOG.debug("Sending heartbeat") try: self._client.beat() except StompConnectionError: event.success = False self.fire(disconnected()) @handler("generate_events") def generate_events(self, event): if not self.connected: return try: if self._client.canRead(1): frame = self._client.receiveFrame() LOG.debug("Recieved frame %s", frame) self.fire(message(frame)) except StompConnectionError: self.fire(disconnected()) @handler("send") def send(self, event, destination, body, headers=None, receipt=None): LOG.debug("send()") if not self.connected: LOG.error("Can't send when Stomp is disconnected") self.fire(on_stomp_error(None, Exception("Message send attempted with stomp disconnected"))) event.success = False return try: self._client.send(destination, body=body.encode('utf-8'), headers=headers, receipt=receipt) LOG.debug("Message sent") except StompConnectionError as err: event.success = False self.fire(disconnected()) except StompError as err: LOG.error("Error sending ack") event.success = False self.fire(on_stomp_error(None, err)) @handler("subscribe") def _subscribe(self, event, destination, ack=ACK_CLIENT_INDIVIDUAL): if ack not in ACK_MODES: raise ValueError("Invalid client ack mode specified") LOG.info("Subscribe to message destination %s", destination) try: # Set ID to match destination name for easy reference later frame, token = self._client.subscribe(destination, headers={StompSpec.ACK_HEADER: ack, 'id': destination}) self._subscribed[destination] = token except StompConnectionError as err: event.success = False self.fire(disconnected()) except StompError as err: event.success = False LOG.debug(traceback.format_exc()) self.fire(on_stomp_error(None, err)) @handler("unsubscribe") def _unsubscribe(self, event, destination): if destination not in self._subscribed: LOG.error("Unsubscribe Request Ignored. Not subscribed to %s", destination) return try: token = self._subscribed.pop(destination) frame = self._client.unsubscribe(token) LOG.debug("Unsubscribed: %s", frame) except StompConnectionError as err: event.success = False self.fire(disconnected()) except StompError as err: LOG.error("Error sending ack") event.success = False self.fire(on_stomp_error(frame, err)) @handler("message") def on_message(self, event, headers, message): LOG.info("Stomp message received") @handler("ack") def ack_frame(self, event, frame): LOG.debug("ack_frame()") try: self._client.ack(frame) LOG.debug("Ack Sent") except StompConnectionError as err: LOG.error("Error sending ack") event.success = False self.fire(disconnected()) except StompError as err: LOG.error("Error sending ack") event.success = False self.fire(on_stomp_error(frame, err)) def get_subscription(self, frame): """ Get subscription from frame """ LOG.info(self._subscribed) _, token = self._client.message(frame) return self._subscribed[token]
class StompClient(BaseComponent): channel = "stomp" def init( self, host, port, username=None, password=None, connect_timeout=3, connected_timeout=3, version=StompSpec.VERSION_1_2, accept_versions=["1.0", "1.1", "1.2"], heartbeats=(0, 0), ssl_context=None, use_ssl=True, key_file=None, cert_file=None, ca_certs=None, ssl_version=ssl.PROTOCOL_SSLv23, # means 'latest available' key_file_password=None, proxy_host=None, proxy_port=None, proxy_user=None, proxy_password=None, channel=channel): """ Initialize StompClient. Called after __init__ """ self.channel = channel if proxy_host: LOG.info("Connect to %s:%s through proxy %s:%d", host, port, proxy_host, proxy_port) else: LOG.info("Connect to %s:%s", host, port) if use_ssl and not ssl_context: ssl_params = dict(key_file=key_file, cert_file=cert_file, ca_certs=ca_certs, ssl_version=ssl_version, password=key_file_password) LOG.info("Request to use old-style socket wrapper: %s", ssl_params) ssl_context = ssl_params if use_ssl: uri = "ssl://%s:%s" % (host, port) else: uri = "tcp://%s:%s" % (host, port) # Configure failover options so it only tries to connect once self._stomp_server = "failover:(%s)?maxReconnectAttempts=1,startupMaxReconnectAttempts=1" % uri self._stomp_config = StompConfig(uri=self._stomp_server, sslContext=ssl_context, version=version, login=username, passcode=password) self._heartbeats = heartbeats self._accept_versions = accept_versions self._connect_timeout = connect_timeout self._connected_timeout = connected_timeout Stomp._transportFactory = EnhancedStompFrameTransport Stomp._transportFactory.proxy_host = proxy_host Stomp._transportFactory.proxy_port = proxy_port Stomp._transportFactory.proxy_user = proxy_user Stomp._transportFactory.proxy_password = proxy_password self._client = Stomp(self._stomp_config) self._subscribed = {} self.server_heartbeat = None self.client_heartbeat = None self.last_heartbeat = 0 self.ALLOWANCE = 2 # multiplier for heartbeat timeouts @property def connected(self): if self._client.session: return self._client.session.state == StompSession.CONNECTED else: return False @property def socket_connected(self): try: if self._client._transport: return True except: pass return False @property def subscribed(self): return self._subscribed.keys() @property def stomp_logger(self): return LOG_CATEGORY @handler("Disconnect") def _disconnect(self, receipt=None, flush=True, reconnect=False): try: if flush: self._subscribed = {} if self.connected: self._client.disconnect(receipt=receipt) except Exception as e: LOG.error("Failed to disconnect client") try: self.fire(Disconnected(reconnect=reconnect)) self._client.close(flush=flush) except Exception as e: LOG.error("Failed to close client connection") return "disconnected" def start_heartbeats(self): LOG.info("Client HB: %s Server HB: %s", self._client.clientHeartBeat, self._client.serverHeartBeat) if self._client.clientHeartBeat: if self.client_heartbeat: # Timer already exists, just reset it self.client_heartbeat.reset() else: LOG.info("Client will send heartbeats to server") # Send heartbeats at 80% of agreed rate self.client_heartbeat = Timer( (self._client.clientHeartBeat / 1000.0) * 0.8, ClientHeartbeat(), persist=True) self.client_heartbeat.register(self) else: LOG.info("No Client heartbeats will be sent") if self._client.serverHeartBeat: if self.server_heartbeat: # Timer already exists, just reset it self.server_heartbeat.reset() else: LOG.info("Requested heartbeats from server.") # Allow a grace period on server heartbeats self.server_heartbeat = Timer( (self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE, ServerHeartbeat(), persist=True) self.server_heartbeat.register(self) else: LOG.info("Expecting no heartbeats from Server") @handler("Connect") def connect(self, event, host=None, *args, **kwargs): """ connect to Stomp server """ LOG.info("Connect to Stomp...") try: self._client.connect(heartBeats=self._heartbeats, host=host, versions=self._accept_versions, connectTimeout=self._connect_timeout, connectedTimeout=self._connected_timeout) LOG.debug("State after Connection Attempt: %s", self._client.session.state) if self.connected: LOG.info("Connected to %s", self._stomp_server) self.fire(Connected()) self.start_heartbeats() return "success" except StompConnectionError as err: LOG.debug(traceback.format_exc()) self.fire(ConnectionFailed(self._stomp_server)) event.success = False return "fail" @handler("ServerHeartbeat") def check_server_heartbeat(self, event): """ Confirm that heartbeat from server hasn't timed out """ now = time.time() self.last_heartbeat = self._client.lastReceived or self.last_heartbeat if self.last_heartbeat: elapsed = now - self.last_heartbeat else: elapsed = -1 if ((self._client.serverHeartBeat / 1000.0) * self.ALLOWANCE + self.last_heartbeat) < now: LOG.error( "Server heartbeat timeout. %d seconds since last heartbeat.", elapsed) event.success = False self.fire(HeartbeatTimeout()) @handler("ClientHeartbeat") def send_heartbeat(self, event): if self.connected: LOG.debug("Sending heartbeat") try: self._client.beat() except (StompConnectionError, StompError) as err: event.success = False self.fire(OnStompError(None, err)) @handler("generate_events") def generate_events(self, event): event.reduce_time_left(0.1) if not self.connected: return try: if self._client.canRead(0): frame = self._client.receiveFrame() LOG.debug("Recieved frame %s", frame) if frame.command == StompSpec.ERROR: self.fire(OnStompError(frame, None)) else: self.fire(Message(frame)) except (StompConnectionError, StompError) as err: LOG.error("Failed attempt to generate events.") self.fire(OnStompError(None, err)) @handler("Send") def send(self, event, destination, body, headers=None, receipt=None): LOG.debug("send()") try: self._client.send(destination, body=body.encode('utf-8'), headers=headers, receipt=receipt) LOG.debug("Message sent") except (StompConnectionError, StompError) as err: LOG.error("Error sending frame") event.success = False self.fire(OnStompError(None, err)) raise # To fire Send_failure event @handler("Subscribe") def _subscribe(self, event, destination, additional_headers=None, ack=ACK_CLIENT_INDIVIDUAL): if ack not in ACK_MODES: raise ValueError("Invalid client ack mode specified") if destination in self._client.session._subscriptions: LOG.debug("Ignoring subscribe request to %s. Already subscribed.", destination) LOG.info("Subscribe to message destination %s", destination) try: headers = {StompSpec.ACK_HEADER: ack, 'id': destination} if additional_headers: headers.update(additional_headers) # Set ID to match destination name for easy reference later frame, token = self._client.subscribe(destination, headers) self._subscribed[destination] = token except (StompConnectionError, StompError) as err: LOG.error("Failed to subscribe to queue.") event.success = False LOG.debug(traceback.format_exc()) self.fire(OnStompError(None, err)) @handler("Unsubscribe") def _unsubscribe(self, event, destination): if destination not in self._subscribed: LOG.error("Unsubscribe Request Ignored. Not subscribed to %s", destination) return try: token = self._subscribed.pop(destination) frame = self._client.unsubscribe(token) LOG.debug("Unsubscribed: %s", frame) except (StompConnectionError, StompError) as err: event.success = False LOG.error("Unsubscribe Failed.") self.fire(OnStompError(frame, err)) @handler("Message") def on_message(self, event, headers, message): LOG.debug("Stomp message received") @handler("Ack") def ack_frame(self, event, frame): LOG.debug("ack_frame()") try: self._client.ack(frame) LOG.debug("Ack Sent") except (StompConnectionError, StompError) as err: LOG.error("Error sending ack") event.success = False self.fire(OnStompError(frame, err)) raise # To fire Ack_failure event def get_subscription(self, frame): """ Get subscription from frame """ _, token = self._client.message(frame) return self._subscribed[token]
def test_timer(app): timer = Timer(0.1, test(), "timer") timer.register(app) assert pytest.wait_for(app, "flag") app.reset()