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 * Copyright (C) 2014 Pierre-Yves Ritschard
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; only version 2 of the License is applicable.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Pierre-Yves Ritschard <pyr at spootnik.org>
23 * Florian octo Forster <octo at collectd.org>
24 * Sebastian Harl <sh at tokkee.org>
25 * Andrés J. Díaz <ajdiaz at connectical.com>
31 #include "utils_avltree.h"
32 #include "utils_cache.h"
38 * Private data structures
40 #define UT_FLAG_INVERT 0x01
41 #define UT_FLAG_PERSIST 0x02
42 #define UT_FLAG_PERCENTAGE 0x04
43 #define UT_FLAG_INTERESTING 0x08
44 #define UT_FLAG_PERSIST_OK 0x10
45 typedef struct threshold_s
47 char host[DATA_MAX_NAME_LEN];
48 char plugin[DATA_MAX_NAME_LEN];
49 char plugin_instance[DATA_MAX_NAME_LEN];
50 char type[DATA_MAX_NAME_LEN];
51 char type_instance[DATA_MAX_NAME_LEN];
52 char data_source[DATA_MAX_NAME_LEN];
60 struct threshold_s *next;
65 * Private (static) variables
67 static c_avl_tree_t *threshold_tree = NULL;
68 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
72 * Threshold management
73 * ====================
74 * The following functions add, delete, search, etc. configured thresholds to
75 * the underlying AVL trees.
78 * threshold_t *threshold_get
80 * Retrieve one specific threshold configuration. For looking up a threshold
81 * matching a value_list_t, see "threshold_search" below. Returns NULL if the
82 * specified threshold doesn't exist.
84 static threshold_t *threshold_get (const char *hostname,
85 const char *plugin, const char *plugin_instance,
86 const char *type, const char *type_instance)
88 char name[6 * DATA_MAX_NAME_LEN];
89 threshold_t *th = NULL;
91 format_name (name, sizeof (name),
92 (hostname == NULL) ? "" : hostname,
93 (plugin == NULL) ? "" : plugin, plugin_instance,
94 (type == NULL) ? "" : type, type_instance);
95 name[sizeof (name) - 1] = '\0';
97 if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
101 } /* }}} threshold_t *threshold_get */
104 * int ut_threshold_add
106 * Adds a threshold configuration to the list of thresholds. The threshold_t
107 * structure is copied and may be destroyed after this call. Returns zero on
108 * success, non-zero otherwise.
110 static int ut_threshold_add (const threshold_t *th)
112 char name[6 * DATA_MAX_NAME_LEN];
114 threshold_t *th_copy;
118 if (format_name (name, sizeof (name), th->host,
119 th->plugin, th->plugin_instance,
120 th->type, th->type_instance) != 0)
122 ERROR ("ut_threshold_add: format_name failed.");
126 name_copy = strdup (name);
127 if (name_copy == NULL)
129 ERROR ("ut_threshold_add: strdup failed.");
133 th_copy = (threshold_t *) malloc (sizeof (threshold_t));
137 ERROR ("ut_threshold_add: malloc failed.");
140 memcpy (th_copy, th, sizeof (threshold_t));
143 DEBUG ("ut_threshold_add: Adding entry `%s'", name);
145 pthread_mutex_lock (&threshold_lock);
147 th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
148 th->type, th->type_instance);
150 while ((th_ptr != NULL) && (th_ptr->next != NULL))
151 th_ptr = th_ptr->next;
153 if (th_ptr == NULL) /* no such threshold yet */
155 status = c_avl_insert (threshold_tree, name_copy, th_copy);
157 else /* th_ptr points to the last threshold in the list */
159 th_ptr->next = th_copy;
160 /* name_copy isn't needed */
164 pthread_mutex_unlock (&threshold_lock);
168 ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
174 } /* }}} int ut_threshold_add */
177 * threshold_t *threshold_search
179 * Searches for a threshold configuration using all the possible variations of
180 * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be
182 * XXX: This is likely the least efficient function in collectd.
184 static threshold_t *threshold_search (const value_list_t *vl)
188 if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
189 vl->type, vl->type_instance)) != NULL)
191 else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
192 vl->type, NULL)) != NULL)
194 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
195 vl->type, vl->type_instance)) != NULL)
197 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
198 vl->type, NULL)) != NULL)
200 else if ((th = threshold_get (vl->host, "", NULL,
201 vl->type, vl->type_instance)) != NULL)
203 else if ((th = threshold_get (vl->host, "", NULL,
204 vl->type, NULL)) != NULL)
206 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
207 vl->type, vl->type_instance)) != NULL)
209 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
210 vl->type, NULL)) != NULL)
212 else if ((th = threshold_get ("", vl->plugin, NULL,
213 vl->type, vl->type_instance)) != NULL)
215 else if ((th = threshold_get ("", vl->plugin, NULL,
216 vl->type, NULL)) != NULL)
218 else if ((th = threshold_get ("", "", NULL,
219 vl->type, vl->type_instance)) != NULL)
221 else if ((th = threshold_get ("", "", NULL,
222 vl->type, NULL)) != NULL)
226 } /* }}} threshold_t *threshold_search */
231 * The following approximately two hundred functions are used to handle the
232 * configuration and fill the threshold list.
234 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
236 if ((ci->values_num != 1)
237 || (ci->values[0].type != OCONFIG_TYPE_STRING))
239 WARNING ("threshold values: The `DataSource' option needs exactly one "
244 sstrncpy (th->data_source, ci->values[0].value.string,
245 sizeof (th->data_source));
248 } /* int ut_config_type_datasource */
250 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
252 if ((ci->values_num != 1)
253 || (ci->values[0].type != OCONFIG_TYPE_STRING))
255 WARNING ("threshold values: The `Instance' option needs exactly one "
260 sstrncpy (th->type_instance, ci->values[0].value.string,
261 sizeof (th->type_instance));
264 } /* int ut_config_type_instance */
266 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
268 if ((ci->values_num != 1)
269 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
271 WARNING ("threshold values: The `%s' option needs exactly one "
272 "number argument.", ci->key);
276 if (strcasecmp (ci->key, "WarningMax") == 0)
277 th->warning_max = ci->values[0].value.number;
279 th->failure_max = ci->values[0].value.number;
282 } /* int ut_config_type_max */
284 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
286 if ((ci->values_num != 1)
287 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
289 WARNING ("threshold values: The `%s' option needs exactly one "
290 "number argument.", ci->key);
294 if (strcasecmp (ci->key, "WarningMin") == 0)
295 th->warning_min = ci->values[0].value.number;
297 th->failure_min = ci->values[0].value.number;
300 } /* int ut_config_type_min */
302 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
304 if ((ci->values_num != 1)
305 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
307 WARNING ("threshold values: The `%s' option needs exactly one "
308 "number argument.", ci->key);
312 th->hits = ci->values[0].value.number;
315 } /* int ut_config_type_hits */
317 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
319 if ((ci->values_num != 1)
320 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
322 WARNING ("threshold values: The `%s' option needs exactly one "
323 "number argument.", ci->key);
327 th->hysteresis = ci->values[0].value.number;
330 } /* int ut_config_type_hysteresis */
332 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
338 if ((ci->values_num != 1)
339 || (ci->values[0].type != OCONFIG_TYPE_STRING))
341 WARNING ("threshold values: The `Type' block needs exactly one string "
346 if (ci->children_num < 1)
348 WARNING ("threshold values: The `Type' block needs at least one option.");
352 memcpy (&th, th_orig, sizeof (th));
353 sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
355 th.warning_min = NAN;
356 th.warning_max = NAN;
357 th.failure_min = NAN;
358 th.failure_max = NAN;
361 th.flags = UT_FLAG_INTERESTING; /* interesting by default */
363 for (i = 0; i < ci->children_num; i++)
365 oconfig_item_t *option = ci->children + i;
368 if (strcasecmp ("Instance", option->key) == 0)
369 status = ut_config_type_instance (&th, option);
370 else if (strcasecmp ("DataSource", option->key) == 0)
371 status = ut_config_type_datasource (&th, option);
372 else if ((strcasecmp ("WarningMax", option->key) == 0)
373 || (strcasecmp ("FailureMax", option->key) == 0))
374 status = ut_config_type_max (&th, option);
375 else if ((strcasecmp ("WarningMin", option->key) == 0)
376 || (strcasecmp ("FailureMin", option->key) == 0))
377 status = ut_config_type_min (&th, option);
378 else if (strcasecmp ("Interesting", option->key) == 0)
379 status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
380 else if (strcasecmp ("Invert", option->key) == 0)
381 status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
382 else if (strcasecmp ("Persist", option->key) == 0)
383 status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
384 else if (strcasecmp ("PersistOK", option->key) == 0)
385 status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST_OK);
386 else if (strcasecmp ("Percentage", option->key) == 0)
387 status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
388 else if (strcasecmp ("Hits", option->key) == 0)
389 status = ut_config_type_hits (&th, option);
390 else if (strcasecmp ("Hysteresis", option->key) == 0)
391 status = ut_config_type_hysteresis (&th, option);
394 WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
395 "block.", option->key);
405 status = ut_threshold_add (&th);
409 } /* int ut_config_type */
411 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
413 if ((ci->values_num != 1)
414 || (ci->values[0].type != OCONFIG_TYPE_STRING))
416 WARNING ("threshold values: The `Instance' option needs exactly one "
421 sstrncpy (th->plugin_instance, ci->values[0].value.string,
422 sizeof (th->plugin_instance));
425 } /* int ut_config_plugin_instance */
427 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
433 if ((ci->values_num != 1)
434 || (ci->values[0].type != OCONFIG_TYPE_STRING))
436 WARNING ("threshold values: The `Plugin' block needs exactly one string "
441 if (ci->children_num < 1)
443 WARNING ("threshold values: The `Plugin' block needs at least one nested "
448 memcpy (&th, th_orig, sizeof (th));
449 sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
451 for (i = 0; i < ci->children_num; i++)
453 oconfig_item_t *option = ci->children + i;
456 if (strcasecmp ("Type", option->key) == 0)
457 status = ut_config_type (&th, option);
458 else if (strcasecmp ("Instance", option->key) == 0)
459 status = ut_config_plugin_instance (&th, option);
462 WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
463 "block.", option->key);
472 } /* int ut_config_plugin */
474 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
480 if ((ci->values_num != 1)
481 || (ci->values[0].type != OCONFIG_TYPE_STRING))
483 WARNING ("threshold values: The `Host' block needs exactly one string "
488 if (ci->children_num < 1)
490 WARNING ("threshold values: The `Host' block needs at least one nested "
495 memcpy (&th, th_orig, sizeof (th));
496 sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
498 for (i = 0; i < ci->children_num; i++)
500 oconfig_item_t *option = ci->children + i;
503 if (strcasecmp ("Type", option->key) == 0)
504 status = ut_config_type (&th, option);
505 else if (strcasecmp ("Plugin", option->key) == 0)
506 status = ut_config_plugin (&th, option);
509 WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
510 "block.", option->key);
519 } /* int ut_config_host */
521 * End of the functions used to configure threshold values.
526 * int ut_check_one_data_source
528 * Checks one data source against the given threshold configuration. If the
529 * `DataSource' option is set in the threshold, and the name does NOT match,
530 * `okay' is returned. If the threshold does match, its failure and warning
531 * min and max values are checked and `failure' or `warning' is returned if
535 static int ut_check_one_data_source (const data_set_t *ds,
536 const value_list_t __attribute__((unused)) *vl,
537 const threshold_t *th,
538 const gauge_t *values,
544 int prev_state = STATE_OKAY;
546 /* check if this threshold applies to this data source */
549 ds_name = ds->ds[ds_index].name;
550 if ((th->data_source[0] != 0)
551 && (strcmp (ds_name, th->data_source) != 0))
555 if ((th->flags & UT_FLAG_INVERT) != 0)
561 /* XXX: This is an experimental code, not optimized, not fast, not reliable,
562 * and probably, do not work as you expect. Enjoy! :D */
563 if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
568 if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
569 (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
574 if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
575 (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
581 else { /* no hysteresis */
582 if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
583 || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
586 if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
587 || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
592 return (STATE_ERROR);
595 return (STATE_WARNING);
598 } /* }}} int ut_check_one_data_source */
601 * int ut_check_one_threshold
603 * Checks all data sources of a value list against the given threshold, using
604 * the ut_check_one_data_source function above. Returns the worst status,
605 * which is `okay' if nothing has failed.
606 * Returns less than zero if the data set doesn't have any data sources.
608 static int ut_check_one_threshold (const data_set_t *ds,
609 const value_list_t *vl,
610 const threshold_t *th,
611 const gauge_t *values,
617 gauge_t values_copy[ds->ds_num];
619 memcpy (values_copy, values, sizeof (values_copy));
621 if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
628 WARNING ("ut_check_one_threshold: The %s type has only one data "
629 "source, but you have configured to check this as a percentage. "
630 "That doesn't make much sense, because the percentage will always "
631 "be 100%%!", ds->type);
634 /* Prepare `sum' and `num'. */
635 for (i = 0; i < ds->ds_num; i++)
636 if (!isnan (values[i]))
642 if ((num == 0) /* All data sources are undefined. */
643 || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
645 for (i = 0; i < ds->ds_num; i++)
646 values_copy[i] = NAN;
648 else /* We can actually calculate the percentage. */
650 for (i = 0; i < ds->ds_num; i++)
651 values_copy[i] = 100.0 * values[i] / sum;
653 } /* if (UT_FLAG_PERCENTAGE) */
655 for (i = 0; i < ds->ds_num; i++)
657 status = ut_check_one_data_source (ds, vl, th, values_copy, i);
660 if (statuses[i] < status)
661 statuses[i] = status;
663 } /* for (ds->ds_num) */
666 } /* }}} int ut_check_one_threshold */
669 * int ut_check_threshold
671 * Gets a list of matching thresholds and searches for the worst status by one
672 * of the thresholds. Then reports that status using the ut_report_state
674 * Returns zero on success and if no threshold has been configured. Returns
675 * less than zero on failure.
677 int write_riemann_threshold_check (const data_set_t *ds, const value_list_t *vl,
684 memset(statuses, 0, vl->values_len * sizeof(*statuses));
687 if (threshold_tree == NULL)
690 /* Is this lock really necessary? So far, thresholds are only inserted at
692 pthread_mutex_lock (&threshold_lock);
693 th = threshold_search (vl);
694 pthread_mutex_unlock (&threshold_lock);
698 DEBUG ("ut_check_threshold: Found matching threshold(s)");
700 values = uc_get_rate (ds, vl);
706 status = ut_check_one_threshold (ds, vl, th, values, statuses);
709 ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
720 } /* }}} int ut_check_threshold */
722 int write_riemann_threshold_config (oconfig_item_t *ci)
729 if (threshold_tree == NULL)
731 threshold_tree = c_avl_create ((void *) strcmp);
732 if (threshold_tree == NULL)
734 ERROR ("ut_config: c_avl_create failed.");
739 memset (&th, '\0', sizeof (th));
740 th.warning_min = NAN;
741 th.warning_max = NAN;
742 th.failure_min = NAN;
743 th.failure_max = NAN;
747 th.flags = UT_FLAG_INTERESTING; /* interesting by default */
749 for (i = 0; i < ci->children_num; i++)
751 oconfig_item_t *option = ci->children + i;
754 if (strcasecmp ("Type", option->key) == 0)
755 status = ut_config_type (&th, option);
756 else if (strcasecmp ("Plugin", option->key) == 0)
757 status = ut_config_plugin (&th, option);
758 else if (strcasecmp ("Host", option->key) == 0)
759 status = ut_config_host (&th, option);
762 WARNING ("threshold values: Option `%s' not allowed here.", option->key);
771 } /* }}} int um_config */
773 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */