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