f12b405773609c89cbe37ea4e8061d93d714a706
[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  **/
25
26 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
29 #include "utils_avltree.h"
30 #include "utils_cache.h"
31 #include "utils_threshold.h"
32
33 /*
34  * Threshold management
35  * ====================
36  * The following functions add, delete, search, etc. configured thresholds to
37  * the underlying AVL trees.
38  */
39
40 /*
41  * int ut_threshold_add
42  *
43  * Adds a threshold configuration to the list of thresholds. The threshold_t
44  * structure is copied and may be destroyed after this call. Returns zero on
45  * success, non-zero otherwise.
46  */
47 static int ut_threshold_add (const threshold_t *th)
48 { /* {{{ */
49   char name[6 * DATA_MAX_NAME_LEN];
50   char *name_copy;
51   threshold_t *th_copy;
52   threshold_t *th_ptr;
53   int status = 0;
54
55   if (format_name (name, sizeof (name), th->host,
56         th->plugin, th->plugin_instance,
57         th->type, th->type_instance) != 0)
58   {
59     ERROR ("ut_threshold_add: format_name failed.");
60     return (-1);
61   }
62
63   name_copy = strdup (name);
64   if (name_copy == NULL)
65   {
66     ERROR ("ut_threshold_add: strdup failed.");
67     return (-1);
68   }
69
70   th_copy = malloc (sizeof (*th_copy));
71   if (th_copy == NULL)
72   {
73     sfree (name_copy);
74     ERROR ("ut_threshold_add: malloc failed.");
75     return (-1);
76   }
77   memcpy (th_copy, th, sizeof (threshold_t));
78
79   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
80
81   pthread_mutex_lock (&threshold_lock);
82
83   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
84       th->type, th->type_instance);
85
86   while ((th_ptr != NULL) && (th_ptr->next != NULL))
87     th_ptr = th_ptr->next;
88
89   if (th_ptr == NULL) /* no such threshold yet */
90   {
91     status = c_avl_insert (threshold_tree, name_copy, th_copy);
92   }
93   else /* th_ptr points to the last threshold in the list */
94   {
95     th_ptr->next = th_copy;
96     /* name_copy isn't needed */
97     sfree (name_copy);
98   }
99
100   pthread_mutex_unlock (&threshold_lock);
101
102   if (status != 0)
103   {
104     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
105     sfree (name_copy);
106     sfree (th_copy);
107   }
108
109   return (status);
110 } /* }}} int ut_threshold_add */
111
112 /*
113  * Configuration
114  * =============
115  * The following approximately two hundred functions are used to handle the
116  * configuration and fill the threshold list.
117  * {{{ */
118 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
119 {
120   if ((ci->values_num != 1)
121       || (ci->values[0].type != OCONFIG_TYPE_STRING))
122   {
123     WARNING ("threshold values: The `DataSource' option needs exactly one "
124         "string argument.");
125     return (-1);
126   }
127
128   sstrncpy (th->data_source, ci->values[0].value.string,
129       sizeof (th->data_source));
130
131   return (0);
132 } /* int ut_config_type_datasource */
133
134 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
135 {
136   if ((ci->values_num != 1)
137       || (ci->values[0].type != OCONFIG_TYPE_STRING))
138   {
139     WARNING ("threshold values: The `Instance' option needs exactly one "
140         "string argument.");
141     return (-1);
142   }
143
144   sstrncpy (th->type_instance, ci->values[0].value.string,
145       sizeof (th->type_instance));
146
147   return (0);
148 } /* int ut_config_type_instance */
149
150 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
151 {
152   if ((ci->values_num != 1)
153       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
154   {
155     WARNING ("threshold values: The `%s' option needs exactly one "
156         "number argument.", ci->key);
157     return (-1);
158   }
159
160   if (strcasecmp (ci->key, "WarningMax") == 0)
161     th->warning_max = ci->values[0].value.number;
162   else
163     th->failure_max = ci->values[0].value.number;
164
165   return (0);
166 } /* int ut_config_type_max */
167
168 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
169 {
170   if ((ci->values_num != 1)
171       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
172   {
173     WARNING ("threshold values: The `%s' option needs exactly one "
174         "number argument.", ci->key);
175     return (-1);
176   }
177
178   if (strcasecmp (ci->key, "WarningMin") == 0)
179     th->warning_min = ci->values[0].value.number;
180   else
181     th->failure_min = ci->values[0].value.number;
182
183   return (0);
184 } /* int ut_config_type_min */
185
186 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
187 {
188   if ((ci->values_num != 1)
189       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
190   {
191     WARNING ("threshold values: The `%s' option needs exactly one "
192       "number argument.", ci->key);
193     return (-1);
194   }
195
196   th->hits = ci->values[0].value.number;
197
198   return (0);
199 } /* int ut_config_type_hits */
200
201 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
202 {
203   if ((ci->values_num != 1)
204       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
205   {
206     WARNING ("threshold values: The `%s' option needs exactly one "
207       "number argument.", ci->key);
208     return (-1);
209   }
210
211   th->hysteresis = ci->values[0].value.number;
212
213   return (0);
214 } /* int ut_config_type_hysteresis */
215
216 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
217 {
218   int i;
219   threshold_t th;
220   int status = 0;
221
222   if ((ci->values_num != 1)
223       || (ci->values[0].type != OCONFIG_TYPE_STRING))
224   {
225     WARNING ("threshold values: The `Type' block needs exactly one string "
226         "argument.");
227     return (-1);
228   }
229
230   if (ci->children_num < 1)
231   {
232     WARNING ("threshold values: The `Type' block needs at least one option.");
233     return (-1);
234   }
235
236   memcpy (&th, th_orig, sizeof (th));
237   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
238
239   th.warning_min = NAN;
240   th.warning_max = NAN;
241   th.failure_min = NAN;
242   th.failure_max = NAN;
243   th.hits = 0;
244   th.hysteresis = 0;
245   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
246
247   for (i = 0; i < ci->children_num; i++)
248   {
249     oconfig_item_t *option = ci->children + i;
250
251     if (strcasecmp ("Instance", option->key) == 0)
252       status = ut_config_type_instance (&th, option);
253     else if (strcasecmp ("DataSource", option->key) == 0)
254       status = ut_config_type_datasource (&th, option);
255     else if ((strcasecmp ("WarningMax", option->key) == 0)
256         || (strcasecmp ("FailureMax", option->key) == 0))
257       status = ut_config_type_max (&th, option);
258     else if ((strcasecmp ("WarningMin", option->key) == 0)
259         || (strcasecmp ("FailureMin", option->key) == 0))
260       status = ut_config_type_min (&th, option);
261     else if (strcasecmp ("Interesting", option->key) == 0)
262       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
263     else if (strcasecmp ("Invert", option->key) == 0)
264       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
265     else if (strcasecmp ("Persist", option->key) == 0)
266       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
267     else if (strcasecmp ("PersistOK", option->key) == 0)
268       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST_OK);
269     else if (strcasecmp ("Percentage", option->key) == 0)
270       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
271     else if (strcasecmp ("Hits", option->key) == 0)
272       status = ut_config_type_hits (&th, option);
273     else if (strcasecmp ("Hysteresis", option->key) == 0)
274       status = ut_config_type_hysteresis (&th, option);
275     else
276     {
277       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
278           "block.", option->key);
279       status = -1;
280     }
281
282     if (status != 0)
283       break;
284   }
285
286   if (status == 0)
287   {
288     status = ut_threshold_add (&th);
289   }
290
291   return (status);
292 } /* int ut_config_type */
293
294 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
295 {
296   if ((ci->values_num != 1)
297       || (ci->values[0].type != OCONFIG_TYPE_STRING))
298   {
299     WARNING ("threshold values: The `Instance' option needs exactly one "
300         "string argument.");
301     return (-1);
302   }
303
304   sstrncpy (th->plugin_instance, ci->values[0].value.string,
305       sizeof (th->plugin_instance));
306
307   return (0);
308 } /* int ut_config_plugin_instance */
309
310 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
311 {
312   int i;
313   threshold_t th;
314   int status = 0;
315
316   if ((ci->values_num != 1)
317       || (ci->values[0].type != OCONFIG_TYPE_STRING))
318   {
319     WARNING ("threshold values: The `Plugin' block needs exactly one string "
320         "argument.");
321     return (-1);
322   }
323
324   if (ci->children_num < 1)
325   {
326     WARNING ("threshold values: The `Plugin' block needs at least one nested "
327         "block.");
328     return (-1);
329   }
330
331   memcpy (&th, th_orig, sizeof (th));
332   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
333
334   for (i = 0; i < ci->children_num; i++)
335   {
336     oconfig_item_t *option = ci->children + i;
337
338     if (strcasecmp ("Type", option->key) == 0)
339       status = ut_config_type (&th, option);
340     else if (strcasecmp ("Instance", option->key) == 0)
341       status = ut_config_plugin_instance (&th, option);
342     else
343     {
344       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
345           "block.", option->key);
346       status = -1;
347     }
348
349     if (status != 0)
350       break;
351   }
352
353   return (status);
354 } /* int ut_config_plugin */
355
356 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
357 {
358   int i;
359   threshold_t th;
360   int status = 0;
361
362   if ((ci->values_num != 1)
363       || (ci->values[0].type != OCONFIG_TYPE_STRING))
364   {
365     WARNING ("threshold values: The `Host' block needs exactly one string "
366         "argument.");
367     return (-1);
368   }
369
370   if (ci->children_num < 1)
371   {
372     WARNING ("threshold values: The `Host' block needs at least one nested "
373         "block.");
374     return (-1);
375   }
376
377   memcpy (&th, th_orig, sizeof (th));
378   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
379
380   for (i = 0; i < ci->children_num; i++)
381   {
382     oconfig_item_t *option = ci->children + i;
383
384     if (strcasecmp ("Type", option->key) == 0)
385       status = ut_config_type (&th, option);
386     else if (strcasecmp ("Plugin", option->key) == 0)
387       status = ut_config_plugin (&th, option);
388     else
389     {
390       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
391           "block.", option->key);
392       status = -1;
393     }
394
395     if (status != 0)
396       break;
397   }
398
399   return (status);
400 } /* int ut_config_host */
401 /*
402  * End of the functions used to configure threshold values.
403  */
404 /* }}} */
405
406 /*
407  * int ut_report_state
408  *
409  * Checks if the `state' differs from the old state and creates a notification
410  * if appropriate.
411  * Does not fail.
412  */
413 static int ut_report_state (const data_set_t *ds,
414     const value_list_t *vl,
415     const threshold_t *th,
416     const gauge_t *values,
417     int ds_index,
418     int state)
419 { /* {{{ */
420   int state_old;
421   notification_t n;
422
423   char *buf;
424   size_t bufsize;
425
426   int status;
427
428   /* Check if hits matched */
429   if ( (th->hits != 0) )
430   {
431     int hits = uc_get_hits(ds,vl);
432     /* STATE_OKAY resets hits unless PERSIST_OK flag is set. Hits resets if
433      * threshold is hit. */
434     if ( ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) ) || (hits > th->hits) )
435     {
436         DEBUG("ut_report_state: reset uc_get_hits = 0");
437         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
438     } else {
439       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
440       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
441       return (0);
442     }
443   } /* end check hits */
444
445   state_old = uc_get_state (ds, vl);
446
447   /* If the state didn't change, report if `persistent' is specified. If the
448    * state is `okay', then only report if `persist_ok` flag is set. */
449   if (state == state_old)
450   {
451     if ((th->flags & UT_FLAG_PERSIST) == 0)
452       return (0);
453     else if ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) )
454       return (0);
455   }
456
457   if (state != state_old)
458     uc_set_state (ds, vl, state);
459
460   NOTIFICATION_INIT_VL (&n, vl);
461
462   buf = n.message;
463   bufsize = sizeof (n.message);
464
465   if (state == STATE_OKAY)
466     n.severity = NOTIF_OKAY;
467   else if (state == STATE_WARNING)
468     n.severity = NOTIF_WARNING;
469   else
470     n.severity = NOTIF_FAILURE;
471
472   n.time = vl->time;
473
474   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
475       vl->host, vl->plugin);
476   buf += status;
477   bufsize -= status;
478
479   if (vl->plugin_instance[0] != '\0')
480   {
481     status = ssnprintf (buf, bufsize, " (instance %s)",
482         vl->plugin_instance);
483     buf += status;
484     bufsize -= status;
485   }
486
487   status = ssnprintf (buf, bufsize, " type %s", vl->type);
488   buf += status;
489   bufsize -= status;
490
491   if (vl->type_instance[0] != '\0')
492   {
493     status = ssnprintf (buf, bufsize, " (instance %s)",
494         vl->type_instance);
495     buf += status;
496     bufsize -= status;
497   }
498
499   plugin_notification_meta_add_string (&n, "DataSource",
500       ds->ds[ds_index].name);
501   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
502   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
503   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
504   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
505   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
506
507   /* Send an okay notification */
508   if (state == STATE_OKAY)
509   {
510     if (state_old == STATE_MISSING)
511       ssnprintf (buf, bufsize, ": Value is no longer missing.");
512     else
513       ssnprintf (buf, bufsize,
514           ": All data sources are within range again. "
515           "Current value of \"%s\" is %f.",
516           ds->ds[ds_index].name, values[ds_index]);
517   }
518   else
519   {
520     double min;
521     double max;
522
523     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
524     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
525
526     if (th->flags & UT_FLAG_INVERT)
527     {
528       if (!isnan (min) && !isnan (max))
529       {
530         ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
531             "%f. That is within the %s region of %f%s and %f%s.",
532             ds->ds[ds_index].name, values[ds_index],
533             (state == STATE_ERROR) ? "failure" : "warning",
534             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
535             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
536       }
537       else
538       {
539         ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
540             "%f. That is %s the %s threshold of %f%s.",
541             ds->ds[ds_index].name, values[ds_index],
542             isnan (min) ? "below" : "above",
543             (state == STATE_ERROR) ? "failure" : "warning",
544             isnan (min) ? max : min,
545             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
546       }
547     }
548     else if (th->flags & UT_FLAG_PERCENTAGE)
549     {
550       gauge_t value;
551       gauge_t sum;
552       size_t i;
553
554       sum = 0.0;
555       for (i = 0; i < vl->values_len; i++)
556       {
557         if (isnan (values[i]))
558           continue;
559
560         sum += values[i];
561       }
562
563       if (sum == 0.0)
564         value = NAN;
565       else
566         value = 100.0 * values[ds_index] / sum;
567
568       ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
569           "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
570           ds->ds[ds_index].name, values[ds_index], value,
571           (value < min) ? "below" : "above",
572           (state == STATE_ERROR) ? "failure" : "warning",
573           (value < min) ? min : max);
574     }
575     else /* is not inverted */
576     {
577       ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
578           "%f. That is %s the %s threshold of %f.",
579           ds->ds[ds_index].name, values[ds_index],
580           (values[ds_index] < min) ? "below" : "above",
581           (state == STATE_ERROR) ? "failure" : "warning",
582           (values[ds_index] < min) ? min : max);
583     }
584   }
585
586   plugin_dispatch_notification (&n);
587
588   plugin_notification_meta_free (n.meta);
589   return (0);
590 } /* }}} int ut_report_state */
591
592 /*
593  * int ut_check_one_data_source
594  *
595  * Checks one data source against the given threshold configuration. If the
596  * `DataSource' option is set in the threshold, and the name does NOT match,
597  * `okay' is returned. If the threshold does match, its failure and warning
598  * min and max values are checked and `failure' or `warning' is returned if
599  * appropriate.
600  * Does not fail.
601  */
602 static int ut_check_one_data_source (const data_set_t *ds,
603     const value_list_t __attribute__((unused)) *vl,
604     const threshold_t *th,
605     const gauge_t *values,
606     int ds_index)
607 { /* {{{ */
608   const char *ds_name;
609   int is_warning = 0;
610   int is_failure = 0;
611   int prev_state = STATE_OKAY;
612
613   /* check if this threshold applies to this data source */
614   if (ds != NULL)
615   {
616     ds_name = ds->ds[ds_index].name;
617     if ((th->data_source[0] != 0)
618         && (strcmp (ds_name, th->data_source) != 0))
619       return (STATE_OKAY);
620   }
621
622   if ((th->flags & UT_FLAG_INVERT) != 0)
623   {
624     is_warning--;
625     is_failure--;
626   }
627
628   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
629    * and probably, do not work as you expect. Enjoy! :D */
630   if (th->hysteresis > 0)
631   {
632     prev_state = uc_get_state(ds,vl);
633     /* The purpose of hysteresis is elliminating flapping state when the value
634      * oscilates around the thresholds. In other words, what is important is
635      * the previous state; if the new value would trigger a transition, make
636      * sure that we artificially widen the range which is considered to apply
637      * for the previous state, and only trigger the notification if the value
638      * is outside of this expanded range.
639      *
640      * There is no hysteresis for the OKAY state.
641      * */
642     gauge_t hysteresis_for_warning = 0, hysteresis_for_failure = 0;
643     switch (prev_state)
644     {
645       case STATE_ERROR:
646         hysteresis_for_failure = th->hysteresis;
647         break;
648       case STATE_WARNING:
649         hysteresis_for_warning = th->hysteresis;
650         break;
651       case STATE_OKAY:
652         /* do nothing -- the hysteresis only applies to the non-normal states */
653         break;
654     }
655
656     if ((!isnan (th->failure_min) && (th->failure_min + hysteresis_for_failure > values[ds_index]))
657         || (!isnan (th->failure_max) && (th->failure_max - hysteresis_for_failure < values[ds_index])))
658       is_failure++;
659
660     if ((!isnan (th->warning_min) && (th->warning_min + hysteresis_for_warning > values[ds_index]))
661         || (!isnan (th->warning_max) && (th->warning_max - hysteresis_for_warning < values[ds_index])))
662       is_warning++;
663
664   }
665   else { /* no hysteresis */
666     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
667         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
668       is_failure++;
669
670     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
671         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
672       is_warning++;
673   }
674
675   if (is_failure != 0)
676     return (STATE_ERROR);
677
678   if (is_warning != 0)
679     return (STATE_WARNING);
680
681   return (STATE_OKAY);
682 } /* }}} int ut_check_one_data_source */
683
684 /*
685  * int ut_check_one_threshold
686  *
687  * Checks all data sources of a value list against the given threshold, using
688  * the ut_check_one_data_source function above. Returns the worst status,
689  * which is `okay' if nothing has failed.
690  * Returns less than zero if the data set doesn't have any data sources.
691  */
692 static int ut_check_one_threshold (const data_set_t *ds,
693     const value_list_t *vl,
694     const threshold_t *th,
695     const gauge_t *values,
696     int *ret_ds_index)
697 { /* {{{ */
698   int ret = -1;
699   int ds_index = -1;
700   size_t i;
701   gauge_t values_copy[ds->ds_num];
702
703   memcpy (values_copy, values, sizeof (values_copy));
704
705   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
706   {
707     int num = 0;
708     gauge_t sum=0.0;
709
710     if (ds->ds_num == 1)
711     {
712       WARNING ("ut_check_one_threshold: The %s type has only one data "
713           "source, but you have configured to check this as a percentage. "
714           "That doesn't make much sense, because the percentage will always "
715           "be 100%%!", ds->type);
716     }
717
718     /* Prepare `sum' and `num'. */
719     for (i = 0; i < ds->ds_num; i++)
720       if (!isnan (values[i]))
721       {
722         num++;
723         sum += values[i];
724       }
725
726     if ((num == 0) /* All data sources are undefined. */
727         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
728     {
729       for (i = 0; i < ds->ds_num; i++)
730         values_copy[i] = NAN;
731     }
732     else /* We can actually calculate the percentage. */
733     {
734       for (i = 0; i < ds->ds_num; i++)
735         values_copy[i] = 100.0 * values[i] / sum;
736     }
737   } /* if (UT_FLAG_PERCENTAGE) */
738
739   for (i = 0; i < ds->ds_num; i++)
740   {
741     int status;
742
743     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
744     if (ret < status)
745     {
746       ret = status;
747       ds_index = i;
748     }
749   } /* for (ds->ds_num) */
750
751   if (ret_ds_index != NULL)
752     *ret_ds_index = ds_index;
753
754   return (ret);
755 } /* }}} int ut_check_one_threshold */
756
757 /*
758  * int ut_check_threshold
759  *
760  * Gets a list of matching thresholds and searches for the worst status by one
761  * of the thresholds. Then reports that status using the ut_report_state
762  * function above.
763  * Returns zero on success and if no threshold has been configured. Returns
764  * less than zero on failure.
765  */
766 static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
767     __attribute__((unused)) user_data_t *ud)
768 { /* {{{ */
769   threshold_t *th;
770   gauge_t *values;
771   int status;
772
773   int worst_state = -1;
774   threshold_t *worst_th = NULL;
775   int worst_ds_index = -1;
776
777   if (threshold_tree == NULL)
778     return (0);
779
780   /* Is this lock really necessary? So far, thresholds are only inserted at
781    * startup. -octo */
782   pthread_mutex_lock (&threshold_lock);
783   th = threshold_search (vl);
784   pthread_mutex_unlock (&threshold_lock);
785   if (th == NULL)
786     return (0);
787
788   DEBUG ("ut_check_threshold: Found matching threshold(s)");
789
790   values = uc_get_rate (ds, vl);
791   if (values == NULL)
792     return (0);
793
794   while (th != NULL)
795   {
796     int ds_index = -1;
797
798     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
799     if (status < 0)
800     {
801       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
802       sfree (values);
803       return (-1);
804     }
805
806     if (worst_state < status)
807     {
808       worst_state = status;
809       worst_th = th;
810       worst_ds_index = ds_index;
811     }
812
813     th = th->next;
814   } /* while (th) */
815
816   status = ut_report_state (ds, vl, worst_th, values,
817       worst_ds_index, worst_state);
818   if (status != 0)
819   {
820     ERROR ("ut_check_threshold: ut_report_state failed.");
821     sfree (values);
822     return (-1);
823   }
824
825   sfree (values);
826
827   return (0);
828 } /* }}} int ut_check_threshold */
829
830 /*
831  * int ut_missing
832  *
833  * This function is called whenever a value goes "missing".
834  */
835 static int ut_missing (const value_list_t *vl,
836     __attribute__((unused)) user_data_t *ud)
837 { /* {{{ */
838   threshold_t *th;
839   cdtime_t missing_time;
840   char identifier[6 * DATA_MAX_NAME_LEN];
841   notification_t n;
842   cdtime_t now;
843
844   if (threshold_tree == NULL)
845     return (0);
846
847   th = threshold_search (vl);
848   /* dispatch notifications for "interesting" values only */
849   if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
850     return (0);
851
852   now = cdtime ();
853   missing_time = now - vl->time;
854   FORMAT_VL (identifier, sizeof (identifier), vl);
855
856   NOTIFICATION_INIT_VL (&n, vl);
857   ssnprintf (n.message, sizeof (n.message),
858       "%s has not been updated for %.3f seconds.",
859       identifier, CDTIME_T_TO_DOUBLE (missing_time));
860   n.time = now;
861
862   plugin_dispatch_notification (&n);
863
864   return (0);
865 } /* }}} int ut_missing */
866
867 static int ut_config (oconfig_item_t *ci)
868 { /* {{{ */
869   int i;
870   int status = 0;
871   int old_size = c_avl_size (threshold_tree);
872
873   if (threshold_tree == NULL)
874   {
875     threshold_tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
876     if (threshold_tree == NULL)
877     {
878       ERROR ("ut_config: c_avl_create failed.");
879       return (-1);
880     }
881   }
882
883   threshold_t th = {
884     .warning_min = NAN,
885     .warning_max = NAN,
886     .failure_min = NAN,
887     .failure_max = NAN,
888     .flags = UT_FLAG_INTERESTING /* interesting by default */
889   };
890
891   for (i = 0; i < ci->children_num; i++)
892   {
893     oconfig_item_t *option = ci->children + i;
894
895     if (strcasecmp ("Type", option->key) == 0)
896       status = ut_config_type (&th, option);
897     else if (strcasecmp ("Plugin", option->key) == 0)
898       status = ut_config_plugin (&th, option);
899     else if (strcasecmp ("Host", option->key) == 0)
900       status = ut_config_host (&th, option);
901     else
902     {
903       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
904       status = -1;
905     }
906
907     if (status != 0)
908       break;
909   }
910
911   /* register callbacks if this is the first time we see a valid config */
912   if ((old_size == 0) && (c_avl_size (threshold_tree) > 0))
913   {
914     plugin_register_missing ("threshold", ut_missing,
915         /* user data = */ NULL);
916     plugin_register_write ("threshold", ut_check_threshold,
917         /* user data = */ NULL);
918   }
919
920   return (status);
921 } /* }}} int um_config */
922
923 void module_register (void)
924 {
925   plugin_register_complex_config ("threshold", ut_config);
926 }
927
928 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */