Merge branch 'collectd-4.2' into collectd-4.3
[collectd.git] / src / utils_threshold.c
index 15ccf9c..778b40b 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/utils_threshold.c
- * Copyright (C) 2007  Florian octo Forster
+ * Copyright (C) 2007,2008  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -41,8 +41,10 @@ typedef struct threshold_s
   char plugin_instance[DATA_MAX_NAME_LEN];
   char type[DATA_MAX_NAME_LEN];
   char type_instance[DATA_MAX_NAME_LEN];
-  gauge_t min;
-  gauge_t max;
+  gauge_t warning_min;
+  gauge_t warning_max;
+  gauge_t failure_min;
+  gauge_t failure_max;
   int flags;
 } threshold_t;
 /* }}} */
@@ -138,12 +140,15 @@ static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    WARNING ("threshold values: The `Max' option needs exactly one "
-       "number argument.");
+    WARNING ("threshold values: The `%s' option needs exactly one "
+       "number argument.", ci->key);
     return (-1);
   }
 
-  th->max = ci->values[0].value.number;
+  if (strcasecmp (ci->key, "WarningMax") == 0)
+    th->warning_max = ci->values[0].value.number;
+  else
+    th->failure_max = ci->values[0].value.number;
 
   return (0);
 } /* int ut_config_type_max */
@@ -153,12 +158,15 @@ static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
   if ((ci->values_num != 1)
       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
   {
-    WARNING ("threshold values: The `Min' option needs exactly one "
-       "number argument.");
+    WARNING ("threshold values: The `%s' option needs exactly one "
+       "number argument.", ci->key);
     return (-1);
   }
 
-  th->min = ci->values[0].value.number;
+  if (strcasecmp (ci->key, "WarningMin") == 0)
+    th->warning_min = ci->values[0].value.number;
+  else
+    th->failure_min = ci->values[0].value.number;
 
   return (0);
 } /* int ut_config_type_min */
@@ -223,8 +231,10 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
   strncpy (th.type, ci->values[0].value.string, sizeof (th.type));
   th.type[sizeof (th.type) - 1] = '\0';
 
-  th.min = NAN;
-  th.max = NAN;
+  th.warning_min = NAN;
+  th.warning_max = NAN;
+  th.failure_min = NAN;
+  th.failure_max = NAN;
 
   for (i = 0; i < ci->children_num; i++)
   {
@@ -233,9 +243,11 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
 
     if (strcasecmp ("Instance", option->key) == 0)
       status = ut_config_type_instance (&th, option);
-    else if (strcasecmp ("Max", option->key) == 0)
+    else if ((strcasecmp ("WarningMax", option->key) == 0)
+       || (strcasecmp ("FailureMax", option->key) == 0))
       status = ut_config_type_max (&th, option);
-    else if (strcasecmp ("Min", option->key) == 0)
+    else if ((strcasecmp ("WarningMin", option->key) == 0)
+       || (strcasecmp ("FailureMin", option->key) == 0))
       status = ut_config_type_min (&th, option);
     else if (strcasecmp ("Invert", option->key) == 0)
       status = ut_config_type_invert (&th, option);
@@ -398,8 +410,10 @@ int ut_config (const oconfig_item_t *ci)
   }
 
   memset (&th, '\0', sizeof (th));
-  th.min = NAN;
-  th.max = NAN;
+  th.warning_min = NAN;
+  th.warning_max = NAN;
+  th.failure_min = NAN;
+  th.failure_max = NAN;
     
   for (i = 0; i < ci->children_num; i++)
   {
@@ -495,12 +509,24 @@ static threshold_t *threshold_search (const data_set_t *ds,
 
 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
 {
+  notification_t n;
   threshold_t *th;
   gauge_t *values;
   int i;
 
+  int state_orig;
+  int state_new = STATE_OKAY;
+  int ds_index = 0;
+
+  char *buf;
+  size_t bufsize;
+  int status;
+
   if (threshold_tree == NULL)
     return (0);
+
+  /* Is this lock really necessary? So far, thresholds are only inserted at
+   * startup. -octo */
   pthread_mutex_lock (&threshold_lock);
   th = threshold_search (ds, vl);
   pthread_mutex_unlock (&threshold_lock);
@@ -513,95 +539,143 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
   if (values == NULL)
     return (0);
 
+  state_orig = uc_get_state (ds, vl);
+
   for (i = 0; i < ds->ds_num; i++)
   {
-    int out_of_range = 0;
     int is_inverted = 0;
+    int is_warning = 0;
+    int is_failure = 0;
 
     if ((th->flags & UT_FLAG_INVERT) != 0)
+    {
       is_inverted = 1;
-    if ((!isnan (th->min) && (th->min > values[i]))
-       || (!isnan (th->max) && (th->max < values[i])))
-      out_of_range = 1;
-
-    /* If only one of these conditions is true, there is a problem */
-    if ((out_of_range + is_inverted) == 1)
+      is_warning--;
+      is_failure--;
+    }
+    if ((!isnan (th->failure_min) && (th->failure_min > values[i]))
+       || (!isnan (th->failure_max) && (th->failure_max < values[i])))
+      is_failure++;
+    if ((!isnan (th->warning_min) && (th->warning_min > values[i]))
+       || (!isnan (th->warning_max) && (th->warning_max < values[i])))
+      is_warning++;
+
+    if ((is_failure != 0) && (state_new != STATE_ERROR))
+    {
+      state_new = STATE_ERROR;
+      ds_index = i;
+    }
+    else if ((is_warning != 0)
+       && (state_new != STATE_ERROR)
+       && (state_new != STATE_WARNING))
     {
-      notification_t n;
-      char *buf;
-      size_t bufsize;
-      int status;
+      state_new = STATE_WARNING;
+      ds_index = i;
+    }
+  }
 
-      WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
-         ds->ds[i].name, th->min, values[i], th->max,
-         is_inverted ? "true" : "false");
+  if (state_new != state_orig)
+    uc_set_state (ds, vl, state_new);
 
-      /* Copy the associative members */
-      NOTIFICATION_INIT_VL (&n, vl, ds);
+  /* Return here if we're not going to send a notification */
+  if ((state_new == state_orig)
+      && ((state_new == STATE_OKAY)
+       || ((th->flags & UT_FLAG_PERSIST) == 0)))
+  {
+    sfree (values);
+    return (0);
+  }
 
+  NOTIFICATION_INIT_VL (&n, vl, ds);
+  {
+    /* Copy the associative members */
+    if (state_new == STATE_OKAY)
+      n.severity = NOTIF_OKAY;
+    else if (state_new == STATE_WARNING)
+      n.severity = NOTIF_WARNING;
+    else
       n.severity = NOTIF_FAILURE;
-      n.time = vl->time;
 
-      buf = n.message;
-      bufsize = sizeof (n.message);
+    n.time = vl->time;
+
+    buf = n.message;
+    bufsize = sizeof (n.message);
 
-      status = snprintf (buf, bufsize, "Host %s, plugin %s",
-         vl->host, vl->plugin);
+    status = snprintf (buf, bufsize, "Host %s, plugin %s",
+       vl->host, vl->plugin);
+    buf += status;
+    bufsize -= status;
+
+    if (vl->plugin_instance[0] != '\0')
+    {
+      status = snprintf (buf, bufsize, " (instance %s)",
+         vl->plugin_instance);
       buf += status;
       bufsize -= status;
+    }
 
-      if (vl->plugin_instance[0] != '\0')
-      {
-       status = snprintf (buf, bufsize, " (instance %s)",
-           vl->plugin_instance);
-       buf += status;
-       bufsize -= status;
-      }
+    status = snprintf (buf, bufsize, " type %s", ds->type);
+    buf += status;
+    bufsize -= status;
 
-      status = snprintf (buf, bufsize, " type %s", ds->type);
+    if (vl->type_instance[0] != '\0')
+    {
+      status = snprintf (buf, bufsize, " (instance %s)",
+         vl->type_instance);
       buf += status;
       bufsize -= status;
+    }
+  }
 
-      if (vl->type_instance[0] != '\0')
-      {
-       status = snprintf (buf, bufsize, " (instance %s)",
-           vl->type_instance);
-       buf += status;
-       bufsize -= status;
-      }
+  /* Send a okay notification */
+  if (state_new == STATE_OKAY)
+  {
+    status = snprintf (buf, bufsize, ": All data sources are within range again.");
+    buf += status;
+    bufsize -= status;
+  }
+  else
+  {
+    double min;
+    double max;
+
+    min = (state_new == STATE_ERROR) ? th->failure_min : th->warning_min;
+    max = (state_new == STATE_ERROR) ? th->failure_max : th->warning_max;
 
-      if (is_inverted)
+    if (th->flags & UT_FLAG_INVERT)
+    {
+      if (!isnan (min) && !isnan (max))
       {
-       if (!isnan (th->min) && !isnan (th->max))
-       {
-         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
-             "%lf. That is within the critical region of %lf and %lf.",
-             ds->ds[i].name, values[i],
-             th->min, th->min);
-       }
-       else
-       {
-         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
-             "%lf. That is %s the configured threshold of %lf.",
-             ds->ds[i].name, values[i],
-             isnan (th->min) ? "below" : "above",
-             isnan (th->min) ? th->max : th->min);
-       }
+       status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+           "%f. That is within the %s region of %f and %f.",
+           ds->ds[ds_index].name, values[ds_index],
+           (state_new == STATE_ERROR) ? "failure" : "warning",
+           min, min);
       }
-      else /* (!is_inverted) */
+      else
       {
        status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
-           "%lf. That is %s the configured threshold of %lf.",
-           ds->ds[i].name, values[i],
-           (values[i] < th->min) ? "below" : "above",
-           (values[i] < th->min) ? th->min : th->max);
+           "%f. That is %s the %s threshold of %f.",
+           ds->ds[ds_index].name, values[ds_index],
+           isnan (min) ? "below" : "above",
+           (state_new == STATE_ERROR) ? "failure" : "warning",
+           isnan (min) ? max : min);
       }
-      buf += status;
-      bufsize -= status;
-
-      plugin_dispatch_notification (&n);
     }
-  } /* for (i = 0; i < ds->ds_num; i++) */
+    else /* is not inverted */
+    {
+      status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+         "%f. That is %s the %s threshold of %f.",
+         ds->ds[ds_index].name, values[ds_index],
+         (values[ds_index] < min) ? "below" : "above",
+         (state_new == STATE_ERROR) ? "failure" : "warning",
+         (values[ds_index] < min) ? min : max);
+    }
+    buf += status;
+    bufsize -= status;
+  }
+
+  plugin_dispatch_notification (&n);
 
   sfree (values);