def GET(self, deviceId, notificationId): """ Get single notification :: GET /_notifications/{deviceId}/{notificationId} Returns: :: { "uid": "e78599c4-758b-4c6e-87b1-daabaccff798", "timestamp": "2014-02-07 16:26:44", "metric": "e5511f295a474037a75bc966d02b67d2", "acknowledged": 0, "seen": 0, "windowsize": 3600, "device": "9a90eaf2-6374-4230-aa96-0830c0a737fe" } :param uid: Notification ID :type uid: str :param timestamp: Notification timestamp :type timestamp: timestamp :param metric: Metric that triggered notification :type metric: str :param acknowledged: Acknowledged status :type acknowledged: int :param seen: Seen status :type seen: int :param windowsize: Notification window in seconds during which no other notifications for a given instance should be sent to a given device :type windowsize: int :param device: Device ID :type device: str """ # Update the last_timestamp for the notification device. try: with web.ctx.connFactory() as conn: repository.updateNotificationDeviceTimestamp(conn, deviceId) except ObjectNotFoundError: return web.notfound("Notification device not found: %s" % deviceId) try: with web.ctx.connFactory() as conn: notificationRow = repository.getNotification(conn, notificationId, NotificationHandler.fields) notificationDict = dict([(col, notificationRow[col]) for col in NotificationHandler.fields]) if notificationRow["device"] != deviceId: return web.notfound("Device not found: %s" % deviceId) self.addStandardHeaders() return utils.jsonEncode(notificationDict) except ObjectNotFoundError: return web.notfound("Notification not found: %s" % notificationId)
def GET(self, deviceId, notificationId): """ Get single notification :: GET /_notifications/{deviceId}/{notificationId} Returns: :: { "uid": "e78599c4-758b-4c6e-87b1-daabaccff798", "timestamp": "2014-02-07 16:26:44", "metric": "e5511f295a474037a75bc966d02b67d2", "acknowledged": 0, "seen": 0, "windowsize": 3600, "device": "9a90eaf2-6374-4230-aa96-0830c0a737fe" } :param uid: Notification ID :type uid: str :param timestamp: Notification timestamp :type timestamp: timestamp :param metric: Metric that triggered notification :type metric: str :param acknowledged: Acknowledged status :type acknowledged: int :param seen: Seen status :type seen: int :param windowsize: Notification window in seconds during which no other notifications for a given instance should be sent to a given device :type windowsize: int :param device: Device ID :type device: str """ # Update the last_timestamp for the notification device. try: with web.ctx.connFactory() as conn: repository.updateNotificationDeviceTimestamp(conn, deviceId) except ObjectNotFoundError: return web.notfound("Notification device not found: %s" % deviceId) try: with web.ctx.connFactory() as conn: notificationRow = repository.getNotification( conn, notificationId, NotificationHandler.fields) notificationDict = dict([(col, notificationRow[col]) for col in NotificationHandler.fields]) if notificationRow["device"] != deviceId: return web.notfound("Device not found: %s" % deviceId) self.addStandardHeaders() return utils.jsonEncode(notificationDict) except ObjectNotFoundError: return web.notfound("Notification not found: %s" % notificationId)
def messageHandler(self, message): """ Inspect all inbound model results in a batch for anomaly thresholds and trigger notifications where applicable. :param amqp.messages.ConsumerMessage message: ``message.body`` is a serialized batch of model inference results generated in ``AnomalyService`` and must be deserialized using ``AnomalyService.deserializeModelResult()``. The message conforms to htmengine/runtime/json_schema/model_inference_results_msg_schema.json """ if message.properties.headers and "dataType" in message.properties.headers: # Not a model inference result return YOMP.app.config.loadConfig() # reload config on every batch engine = repository.engineFactory() # Cache minimum threshold to trigger any notification to avoid permuting # settings x metricDataRows try: try: batch = AnomalyService.deserializeModelResult(message.body) except Exception: self._log.exception("Error deserializing model result") raise # Load all settings for all users (once per incoming batch) with engine.connect() as conn: settings = repository.retryOnTransientErrors(repository.getAllNotificationSettings)(conn) self._log.debug("settings: %r" % settings) if settings: minThreshold = min(setting.sensitivity for setting in settings) else: minThreshold = 0.99999 metricInfo = batch["metric"] metricId = metricInfo["uid"] resource = metricInfo["resource"] for row in batch["results"]: if row["anomaly"] >= minThreshold: for settingObj in settings: if row["rowid"] <= 1000: continue # Not enough data rowDatetime = datetime.utcfromtimestamp(row["ts"]) if rowDatetime < datetime.utcnow() - timedelta(seconds=3600): continue # Skip old if row["anomaly"] >= settingObj.sensitivity: # First let's clear any old users out of the database. with engine.connect() as conn: repository.retryOnTransientErrors(repository.deleteStaleNotificationDevices)( conn, _NOTIFICATION_DEVICE_STALE_DAYS ) # If anomaly_score meets or exceeds any of the device # notification sensitivity settings, trigger notification. # repository.addNotification() will handle throttling. notificationId = str(uuid.uuid4()) with engine.connect() as conn: result = repository.retryOnTransientErrors(repository.addNotification)( conn, uid=notificationId, server=resource, metric=metricId, rowid=row["rowid"], device=settingObj.uid, windowsize=(settingObj.windowsize), timestamp=rowDatetime, acknowledged=0, seen=0, ) self._log.info( "NOTIFICATION=%s SERVER=%s METRICID=%s DEVICE=%s " "Notification generated. " % (notificationId, resource, metricId, settingObj.uid) ) if result is not None and result.rowcount > 0 and settingObj.email_addr: # Notification was generated. Attempt to send email with engine.connect() as conn: notificationObj = repository.getNotification(conn, notificationId) self.sendNotificationEmail(engine, settingObj, notificationObj) if not settings: # There are no device notification settings stored on this server, # no notifications will be generated. However, log that a # an anomaly was detected and notification would be sent if there # were any configured devices self._log.info( "<%r>" % (metricInfo) + ("{TAG:APP.NOTIFICATION} Anomaly " "detected at %s, but no devices are " "configured.") % rowDatetime ) finally: message.ack() # Do cleanup with engine.connect() as conn: repository.clearOldNotifications(conn) # Delete all notifications outside