Code

Merge branch 'collectd-4.10' into collectd-5.0
[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 */
516 int ut_config (oconfig_item_t *ci)
518   int i;
519   int status = 0;
521   threshold_t th;
523   if (threshold_tree == NULL)
524   {
525     threshold_tree = c_avl_create ((void *) strcmp);
526     if (threshold_tree == NULL)
527     {
528       ERROR ("ut_config: c_avl_create failed.");
529       return (-1);
530     }
531   }
533   memset (&th, '\0', sizeof (th));
534   th.warning_min = NAN;
535   th.warning_max = NAN;
536   th.failure_min = NAN;
537   th.failure_max = NAN;
539   th.hits = 0;
540   th.hysteresis = 0;
541   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
542     
543   for (i = 0; i < ci->children_num; i++)
544   {
545     oconfig_item_t *option = ci->children + i;
546     status = 0;
548     if (strcasecmp ("Type", option->key) == 0)
549       status = ut_config_type (&th, option);
550     else if (strcasecmp ("Plugin", option->key) == 0)
551       status = ut_config_plugin (&th, option);
552     else if (strcasecmp ("Host", option->key) == 0)
553       status = ut_config_host (&th, option);
554     else
555     {
556       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
557       status = -1;
558     }
560     if (status != 0)
561       break;
562   }
564   return (status);
565 } /* int um_config */
566 /*
567  * End of the functions used to configure threshold values.
568  */
569 /* }}} */
571 /*
572  * int ut_report_state
573  *
574  * Checks if the `state' differs from the old state and creates a notification
575  * if appropriate.
576  * Does not fail.
577  */
578 static int ut_report_state (const data_set_t *ds,
579     const value_list_t *vl,
580     const threshold_t *th,
581     const gauge_t *values,
582     int ds_index,
583     int state)
584 { /* {{{ */
585   int state_old;
586   notification_t n;
588   char *buf;
589   size_t bufsize;
591   int status;
593   /* Check if hits matched */
594   if ( (th->hits != 0) )
595   {
596     int hits = uc_get_hits(ds,vl);
597     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
598     if ( (state == STATE_OKAY) || (hits > th->hits) )
599     {
600         DEBUG("ut_report_state: reset uc_get_hits = 0");
601         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
602     } else {
603       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
604       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
605       return (0);
606     }
607   } /* end check hits */
609   state_old = uc_get_state (ds, vl);
611   /* If the state didn't change, only report if `persistent' is specified and
612    * the state is not `okay'. */
613   if (state == state_old)
614   {
615     if ((th->flags & UT_FLAG_PERSIST) == 0)
616       return (0);
617     else if (state == STATE_OKAY)
618       return (0);
619   }
621   if (state != state_old)
622     uc_set_state (ds, vl, state);
624   NOTIFICATION_INIT_VL (&n, vl);
626   buf = n.message;
627   bufsize = sizeof (n.message);
629   if (state == STATE_OKAY)
630     n.severity = NOTIF_OKAY;
631   else if (state == STATE_WARNING)
632     n.severity = NOTIF_WARNING;
633   else
634     n.severity = NOTIF_FAILURE;
636   n.time = vl->time;
638   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
639       vl->host, vl->plugin);
640   buf += status;
641   bufsize -= status;
643   if (vl->plugin_instance[0] != '\0')
644   {
645     status = ssnprintf (buf, bufsize, " (instance %s)",
646         vl->plugin_instance);
647     buf += status;
648     bufsize -= status;
649   }
651   status = ssnprintf (buf, bufsize, " type %s", vl->type);
652   buf += status;
653   bufsize -= status;
655   if (vl->type_instance[0] != '\0')
656   {
657     status = ssnprintf (buf, bufsize, " (instance %s)",
658         vl->type_instance);
659     buf += status;
660     bufsize -= status;
661   }
663   plugin_notification_meta_add_string (&n, "DataSource",
664       ds->ds[ds_index].name);
665   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
666   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
667   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
668   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
669   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
671   /* Send an okay notification */
672   if (state == STATE_OKAY)
673   {
674     if (state_old == STATE_MISSING)
675       status = ssnprintf (buf, bufsize,
676           ": Value is no longer missing.");
677     else
678       status = ssnprintf (buf, bufsize,
679           ": All data sources are within range again.");
680     buf += status;
681     bufsize -= status;
682   }
683   else
684   {
685     double min;
686     double max;
688     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
689     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
691     if (th->flags & UT_FLAG_INVERT)
692     {
693       if (!isnan (min) && !isnan (max))
694       {
695         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
696             "%f. That is within the %s region of %f%s and %f%s.",
697             ds->ds[ds_index].name, values[ds_index],
698             (state == STATE_ERROR) ? "failure" : "warning",
699             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
700             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
701       }
702       else
703       {
704         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
705             "%f. That is %s the %s threshold of %f%s.",
706             ds->ds[ds_index].name, values[ds_index],
707             isnan (min) ? "below" : "above",
708             (state == STATE_ERROR) ? "failure" : "warning",
709             isnan (min) ? max : min,
710             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
711       }
712     }
713     else if (th->flags & UT_FLAG_PERCENTAGE)
714     {
715       gauge_t value;
716       gauge_t sum;
717       int i;
719       sum = 0.0;
720       for (i = 0; i < vl->values_len; i++)
721       {
722         if (isnan (values[i]))
723           continue;
725         sum += values[i];
726       }
728       if (sum == 0.0)
729         value = NAN;
730       else
731         value = 100.0 * values[ds_index] / sum;
733       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
734           "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
735           ds->ds[ds_index].name, values[ds_index], value,
736           (value < min) ? "below" : "above",
737           (state == STATE_ERROR) ? "failure" : "warning",
738           (value < min) ? min : max);
739     }
740     else /* is not inverted */
741     {
742       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
743           "%f. That is %s the %s threshold of %f.",
744           ds->ds[ds_index].name, values[ds_index],
745           (values[ds_index] < min) ? "below" : "above",
746           (state == STATE_ERROR) ? "failure" : "warning",
747           (values[ds_index] < min) ? min : max);
748     }
749     buf += status;
750     bufsize -= status;
751   }
753   plugin_dispatch_notification (&n);
755   plugin_notification_meta_free (n.meta);
756   return (0);
757 } /* }}} int ut_report_state */
759 /*
760  * int ut_check_one_data_source
761  *
762  * Checks one data source against the given threshold configuration. If the
763  * `DataSource' option is set in the threshold, and the name does NOT match,
764  * `okay' is returned. If the threshold does match, its failure and warning
765  * min and max values are checked and `failure' or `warning' is returned if
766  * appropriate.
767  * Does not fail.
768  */
769 static int ut_check_one_data_source (const data_set_t *ds,
770     const value_list_t __attribute__((unused)) *vl,
771     const threshold_t *th,
772     const gauge_t *values,
773     int ds_index)
774 { /* {{{ */
775   const char *ds_name;
776   int is_warning = 0;
777   int is_failure = 0;
778   int prev_state = STATE_OKAY;
780   /* check if this threshold applies to this data source */
781   if (ds != NULL)
782   {
783     ds_name = ds->ds[ds_index].name;
784     if ((th->data_source[0] != 0)
785         && (strcmp (ds_name, th->data_source) != 0))
786       return (STATE_OKAY);
787   }
789   if ((th->flags & UT_FLAG_INVERT) != 0)
790   {
791     is_warning--;
792     is_failure--;
793   }
795   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
796    * and probably, do not work as you expect. Enjoy! :D */
797   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
798   {
799     switch(prev_state)
800     {
801       case STATE_ERROR:
802         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
803              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
804           return (STATE_OKAY);
805         else
806           is_failure++;
807       case STATE_WARNING:
808         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
809              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
810           return (STATE_OKAY);
811         else
812           is_warning++;
813      }
814   }
815   else { /* no hysteresis */
816     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
817         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
818       is_failure++;
820     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
821         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
822       is_warning++;
823  }
825   if (is_failure != 0)
826     return (STATE_ERROR);
828   if (is_warning != 0)
829     return (STATE_WARNING);
831   return (STATE_OKAY);
832 } /* }}} int ut_check_one_data_source */
834 /*
835  * int ut_check_one_threshold
836  *
837  * Checks all data sources of a value list against the given threshold, using
838  * the ut_check_one_data_source function above. Returns the worst status,
839  * which is `okay' if nothing has failed.
840  * Returns less than zero if the data set doesn't have any data sources.
841  */
842 static int ut_check_one_threshold (const data_set_t *ds,
843     const value_list_t *vl,
844     const threshold_t *th,
845     const gauge_t *values,
846     int *ret_ds_index)
847 { /* {{{ */
848   int ret = -1;
849   int ds_index = -1;
850   int i;
851   gauge_t values_copy[ds->ds_num];
853   memcpy (values_copy, values, sizeof (values_copy));
855   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
856   {
857     int num = 0;
858     gauge_t sum=0.0;
860     if (ds->ds_num == 1)
861     {
862       WARNING ("ut_check_one_threshold: The %s type has only one data "
863           "source, but you have configured to check this as a percentage. "
864           "That doesn't make much sense, because the percentage will always "
865           "be 100%%!", ds->type);
866     }
868     /* Prepare `sum' and `num'. */
869     for (i = 0; i < ds->ds_num; i++)
870       if (!isnan (values[i]))
871       {
872         num++;
873         sum += values[i];
874       }
876     if ((num == 0) /* All data sources are undefined. */
877         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
878     {
879       for (i = 0; i < ds->ds_num; i++)
880         values_copy[i] = NAN;
881     }
882     else /* We can actually calculate the percentage. */
883     {
884       for (i = 0; i < ds->ds_num; i++)
885         values_copy[i] = 100.0 * values[i] / sum;
886     }
887   } /* if (UT_FLAG_PERCENTAGE) */
889   for (i = 0; i < ds->ds_num; i++)
890   {
891     int status;
893     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
894     if (ret < status)
895     {
896       ret = status;
897       ds_index = i;
898     }
899   } /* for (ds->ds_num) */
901   if (ret_ds_index != NULL)
902     *ret_ds_index = ds_index;
904   return (ret);
905 } /* }}} int ut_check_one_threshold */
907 /*
908  * int ut_check_threshold
909  *
910  * Gets a list of matching thresholds and searches for the worst status by one
911  * of the thresholds. Then reports that status using the ut_report_state
912  * function above. 
913  * Returns zero on success and if no threshold has been configured. Returns
914  * less than zero on failure.
915  */
916 static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
917     __attribute__((unused)) user_data_t *ud)
918 { /* {{{ */
919   threshold_t *th;
920   gauge_t *values;
921   int status;
923   int worst_state = -1;
924   threshold_t *worst_th = NULL;
925   int worst_ds_index = -1;
927   if (threshold_tree == NULL)
928     return (0);
930   /* Is this lock really necessary? So far, thresholds are only inserted at
931    * startup. -octo */
932   pthread_mutex_lock (&threshold_lock);
933   th = threshold_search (vl);
934   pthread_mutex_unlock (&threshold_lock);
935   if (th == NULL)
936     return (0);
938   DEBUG ("ut_check_threshold: Found matching threshold(s)");
940   values = uc_get_rate (ds, vl);
941   if (values == NULL)
942     return (0);
944   while (th != NULL)
945   {
946     int ds_index = -1;
948     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
949     if (status < 0)
950     {
951       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
952       sfree (values);
953       return (-1);
954     }
956     if (worst_state < status)
957     {
958       worst_state = status;
959       worst_th = th;
960       worst_ds_index = ds_index;
961     }
963     th = th->next;
964   } /* while (th) */
966   status = ut_report_state (ds, vl, worst_th, values,
967       worst_ds_index, worst_state);
968   if (status != 0)
969   {
970     ERROR ("ut_check_threshold: ut_report_state failed.");
971     sfree (values);
972     return (-1);
973   }
975   sfree (values);
977   return (0);
978 } /* }}} int ut_check_threshold */
980 /*
981  * int ut_missing
982  *
983  * This function is called whenever a value goes "missing".
984  */
985 static int ut_missing (const value_list_t *vl,
986     __attribute__((unused)) user_data_t *ud)
987 { /* {{{ */
988   threshold_t *th;
989   cdtime_t missing_time;
990   char identifier[6 * DATA_MAX_NAME_LEN];
991   notification_t n;
993   th = threshold_search (vl);
994   if (th == NULL)
995     return (0);
997   missing_time = cdtime () - vl->time;
998   FORMAT_VL (identifier, sizeof (identifier), vl);
1000   NOTIFICATION_INIT_VL (&n, vl);
1001   ssnprintf (n.message, sizeof (n.message),
1002       "%s has not been updated for %.3f seconds.",
1003       identifier, CDTIME_T_TO_DOUBLE (missing_time));
1005   plugin_dispatch_notification (&n);
1007   return (0);
1008 } /* }}} int ut_missing */
1010 void module_register (void)
1012   plugin_register_complex_config ("threshold", ut_config);
1013   plugin_register_missing ("threshold", ut_missing,
1014       /* user data = */ NULL);
1015   plugin_register_write ("threshold", ut_check_threshold,
1016       /* user data = */ NULL);
1019 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */