Code

threshold: Don't register missing/write callbacks unless we have thresholds.
[collectd.git] / src / threshold.c
1 /**
2  * collectd - src/threshold.c
3  * Copyright (C) 2007-2010  Florian Forster
4  * Copyright (C) 2008-2009  Sebastian Harl
5  * Copyright (C) 2009       Andrés J. Díaz
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; only version 2 of the License is applicable.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Author:
21  *   Florian octo Forster <octo at collectd.org>
22  *   Sebastian Harl <sh at tokkee.org>
23  *   Andrés J. Díaz <ajdiaz at connectical.com>
24  **/
26 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
29 #include "utils_avltree.h"
30 #include "utils_cache.h"
32 #include <assert.h>
33 #include <pthread.h>
35 /*
36  * Private data structures
37  * {{{ */
38 #define UT_FLAG_INVERT  0x01
39 #define UT_FLAG_PERSIST 0x02
40 #define UT_FLAG_PERCENTAGE 0x04
41 #define UT_FLAG_INTERESTING 0x08
42 typedef struct threshold_s
43 {
44   char host[DATA_MAX_NAME_LEN];
45   char plugin[DATA_MAX_NAME_LEN];
46   char plugin_instance[DATA_MAX_NAME_LEN];
47   char type[DATA_MAX_NAME_LEN];
48   char type_instance[DATA_MAX_NAME_LEN];
49   char data_source[DATA_MAX_NAME_LEN];
50   gauge_t warning_min;
51   gauge_t warning_max;
52   gauge_t failure_min;
53   gauge_t failure_max;
54   gauge_t hysteresis;
55   unsigned int flags;
56   int hits;
57   struct threshold_s *next;
58 } threshold_t;
59 /* }}} */
61 /*
62  * Private (static) variables
63  * {{{ */
64 static c_avl_tree_t   *threshold_tree = NULL;
65 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
66 /* }}} */
68 /*
69  * Threshold management
70  * ====================
71  * The following functions add, delete, search, etc. configured thresholds to
72  * the underlying AVL trees.
73  */
74 /*
75  * threshold_t *threshold_get
76  *
77  * Retrieve one specific threshold configuration. For looking up a threshold
78  * matching a value_list_t, see "threshold_search" below. Returns NULL if the
79  * specified threshold doesn't exist.
80  */
81 static threshold_t *threshold_get (const char *hostname,
82     const char *plugin, const char *plugin_instance,
83     const char *type, const char *type_instance)
84 { /* {{{ */
85   char name[6 * DATA_MAX_NAME_LEN];
86   threshold_t *th = NULL;
88   format_name (name, sizeof (name),
89       (hostname == NULL) ? "" : hostname,
90       (plugin == NULL) ? "" : plugin, plugin_instance,
91       (type == NULL) ? "" : type, type_instance);
92   name[sizeof (name) - 1] = '\0';
94   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
95     return (th);
96   else
97     return (NULL);
98 } /* }}} threshold_t *threshold_get */
100 /*
101  * int ut_threshold_add
102  *
103  * Adds a threshold configuration to the list of thresholds. The threshold_t
104  * structure is copied and may be destroyed after this call. Returns zero on
105  * success, non-zero otherwise.
106  */
107 static int ut_threshold_add (const threshold_t *th)
108 { /* {{{ */
109   char name[6 * DATA_MAX_NAME_LEN];
110   char *name_copy;
111   threshold_t *th_copy;
112   threshold_t *th_ptr;
113   int status = 0;
115   if (format_name (name, sizeof (name), th->host,
116         th->plugin, th->plugin_instance,
117         th->type, th->type_instance) != 0)
118   {
119     ERROR ("ut_threshold_add: format_name failed.");
120     return (-1);
121   }
123   name_copy = strdup (name);
124   if (name_copy == NULL)
125   {
126     ERROR ("ut_threshold_add: strdup failed.");
127     return (-1);
128   }
130   th_copy = (threshold_t *) malloc (sizeof (threshold_t));
131   if (th_copy == NULL)
132   {
133     sfree (name_copy);
134     ERROR ("ut_threshold_add: malloc failed.");
135     return (-1);
136   }
137   memcpy (th_copy, th, sizeof (threshold_t));
138   th_ptr = NULL;
140   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
142   pthread_mutex_lock (&threshold_lock);
144   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
145       th->type, th->type_instance);
147   while ((th_ptr != NULL) && (th_ptr->next != NULL))
148     th_ptr = th_ptr->next;
150   if (th_ptr == NULL) /* no such threshold yet */
151   {
152     status = c_avl_insert (threshold_tree, name_copy, th_copy);
153   }
154   else /* th_ptr points to the last threshold in the list */
155   {
156     th_ptr->next = th_copy;
157     /* name_copy isn't needed */
158     sfree (name_copy);
159   }
161   pthread_mutex_unlock (&threshold_lock);
163   if (status != 0)
164   {
165     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
166     sfree (name_copy);
167     sfree (th_copy);
168   }
170   return (status);
171 } /* }}} int ut_threshold_add */
173 /* 
174  * threshold_t *threshold_search
175  *
176  * Searches for a threshold configuration using all the possible variations of
177  * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
178  * found.
179  * XXX: This is likely the least efficient function in collectd.
180  */
181 static threshold_t *threshold_search (const value_list_t *vl)
182 { /* {{{ */
183   threshold_t *th;
185   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
186           vl->type, vl->type_instance)) != NULL)
187     return (th);
188   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
189           vl->type, NULL)) != NULL)
190     return (th);
191   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
192           vl->type, vl->type_instance)) != NULL)
193     return (th);
194   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
195           vl->type, NULL)) != NULL)
196     return (th);
197   else if ((th = threshold_get (vl->host, "", NULL,
198           vl->type, vl->type_instance)) != NULL)
199     return (th);
200   else if ((th = threshold_get (vl->host, "", NULL,
201           vl->type, NULL)) != NULL)
202     return (th);
203   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
204           vl->type, vl->type_instance)) != NULL)
205     return (th);
206   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
207           vl->type, NULL)) != NULL)
208     return (th);
209   else if ((th = threshold_get ("", vl->plugin, NULL,
210           vl->type, vl->type_instance)) != NULL)
211     return (th);
212   else if ((th = threshold_get ("", vl->plugin, NULL,
213           vl->type, NULL)) != NULL)
214     return (th);
215   else if ((th = threshold_get ("", "", NULL,
216           vl->type, vl->type_instance)) != NULL)
217     return (th);
218   else if ((th = threshold_get ("", "", NULL,
219           vl->type, NULL)) != NULL)
220     return (th);
222   return (NULL);
223 } /* }}} threshold_t *threshold_search */
225 /*
226  * Configuration
227  * =============
228  * The following approximately two hundred functions are used to handle the
229  * configuration and fill the threshold list.
230  * {{{ */
231 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
233   if ((ci->values_num != 1)
234       || (ci->values[0].type != OCONFIG_TYPE_STRING))
235   {
236     WARNING ("threshold values: The `DataSource' option needs exactly one "
237         "string argument.");
238     return (-1);
239   }
241   sstrncpy (th->data_source, ci->values[0].value.string,
242       sizeof (th->data_source));
244   return (0);
245 } /* int ut_config_type_datasource */
247 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
249   if ((ci->values_num != 1)
250       || (ci->values[0].type != OCONFIG_TYPE_STRING))
251   {
252     WARNING ("threshold values: The `Instance' option needs exactly one "
253         "string argument.");
254     return (-1);
255   }
257   sstrncpy (th->type_instance, ci->values[0].value.string,
258       sizeof (th->type_instance));
260   return (0);
261 } /* int ut_config_type_instance */
263 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
265   if ((ci->values_num != 1)
266       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
267   {
268     WARNING ("threshold values: The `%s' option needs exactly one "
269         "number argument.", ci->key);
270     return (-1);
271   }
273   if (strcasecmp (ci->key, "WarningMax") == 0)
274     th->warning_max = ci->values[0].value.number;
275   else
276     th->failure_max = ci->values[0].value.number;
278   return (0);
279 } /* int ut_config_type_max */
281 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
283   if ((ci->values_num != 1)
284       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
285   {
286     WARNING ("threshold values: The `%s' option needs exactly one "
287         "number argument.", ci->key);
288     return (-1);
289   }
291   if (strcasecmp (ci->key, "WarningMin") == 0)
292     th->warning_min = ci->values[0].value.number;
293   else
294     th->failure_min = ci->values[0].value.number;
296   return (0);
297 } /* int ut_config_type_min */
299 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
301   if ((ci->values_num != 1)
302       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
303   {
304     WARNING ("threshold values: The `%s' option needs exactly one "
305       "number argument.", ci->key);
306     return (-1);
307   }
309   th->hits = ci->values[0].value.number;
311   return (0);
312 } /* int ut_config_type_hits */
314 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
316   if ((ci->values_num != 1)
317       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
318   {
319     WARNING ("threshold values: The `%s' option needs exactly one "
320       "number argument.", ci->key);
321     return (-1);
322   }
324   th->hysteresis = ci->values[0].value.number;
326   return (0);
327 } /* int ut_config_type_hysteresis */
329 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
331   int i;
332   threshold_t th;
333   int status = 0;
335   if ((ci->values_num != 1)
336       || (ci->values[0].type != OCONFIG_TYPE_STRING))
337   {
338     WARNING ("threshold values: The `Type' block needs exactly one string "
339         "argument.");
340     return (-1);
341   }
343   if (ci->children_num < 1)
344   {
345     WARNING ("threshold values: The `Type' block needs at least one option.");
346     return (-1);
347   }
349   memcpy (&th, th_orig, sizeof (th));
350   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
352   th.warning_min = NAN;
353   th.warning_max = NAN;
354   th.failure_min = NAN;
355   th.failure_max = NAN;
356   th.hits = 0;
357   th.hysteresis = 0;
358   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
360   for (i = 0; i < ci->children_num; i++)
361   {
362     oconfig_item_t *option = ci->children + i;
363     status = 0;
365     if (strcasecmp ("Instance", option->key) == 0)
366       status = ut_config_type_instance (&th, option);
367     else if (strcasecmp ("DataSource", option->key) == 0)
368       status = ut_config_type_datasource (&th, option);
369     else if ((strcasecmp ("WarningMax", option->key) == 0)
370         || (strcasecmp ("FailureMax", option->key) == 0))
371       status = ut_config_type_max (&th, option);
372     else if ((strcasecmp ("WarningMin", option->key) == 0)
373         || (strcasecmp ("FailureMin", option->key) == 0))
374       status = ut_config_type_min (&th, option);
375     else if (strcasecmp ("Interesting", option->key) == 0)
376       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
377     else if (strcasecmp ("Invert", option->key) == 0)
378       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
379     else if (strcasecmp ("Persist", option->key) == 0)
380       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
381     else if (strcasecmp ("Percentage", option->key) == 0)
382       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
383     else if (strcasecmp ("Hits", option->key) == 0)
384       status = ut_config_type_hits (&th, option);
385     else if (strcasecmp ("Hysteresis", option->key) == 0)
386       status = ut_config_type_hysteresis (&th, option);
387     else
388     {
389       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
390           "block.", option->key);
391       status = -1;
392     }
394     if (status != 0)
395       break;
396   }
398   if (status == 0)
399   {
400     status = ut_threshold_add (&th);
401   }
403   return (status);
404 } /* int ut_config_type */
406 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
408   if ((ci->values_num != 1)
409       || (ci->values[0].type != OCONFIG_TYPE_STRING))
410   {
411     WARNING ("threshold values: The `Instance' option needs exactly one "
412         "string argument.");
413     return (-1);
414   }
416   sstrncpy (th->plugin_instance, ci->values[0].value.string,
417       sizeof (th->plugin_instance));
419   return (0);
420 } /* int ut_config_plugin_instance */
422 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
424   int i;
425   threshold_t th;
426   int status = 0;
428   if ((ci->values_num != 1)
429       || (ci->values[0].type != OCONFIG_TYPE_STRING))
430   {
431     WARNING ("threshold values: The `Plugin' block needs exactly one string "
432         "argument.");
433     return (-1);
434   }
436   if (ci->children_num < 1)
437   {
438     WARNING ("threshold values: The `Plugin' block needs at least one nested "
439         "block.");
440     return (-1);
441   }
443   memcpy (&th, th_orig, sizeof (th));
444   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
446   for (i = 0; i < ci->children_num; i++)
447   {
448     oconfig_item_t *option = ci->children + i;
449     status = 0;
451     if (strcasecmp ("Type", option->key) == 0)
452       status = ut_config_type (&th, option);
453     else if (strcasecmp ("Instance", option->key) == 0)
454       status = ut_config_plugin_instance (&th, option);
455     else
456     {
457       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
458           "block.", option->key);
459       status = -1;
460     }
462     if (status != 0)
463       break;
464   }
466   return (status);
467 } /* int ut_config_plugin */
469 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
471   int i;
472   threshold_t th;
473   int status = 0;
475   if ((ci->values_num != 1)
476       || (ci->values[0].type != OCONFIG_TYPE_STRING))
477   {
478     WARNING ("threshold values: The `Host' block needs exactly one string "
479         "argument.");
480     return (-1);
481   }
483   if (ci->children_num < 1)
484   {
485     WARNING ("threshold values: The `Host' block needs at least one nested "
486         "block.");
487     return (-1);
488   }
490   memcpy (&th, th_orig, sizeof (th));
491   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
493   for (i = 0; i < ci->children_num; i++)
494   {
495     oconfig_item_t *option = ci->children + i;
496     status = 0;
498     if (strcasecmp ("Type", option->key) == 0)
499       status = ut_config_type (&th, option);
500     else if (strcasecmp ("Plugin", option->key) == 0)
501       status = ut_config_plugin (&th, option);
502     else
503     {
504       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
505           "block.", option->key);
506       status = -1;
507     }
509     if (status != 0)
510       break;
511   }
513   return (status);
514 } /* int ut_config_host */
515 /*
516  * End of the functions used to configure threshold values.
517  */
518 /* }}} */
520 /*
521  * int ut_report_state
522  *
523  * Checks if the `state' differs from the old state and creates a notification
524  * if appropriate.
525  * Does not fail.
526  */
527 static int ut_report_state (const data_set_t *ds,
528     const value_list_t *vl,
529     const threshold_t *th,
530     const gauge_t *values,
531     int ds_index,
532     int state)
533 { /* {{{ */
534   int state_old;
535   notification_t n;
537   char *buf;
538   size_t bufsize;
540   int status;
542   /* Check if hits matched */
543   if ( (th->hits != 0) )
544   {
545     int hits = uc_get_hits(ds,vl);
546     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
547     if ( (state == STATE_OKAY) || (hits > th->hits) )
548     {
549         DEBUG("ut_report_state: reset uc_get_hits = 0");
550         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
551     } else {
552       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
553       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
554       return (0);
555     }
556   } /* end check hits */
558   state_old = uc_get_state (ds, vl);
560   /* If the state didn't change, only report if `persistent' is specified and
561    * the state is not `okay'. */
562   if (state == state_old)
563   {
564     if ((th->flags & UT_FLAG_PERSIST) == 0)
565       return (0);
566     else if (state == STATE_OKAY)
567       return (0);
568   }
570   if (state != state_old)
571     uc_set_state (ds, vl, state);
573   NOTIFICATION_INIT_VL (&n, vl);
575   buf = n.message;
576   bufsize = sizeof (n.message);
578   if (state == STATE_OKAY)
579     n.severity = NOTIF_OKAY;
580   else if (state == STATE_WARNING)
581     n.severity = NOTIF_WARNING;
582   else
583     n.severity = NOTIF_FAILURE;
585   n.time = vl->time;
587   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
588       vl->host, vl->plugin);
589   buf += status;
590   bufsize -= status;
592   if (vl->plugin_instance[0] != '\0')
593   {
594     status = ssnprintf (buf, bufsize, " (instance %s)",
595         vl->plugin_instance);
596     buf += status;
597     bufsize -= status;
598   }
600   status = ssnprintf (buf, bufsize, " type %s", vl->type);
601   buf += status;
602   bufsize -= status;
604   if (vl->type_instance[0] != '\0')
605   {
606     status = ssnprintf (buf, bufsize, " (instance %s)",
607         vl->type_instance);
608     buf += status;
609     bufsize -= status;
610   }
612   plugin_notification_meta_add_string (&n, "DataSource",
613       ds->ds[ds_index].name);
614   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
615   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
616   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
617   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
618   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
620   /* Send an okay notification */
621   if (state == STATE_OKAY)
622   {
623     if (state_old == STATE_MISSING)
624       status = ssnprintf (buf, bufsize,
625           ": Value is no longer missing.");
626     else
627       status = ssnprintf (buf, bufsize,
628           ": All data sources are within range again.");
629     buf += status;
630     bufsize -= status;
631   }
632   else
633   {
634     double min;
635     double max;
637     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
638     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
640     if (th->flags & UT_FLAG_INVERT)
641     {
642       if (!isnan (min) && !isnan (max))
643       {
644         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
645             "%f. That is within the %s region of %f%s and %f%s.",
646             ds->ds[ds_index].name, values[ds_index],
647             (state == STATE_ERROR) ? "failure" : "warning",
648             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
649             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
650       }
651       else
652       {
653         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
654             "%f. That is %s the %s threshold of %f%s.",
655             ds->ds[ds_index].name, values[ds_index],
656             isnan (min) ? "below" : "above",
657             (state == STATE_ERROR) ? "failure" : "warning",
658             isnan (min) ? max : min,
659             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
660       }
661     }
662     else if (th->flags & UT_FLAG_PERCENTAGE)
663     {
664       gauge_t value;
665       gauge_t sum;
666       int i;
668       sum = 0.0;
669       for (i = 0; i < vl->values_len; i++)
670       {
671         if (isnan (values[i]))
672           continue;
674         sum += values[i];
675       }
677       if (sum == 0.0)
678         value = NAN;
679       else
680         value = 100.0 * values[ds_index] / sum;
682       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
683           "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
684           ds->ds[ds_index].name, values[ds_index], value,
685           (value < min) ? "below" : "above",
686           (state == STATE_ERROR) ? "failure" : "warning",
687           (value < min) ? min : max);
688     }
689     else /* is not inverted */
690     {
691       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
692           "%f. That is %s the %s threshold of %f.",
693           ds->ds[ds_index].name, values[ds_index],
694           (values[ds_index] < min) ? "below" : "above",
695           (state == STATE_ERROR) ? "failure" : "warning",
696           (values[ds_index] < min) ? min : max);
697     }
698     buf += status;
699     bufsize -= status;
700   }
702   plugin_dispatch_notification (&n);
704   plugin_notification_meta_free (n.meta);
705   return (0);
706 } /* }}} int ut_report_state */
708 /*
709  * int ut_check_one_data_source
710  *
711  * Checks one data source against the given threshold configuration. If the
712  * `DataSource' option is set in the threshold, and the name does NOT match,
713  * `okay' is returned. If the threshold does match, its failure and warning
714  * min and max values are checked and `failure' or `warning' is returned if
715  * appropriate.
716  * Does not fail.
717  */
718 static int ut_check_one_data_source (const data_set_t *ds,
719     const value_list_t __attribute__((unused)) *vl,
720     const threshold_t *th,
721     const gauge_t *values,
722     int ds_index)
723 { /* {{{ */
724   const char *ds_name;
725   int is_warning = 0;
726   int is_failure = 0;
727   int prev_state = STATE_OKAY;
729   /* check if this threshold applies to this data source */
730   if (ds != NULL)
731   {
732     ds_name = ds->ds[ds_index].name;
733     if ((th->data_source[0] != 0)
734         && (strcmp (ds_name, th->data_source) != 0))
735       return (STATE_OKAY);
736   }
738   if ((th->flags & UT_FLAG_INVERT) != 0)
739   {
740     is_warning--;
741     is_failure--;
742   }
744   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
745    * and probably, do not work as you expect. Enjoy! :D */
746   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
747   {
748     switch(prev_state)
749     {
750       case STATE_ERROR:
751         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
752              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
753           return (STATE_OKAY);
754         else
755           is_failure++;
756       case STATE_WARNING:
757         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
758              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
759           return (STATE_OKAY);
760         else
761           is_warning++;
762      }
763   }
764   else { /* no hysteresis */
765     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
766         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
767       is_failure++;
769     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
770         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
771       is_warning++;
772  }
774   if (is_failure != 0)
775     return (STATE_ERROR);
777   if (is_warning != 0)
778     return (STATE_WARNING);
780   return (STATE_OKAY);
781 } /* }}} int ut_check_one_data_source */
783 /*
784  * int ut_check_one_threshold
785  *
786  * Checks all data sources of a value list against the given threshold, using
787  * the ut_check_one_data_source function above. Returns the worst status,
788  * which is `okay' if nothing has failed.
789  * Returns less than zero if the data set doesn't have any data sources.
790  */
791 static int ut_check_one_threshold (const data_set_t *ds,
792     const value_list_t *vl,
793     const threshold_t *th,
794     const gauge_t *values,
795     int *ret_ds_index)
796 { /* {{{ */
797   int ret = -1;
798   int ds_index = -1;
799   int i;
800   gauge_t values_copy[ds->ds_num];
802   memcpy (values_copy, values, sizeof (values_copy));
804   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
805   {
806     int num = 0;
807     gauge_t sum=0.0;
809     if (ds->ds_num == 1)
810     {
811       WARNING ("ut_check_one_threshold: The %s type has only one data "
812           "source, but you have configured to check this as a percentage. "
813           "That doesn't make much sense, because the percentage will always "
814           "be 100%%!", ds->type);
815     }
817     /* Prepare `sum' and `num'. */
818     for (i = 0; i < ds->ds_num; i++)
819       if (!isnan (values[i]))
820       {
821         num++;
822         sum += values[i];
823       }
825     if ((num == 0) /* All data sources are undefined. */
826         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
827     {
828       for (i = 0; i < ds->ds_num; i++)
829         values_copy[i] = NAN;
830     }
831     else /* We can actually calculate the percentage. */
832     {
833       for (i = 0; i < ds->ds_num; i++)
834         values_copy[i] = 100.0 * values[i] / sum;
835     }
836   } /* if (UT_FLAG_PERCENTAGE) */
838   for (i = 0; i < ds->ds_num; i++)
839   {
840     int status;
842     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
843     if (ret < status)
844     {
845       ret = status;
846       ds_index = i;
847     }
848   } /* for (ds->ds_num) */
850   if (ret_ds_index != NULL)
851     *ret_ds_index = ds_index;
853   return (ret);
854 } /* }}} int ut_check_one_threshold */
856 /*
857  * int ut_check_threshold
858  *
859  * Gets a list of matching thresholds and searches for the worst status by one
860  * of the thresholds. Then reports that status using the ut_report_state
861  * function above. 
862  * Returns zero on success and if no threshold has been configured. Returns
863  * less than zero on failure.
864  */
865 static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
866     __attribute__((unused)) user_data_t *ud)
867 { /* {{{ */
868   threshold_t *th;
869   gauge_t *values;
870   int status;
872   int worst_state = -1;
873   threshold_t *worst_th = NULL;
874   int worst_ds_index = -1;
876   if (threshold_tree == NULL)
877     return (0);
879   /* Is this lock really necessary? So far, thresholds are only inserted at
880    * startup. -octo */
881   pthread_mutex_lock (&threshold_lock);
882   th = threshold_search (vl);
883   pthread_mutex_unlock (&threshold_lock);
884   if (th == NULL)
885     return (0);
887   DEBUG ("ut_check_threshold: Found matching threshold(s)");
889   values = uc_get_rate (ds, vl);
890   if (values == NULL)
891     return (0);
893   while (th != NULL)
894   {
895     int ds_index = -1;
897     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
898     if (status < 0)
899     {
900       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
901       sfree (values);
902       return (-1);
903     }
905     if (worst_state < status)
906     {
907       worst_state = status;
908       worst_th = th;
909       worst_ds_index = ds_index;
910     }
912     th = th->next;
913   } /* while (th) */
915   status = ut_report_state (ds, vl, worst_th, values,
916       worst_ds_index, worst_state);
917   if (status != 0)
918   {
919     ERROR ("ut_check_threshold: ut_report_state failed.");
920     sfree (values);
921     return (-1);
922   }
924   sfree (values);
926   return (0);
927 } /* }}} int ut_check_threshold */
929 /*
930  * int ut_missing
931  *
932  * This function is called whenever a value goes "missing".
933  */
934 static int ut_missing (const value_list_t *vl,
935     __attribute__((unused)) user_data_t *ud)
936 { /* {{{ */
937   threshold_t *th;
938   cdtime_t missing_time;
939   char identifier[6 * DATA_MAX_NAME_LEN];
940   notification_t n;
942   /* dispatch notifications for "interesting" values only */
943   if (threshold_tree == NULL)
944     return (0);
946   th = threshold_search (vl);
947   if (th == NULL)
948     return (0);
950   missing_time = cdtime () - vl->time;
951   FORMAT_VL (identifier, sizeof (identifier), vl);
953   NOTIFICATION_INIT_VL (&n, vl);
954   ssnprintf (n.message, sizeof (n.message),
955       "%s has not been updated for %.3f seconds.",
956       identifier, CDTIME_T_TO_DOUBLE (missing_time));
958   plugin_dispatch_notification (&n);
960   return (0);
961 } /* }}} int ut_missing */
963 int ut_config (oconfig_item_t *ci)
964 { /* {{{ */
965   int i;
966   int status = 0;
968   threshold_t th;
970   if (threshold_tree == NULL)
971   {
972     threshold_tree = c_avl_create ((void *) strcmp);
973     if (threshold_tree == NULL)
974     {
975       ERROR ("ut_config: c_avl_create failed.");
976       return (-1);
977     }
978   }
980   memset (&th, '\0', sizeof (th));
981   th.warning_min = NAN;
982   th.warning_max = NAN;
983   th.failure_min = NAN;
984   th.failure_max = NAN;
986   th.hits = 0;
987   th.hysteresis = 0;
988   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
989     
990   for (i = 0; i < ci->children_num; i++)
991   {
992     oconfig_item_t *option = ci->children + i;
993     status = 0;
995     if (strcasecmp ("Type", option->key) == 0)
996       status = ut_config_type (&th, option);
997     else if (strcasecmp ("Plugin", option->key) == 0)
998       status = ut_config_plugin (&th, option);
999     else if (strcasecmp ("Host", option->key) == 0)
1000       status = ut_config_host (&th, option);
1001     else
1002     {
1003       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
1004       status = -1;
1005     }
1007     if (status != 0)
1008       break;
1009   }
1011   if (c_avl_size (threshold_tree) > 0) {
1012     plugin_register_missing ("threshold", ut_missing,
1013         /* user data = */ NULL);
1014     plugin_register_write ("threshold", ut_check_threshold,
1015         /* user data = */ NULL);
1016   }
1018   return (status);
1019 } /* }}} int um_config */
1021 void module_register (void)
1023   plugin_register_complex_config ("threshold", ut_config);
1026 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */