contrib/exec-nagios.px: Added a Perl script which handles Nagios plugins.
[collectd.git] / src / utils_threshold.c
index 1c9ccdd..ea4b591 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
@@ -31,6 +31,9 @@
 /*
  * Private data structures
  * {{{ */
+#define UT_FLAG_INVERT  0x01
+#define UT_FLAG_PERSIST 0x02
+
 typedef struct threshold_s
 {
   char host[DATA_MAX_NAME_LEN];
@@ -38,16 +41,18 @@ 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;
-  int invert;
+  gauge_t warning_min;
+  gauge_t warning_max;
+  gauge_t failure_min;
+  gauge_t failure_max;
+  int flags;
 } threshold_t;
 /* }}} */
 
 /*
  * Private (static) variables
  * {{{ */
-static avl_tree_t     *threshold_tree = NULL;
+static c_avl_tree_t   *threshold_tree = NULL;
 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
 /* }}} */
 
@@ -91,12 +96,12 @@ static int ut_threshold_add (const threshold_t *th)
   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
 
   pthread_mutex_lock (&threshold_lock);
-  status = avl_insert (threshold_tree, name_copy, th_copy);
+  status = c_avl_insert (threshold_tree, name_copy, th_copy);
   pthread_mutex_unlock (&threshold_lock);
 
   if (status != 0)
   {
-    ERROR ("ut_threshold_add: avl_insert (%s) failed.", name);
+    ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
     sfree (name_copy);
     sfree (th_copy);
   }
@@ -135,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_min = ci->values[0].value.number;
+  else
+    th->failure_min = ci->values[0].value.number;
 
   return (0);
 } /* int ut_config_type_max */
@@ -150,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 */
@@ -170,11 +181,32 @@ static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
     return (-1);
   }
 
-  th->invert = (ci->values[0].value.boolean) ? 1 : 0;
+  if (ci->values[0].value.boolean)
+    th->flags |= UT_FLAG_INVERT;
+  else
+    th->flags &= ~UT_FLAG_INVERT;
 
   return (0);
 } /* int ut_config_type_invert */
 
+static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
+{
+  if ((ci->values_num != 1)
+      || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+  {
+    WARNING ("threshold values: The `Persist' option needs exactly one "
+       "boolean argument.");
+    return (-1);
+  }
+
+  if (ci->values[0].value.boolean)
+    th->flags |= UT_FLAG_PERSIST;
+  else
+    th->flags &= ~UT_FLAG_PERSIST;
+
+  return (0);
+} /* int ut_config_type_persist */
+
 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
 {
   int i;
@@ -199,6 +231,11 @@ 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.warning_min = NAN;
+  th.warning_max = NAN;
+  th.failure_min = NAN;
+  th.failure_max = NAN;
+
   for (i = 0; i < ci->children_num; i++)
   {
     oconfig_item_t *option = ci->children + i;
@@ -206,12 +243,16 @@ 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);
+    else if (strcasecmp ("Persist", option->key) == 0)
+      status = ut_config_type_persist (&th, option);
     else
     {
       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
@@ -351,19 +392,28 @@ int ut_config (const oconfig_item_t *ci)
 
   threshold_t th;
 
+  if (ci->values_num != 0)
+  {
+    ERROR ("threshold values: The `Threshold' block may not have any "
+       "arguments.");
+    return (-1);
+  }
+
   if (threshold_tree == NULL)
   {
-    threshold_tree = avl_create ((void *) strcmp);
+    threshold_tree = c_avl_create ((void *) strcmp);
     if (threshold_tree == NULL)
     {
-      ERROR ("ut_config: avl_create failed.");
+      ERROR ("ut_config: c_avl_create failed.");
       return (-1);
     }
   }
 
   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++)
   {
@@ -406,7 +456,7 @@ static threshold_t *threshold_get (const char *hostname,
       (type == NULL) ? "" : type, type_instance);
   name[sizeof (name) - 1] = '\0';
 
-  if (avl_get (threshold_tree, name, (void *) &th) == 0)
+  if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
     return (th);
   else
     return (NULL);
@@ -465,13 +515,15 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
 
   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);
   if (th == NULL)
     return (0);
 
-  DEBUG ("Found matching threshold");
+  DEBUG ("ut_check_threshold: Found matching threshold");
 
   values = uc_get_rate (ds, vl);
   if (values == NULL)
@@ -479,15 +531,41 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
 
   for (i = 0; i < ds->ds_num; i++)
   {
-    if ((th->min > values[i]) || (th->max < values[i]))
+    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->failure_min) && (th->failure_min > values[i]))
+       || (!isnan (th->failure_max) && (th->failure_max < values[i])))
+      is_failure = is_inverted - 1;
+    if ((!isnan (th->warning_min) && (th->warning_min > values[i]))
+       || (!isnan (th->warning_max) && (th->warning_max < values[i])))
+      is_warning = is_inverted - 1;
+
+    if ((is_failure != 0) || (is_warning != 0))
     {
       notification_t n;
       char *buf;
       size_t bufsize;
       int status;
 
-      WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf",
-         ds->ds[i].name, th->min, values[i], th->max);
+      double min;
+      double max;
+
+      min = (is_failure != 0) ? th->failure_min : th->warning_min;
+      max = (is_failure != 0) ? th->failure_max : th->warning_max;
+
+      DEBUG ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
+         ds->ds[i].name, min, values[i], max,
+         is_inverted ? "true" : "false");
+
+      /* Copy the associative members */
+      NOTIFICATION_INIT_VL (&n, vl, ds);
+
+      n.severity = (is_failure != 0) ? NOTIF_FAILURE : NOTIF_WARNING;
+      n.time = vl->time;
 
       buf = n.message;
       bufsize = sizeof (n.message);
@@ -517,20 +595,38 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
        bufsize -= status;
       }
 
-      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);
+      if (is_inverted)
+      {
+       if (!isnan (min) && !isnan (max))
+       {
+         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+             "%f. That is within the %s region of %f and %f.",
+             ds->ds[i].name, values[i],
+             (is_failure != 0) ? "failure" : "warning",
+             min, min);
+       }
+       else
+       {
+         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+             "%f. That is %s the %s threshold of %f.",
+             ds->ds[i].name, values[i],
+             isnan (min) ? "below" : "above",
+             (is_failure != 0) ? "failure" : "warning",
+             isnan (min) ? max : min);
+       }
+      }
+      else /* (!is_inverted) */
+      {
+       status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
+           "%f. That is %s the %s threshold of %f.",
+           ds->ds[i].name, values[i],
+           (values[i] < min) ? "below" : "above",
+           (is_failure != 0) ? "failure" : "warning",
+           (values[i] < min) ? min : max);
+      }
       buf += status;
       bufsize -= status;
 
-      n.severity = NOTIF_FAILURE;
-      n.time = vl->time;
-
-      strncpy (n.host, vl->host, sizeof (n.host));
-      n.host[sizeof (n.host) - 1] = '\0';
-
       plugin_dispatch_notification (&n);
     }
   } /* for (i = 0; i < ds->ds_num; i++) */
@@ -540,4 +636,67 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
   return (0);
 } /* int ut_check_threshold */
 
+int ut_check_interesting (const char *name)
+{
+  char *name_copy = NULL;
+  char *host = NULL;
+  char *plugin = NULL;
+  char *plugin_instance = NULL;
+  char *type = NULL;
+  char *type_instance = NULL;
+  int status;
+  data_set_t ds;
+  value_list_t vl;
+  threshold_t *th;
+
+  /* If there is no tree nothing is interesting. */
+  if (threshold_tree == NULL)
+    return (0);
+
+  name_copy = strdup (name);
+  if (name_copy == NULL)
+  {
+    ERROR ("ut_check_interesting: strdup failed.");
+    return (-1);
+  }
+
+  status = parse_identifier (name_copy, &host,
+      &plugin, &plugin_instance, &type, &type_instance);
+  if (status != 0)
+  {
+    ERROR ("ut_check_interesting: parse_identifier failed.");
+    return (-1);
+  }
+
+  memset (&ds, '\0', sizeof (ds));
+  memset (&vl, '\0', sizeof (vl));
+
+  strncpy (vl.host, host, sizeof (vl.host));
+  vl.host[sizeof (vl.host) - 1] = '\0';
+  strncpy (vl.plugin, plugin, sizeof (vl.plugin));
+  vl.plugin[sizeof (vl.plugin) - 1] = '\0';
+  if (plugin_instance != NULL)
+  {
+    strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+    vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
+  }
+  strncpy (ds.type, type, sizeof (ds.type));
+  ds.type[sizeof (ds.type) - 1] = '\0';
+  if (type_instance != NULL)
+  {
+    strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+  }
+
+  sfree (name_copy);
+  host = plugin = plugin_instance = type = type_instance = NULL;
+
+  th = threshold_search (&ds, &vl);
+  if (th == NULL)
+    return (0);
+  if ((th->flags & UT_FLAG_PERSIST) == 0)
+    return (1);
+  return (2);
+} /* int ut_check_interesting */
+
 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */