2 * collectd - src/utils_threshold.c
3 * Copyright (C) 2007 Florian octo Forster
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.
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.
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
19 * Florian octo Forster <octo at verplant.org>
25 #include "utils_avltree.h"
26 #include "utils_cache.h"
32 * Private data structures
34 #define UT_FLAG_INVERT 0x01
35 #define UT_FLAG_PERSIST 0x02
37 typedef struct threshold_s
39 char host[DATA_MAX_NAME_LEN];
40 char plugin[DATA_MAX_NAME_LEN];
41 char plugin_instance[DATA_MAX_NAME_LEN];
42 char type[DATA_MAX_NAME_LEN];
43 char type_instance[DATA_MAX_NAME_LEN];
51 * Private (static) variables
53 static avl_tree_t *threshold_tree = NULL;
54 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
58 * Threshold management
59 * ====================
60 * The following functions add, delete, search, etc. configured thresholds to
61 * the underlying AVL trees.
63 static int ut_threshold_add (const threshold_t *th)
65 char name[6 * DATA_MAX_NAME_LEN];
70 if (format_name (name, sizeof (name), th->host,
71 th->plugin, th->plugin_instance,
72 th->type, th->type_instance) != 0)
74 ERROR ("ut_threshold_add: format_name failed.");
78 name_copy = strdup (name);
79 if (name_copy == NULL)
81 ERROR ("ut_threshold_add: strdup failed.");
85 th_copy = (threshold_t *) malloc (sizeof (threshold_t));
89 ERROR ("ut_threshold_add: malloc failed.");
92 memcpy (th_copy, th, sizeof (threshold_t));
94 DEBUG ("ut_threshold_add: Adding entry `%s'", name);
96 pthread_mutex_lock (&threshold_lock);
97 status = avl_insert (threshold_tree, name_copy, th_copy);
98 pthread_mutex_unlock (&threshold_lock);
102 ERROR ("ut_threshold_add: avl_insert (%s) failed.", name);
108 } /* int ut_threshold_add */
110 * End of the threshold management functions
116 * The following approximately two hundred functions are used to handle the
117 * configuration and fill the threshold list.
119 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
121 if ((ci->values_num != 1)
122 || (ci->values[0].type != OCONFIG_TYPE_STRING))
124 WARNING ("threshold values: The `Instance' option needs exactly one "
129 strncpy (th->type_instance, ci->values[0].value.string,
130 sizeof (th->type_instance));
131 th->type_instance[sizeof (th->type_instance) - 1] = '\0';
134 } /* int ut_config_type_instance */
136 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
138 if ((ci->values_num != 1)
139 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
141 WARNING ("threshold values: The `Max' option needs exactly one "
146 th->max = ci->values[0].value.number;
149 } /* int ut_config_type_max */
151 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
153 if ((ci->values_num != 1)
154 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
156 WARNING ("threshold values: The `Min' option needs exactly one "
161 th->min = ci->values[0].value.number;
164 } /* int ut_config_type_min */
166 static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
168 if ((ci->values_num != 1)
169 || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
171 WARNING ("threshold values: The `Invert' option needs exactly one "
172 "boolean argument.");
176 if (ci->values[0].value.boolean)
177 th->flags |= UT_FLAG_INVERT;
179 th->flags &= ~UT_FLAG_INVERT;
182 } /* int ut_config_type_invert */
184 static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
186 if ((ci->values_num != 1)
187 || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
189 WARNING ("threshold values: The `Persist' option needs exactly one "
190 "boolean argument.");
194 if (ci->values[0].value.boolean)
195 th->flags |= UT_FLAG_PERSIST;
197 th->flags &= ~UT_FLAG_PERSIST;
200 } /* int ut_config_type_persist */
202 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
208 if ((ci->values_num != 1)
209 || (ci->values[0].type != OCONFIG_TYPE_STRING))
211 WARNING ("threshold values: The `Type' block needs exactly one string "
216 if (ci->children_num < 1)
218 WARNING ("threshold values: The `Type' block needs at least one option.");
222 memcpy (&th, th_orig, sizeof (th));
223 strncpy (th.type, ci->values[0].value.string, sizeof (th.type));
224 th.type[sizeof (th.type) - 1] = '\0';
229 for (i = 0; i < ci->children_num; i++)
231 oconfig_item_t *option = ci->children + i;
234 if (strcasecmp ("Instance", option->key) == 0)
235 status = ut_config_type_instance (&th, option);
236 else if (strcasecmp ("Max", option->key) == 0)
237 status = ut_config_type_max (&th, option);
238 else if (strcasecmp ("Min", option->key) == 0)
239 status = ut_config_type_min (&th, option);
240 else if (strcasecmp ("Invert", option->key) == 0)
241 status = ut_config_type_invert (&th, option);
242 else if (strcasecmp ("Persist", option->key) == 0)
243 status = ut_config_type_persist (&th, option);
246 WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
247 "block.", option->key);
257 status = ut_threshold_add (&th);
261 } /* int ut_config_type */
263 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
265 if ((ci->values_num != 1)
266 || (ci->values[0].type != OCONFIG_TYPE_STRING))
268 WARNING ("threshold values: The `Instance' option needs exactly one "
273 strncpy (th->plugin_instance, ci->values[0].value.string,
274 sizeof (th->plugin_instance));
275 th->plugin_instance[sizeof (th->plugin_instance) - 1] = '\0';
278 } /* int ut_config_plugin_instance */
280 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
286 if ((ci->values_num != 1)
287 || (ci->values[0].type != OCONFIG_TYPE_STRING))
289 WARNING ("threshold values: The `Plugin' block needs exactly one string "
294 if (ci->children_num < 1)
296 WARNING ("threshold values: The `Plugin' block needs at least one nested "
301 memcpy (&th, th_orig, sizeof (th));
302 strncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
303 th.plugin[sizeof (th.plugin) - 1] = '\0';
305 for (i = 0; i < ci->children_num; i++)
307 oconfig_item_t *option = ci->children + i;
310 if (strcasecmp ("Type", option->key) == 0)
311 status = ut_config_type (&th, option);
312 else if (strcasecmp ("Instance", option->key) == 0)
313 status = ut_config_plugin_instance (&th, option);
316 WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
317 "block.", option->key);
326 } /* int ut_config_plugin */
328 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
334 if ((ci->values_num != 1)
335 || (ci->values[0].type != OCONFIG_TYPE_STRING))
337 WARNING ("threshold values: The `Host' block needs exactly one string "
342 if (ci->children_num < 1)
344 WARNING ("threshold values: The `Host' block needs at least one nested "
349 memcpy (&th, th_orig, sizeof (th));
350 strncpy (th.host, ci->values[0].value.string, sizeof (th.host));
351 th.host[sizeof (th.host) - 1] = '\0';
353 for (i = 0; i < ci->children_num; i++)
355 oconfig_item_t *option = ci->children + i;
358 if (strcasecmp ("Type", option->key) == 0)
359 status = ut_config_type (&th, option);
360 else if (strcasecmp ("Plugin", option->key) == 0)
361 status = ut_config_plugin (&th, option);
364 WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
365 "block.", option->key);
374 } /* int ut_config_host */
376 int ut_config (const oconfig_item_t *ci)
383 if (threshold_tree == NULL)
385 threshold_tree = avl_create ((void *) strcmp);
386 if (threshold_tree == NULL)
388 ERROR ("ut_config: avl_create failed.");
393 memset (&th, '\0', sizeof (th));
397 for (i = 0; i < ci->children_num; i++)
399 oconfig_item_t *option = ci->children + i;
402 if (strcasecmp ("Type", option->key) == 0)
403 status = ut_config_type (&th, option);
404 else if (strcasecmp ("Plugin", option->key) == 0)
405 status = ut_config_plugin (&th, option);
406 else if (strcasecmp ("Host", option->key) == 0)
407 status = ut_config_host (&th, option);
410 WARNING ("threshold values: Option `%s' not allowed here.", option->key);
419 } /* int um_config */
421 * End of the functions used to configure threshold values.
425 static threshold_t *threshold_get (const char *hostname,
426 const char *plugin, const char *plugin_instance,
427 const char *type, const char *type_instance)
429 char name[6 * DATA_MAX_NAME_LEN];
430 threshold_t *th = NULL;
432 format_name (name, sizeof (name),
433 (hostname == NULL) ? "" : hostname,
434 (plugin == NULL) ? "" : plugin, plugin_instance,
435 (type == NULL) ? "" : type, type_instance);
436 name[sizeof (name) - 1] = '\0';
438 if (avl_get (threshold_tree, name, (void *) &th) == 0)
442 } /* threshold_t *threshold_get */
444 static threshold_t *threshold_search (const data_set_t *ds,
445 const value_list_t *vl)
449 if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
450 ds->type, vl->type_instance)) != NULL)
452 else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
453 ds->type, NULL)) != NULL)
455 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
456 ds->type, vl->type_instance)) != NULL)
458 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
459 ds->type, NULL)) != NULL)
461 else if ((th = threshold_get (vl->host, "", NULL,
462 ds->type, vl->type_instance)) != NULL)
464 else if ((th = threshold_get (vl->host, "", NULL,
465 ds->type, NULL)) != NULL)
467 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
468 ds->type, vl->type_instance)) != NULL)
470 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
471 ds->type, NULL)) != NULL)
473 else if ((th = threshold_get ("", vl->plugin, NULL,
474 ds->type, vl->type_instance)) != NULL)
476 else if ((th = threshold_get ("", vl->plugin, NULL,
477 ds->type, NULL)) != NULL)
479 else if ((th = threshold_get ("", "", NULL,
480 ds->type, vl->type_instance)) != NULL)
482 else if ((th = threshold_get ("", "", NULL,
483 ds->type, NULL)) != NULL)
487 } /* threshold_t *threshold_search */
489 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
495 if (threshold_tree == NULL)
497 pthread_mutex_lock (&threshold_lock);
498 th = threshold_search (ds, vl);
499 pthread_mutex_unlock (&threshold_lock);
503 DEBUG ("ut_check_threshold: Found matching threshold");
505 values = uc_get_rate (ds, vl);
509 for (i = 0; i < ds->ds_num; i++)
511 int out_of_range = 0;
514 if ((th->flags & UT_FLAG_INVERT) != 0)
516 if ((!isnan (th->min) && (th->min > values[i]))
517 || (!isnan (th->max) && (th->max < values[i])))
520 /* If only one of these conditions is true, there is a problem */
521 if ((out_of_range + is_inverted) == 1)
528 WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
529 ds->ds[i].name, th->min, values[i], th->max,
530 is_inverted ? "true" : "false");
533 bufsize = sizeof (n.message);
535 status = snprintf (buf, bufsize, "Host %s, plugin %s",
536 vl->host, vl->plugin);
540 if (vl->plugin_instance[0] != '\0')
542 status = snprintf (buf, bufsize, " (instance %s)",
543 vl->plugin_instance);
548 status = snprintf (buf, bufsize, " type %s", ds->type);
552 if (vl->type_instance[0] != '\0')
554 status = snprintf (buf, bufsize, " (instance %s)",
562 if (!isnan (th->min) && !isnan (th->max))
564 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
565 "%lf. That is within the critical region of %lf and %lf.",
566 ds->ds[i].name, values[i],
571 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
572 "%lf. That is %s the configured threshold of %lf.",
573 ds->ds[i].name, values[i],
574 isnan (th->min) ? "below" : "above",
575 isnan (th->min) ? th->max : th->min);
578 else /* (!is_inverted) */
580 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
581 "%lf. That is %s the configured threshold of %lf.",
582 ds->ds[i].name, values[i],
583 (values[i] < th->min) ? "below" : "above",
584 (values[i] < th->min) ? th->min : th->max);
589 n.severity = NOTIF_FAILURE;
592 strncpy (n.host, vl->host, sizeof (n.host));
593 n.host[sizeof (n.host) - 1] = '\0';
595 plugin_dispatch_notification (&n);
597 } /* for (i = 0; i < ds->ds_num; i++) */
602 } /* int ut_check_threshold */
604 int ut_check_interesting (const char *name)
606 char *name_copy = NULL;
609 char *plugin_instance = NULL;
611 char *type_instance = NULL;
617 /* If there is no tree nothing is interesting. */
618 if (threshold_tree == NULL)
621 name_copy = strdup (name);
622 if (name_copy == NULL)
624 ERROR ("ut_check_interesting: strdup failed.");
628 status = parse_identifier (name_copy, &host,
629 &plugin, &plugin_instance, &type, &type_instance);
632 ERROR ("ut_check_interesting: parse_identifier failed.");
636 memset (&ds, '\0', sizeof (ds));
637 memset (&vl, '\0', sizeof (vl));
639 strncpy (vl.host, host, sizeof (vl.host));
640 vl.host[sizeof (vl.host) - 1] = '\0';
641 strncpy (vl.plugin, plugin, sizeof (vl.plugin));
642 vl.plugin[sizeof (vl.plugin) - 1] = '\0';
643 if (plugin_instance != NULL)
645 strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
646 vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
648 strncpy (ds.type, type, sizeof (ds.type));
649 ds.type[sizeof (ds.type) - 1] = '\0';
650 if (type_instance != NULL)
652 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
653 vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
657 host = plugin = plugin_instance = type = type_instance = NULL;
659 th = threshold_search (&ds, &vl);
662 if ((th->flags & UT_FLAG_PERSIST) == 0)
665 } /* int ut_check_interesting */
667 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */