Exemplo n.º 1
0
  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)
Exemplo n.º 2
0
  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
      message.ack()
      return

    htm-it.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:
          rowDatetime = datetime.utcfromtimestamp(row["ts"])

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

          for settingObj in settings:
            if row["rowid"] <= 1000:
              continue # Not enough data


            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)
    finally:
      message.ack()

    # Do cleanup
    with engine.connect() as conn:
      repository.clearOldNotifications(conn) # Delete all notifications outside