1 /**
2 * collectd - src/utils_threshold.c
3 * Copyright (C) 2007 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 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_avltree.h"
26 #include "utils_cache.h"
28 #include <assert.h>
29 #include <pthread.h>
31 /*
32 * Private data structures
33 * {{{ */
34 #define UT_FLAG_INVERT 0x01
35 #define UT_FLAG_PERSIST 0x02
37 typedef struct threshold_s
38 {
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];
44 gauge_t min;
45 gauge_t max;
46 int flags;
47 } threshold_t;
48 /* }}} */
50 /*
51 * Private (static) variables
52 * {{{ */
53 static avl_tree_t *threshold_tree = NULL;
54 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
55 /* }}} */
57 /*
58 * Threshold management
59 * ====================
60 * The following functions add, delete, search, etc. configured thresholds to
61 * the underlying AVL trees.
62 * {{{ */
63 static int ut_threshold_add (const threshold_t *th)
64 {
65 char name[6 * DATA_MAX_NAME_LEN];
66 char *name_copy;
67 threshold_t *th_copy;
68 int status = 0;
70 if (format_name (name, sizeof (name), th->host,
71 th->plugin, th->plugin_instance,
72 th->type, th->type_instance) != 0)
73 {
74 ERROR ("ut_threshold_add: format_name failed.");
75 return (-1);
76 }
78 name_copy = strdup (name);
79 if (name_copy == NULL)
80 {
81 ERROR ("ut_threshold_add: strdup failed.");
82 return (-1);
83 }
85 th_copy = (threshold_t *) malloc (sizeof (threshold_t));
86 if (th_copy == NULL)
87 {
88 sfree (name_copy);
89 ERROR ("ut_threshold_add: malloc failed.");
90 return (-1);
91 }
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);
100 if (status != 0)
101 {
102 ERROR ("ut_threshold_add: avl_insert (%s) failed.", name);
103 sfree (name_copy);
104 sfree (th_copy);
105 }
107 return (status);
108 } /* int ut_threshold_add */
109 /*
110 * End of the threshold management functions
111 * }}} */
113 /*
114 * Configuration
115 * =============
116 * The following approximately two hundred functions are used to handle the
117 * configuration and fill the threshold list.
118 * {{{ */
119 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
120 {
121 if ((ci->values_num != 1)
122 || (ci->values[0].type != OCONFIG_TYPE_STRING))
123 {
124 WARNING ("threshold values: The `Instance' option needs exactly one "
125 "string argument.");
126 return (-1);
127 }
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';
133 return (0);
134 } /* int ut_config_type_instance */
136 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
137 {
138 if ((ci->values_num != 1)
139 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
140 {
141 WARNING ("threshold values: The `Max' option needs exactly one "
142 "number argument.");
143 return (-1);
144 }
146 th->max = ci->values[0].value.number;
148 return (0);
149 } /* int ut_config_type_max */
151 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
152 {
153 if ((ci->values_num != 1)
154 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
155 {
156 WARNING ("threshold values: The `Min' option needs exactly one "
157 "number argument.");
158 return (-1);
159 }
161 th->min = ci->values[0].value.number;
163 return (0);
164 } /* int ut_config_type_min */
166 static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
167 {
168 if ((ci->values_num != 1)
169 || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
170 {
171 WARNING ("threshold values: The `Invert' option needs exactly one "
172 "boolean argument.");
173 return (-1);
174 }
176 if (ci->values[0].value.boolean)
177 th->flags |= UT_FLAG_INVERT;
178 else
179 th->flags &= ~UT_FLAG_INVERT;
181 return (0);
182 } /* int ut_config_type_invert */
184 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
185 {
186 int i;
187 threshold_t th;
188 int status = 0;
190 if ((ci->values_num != 1)
191 || (ci->values[0].type != OCONFIG_TYPE_STRING))
192 {
193 WARNING ("threshold values: The `Type' block needs exactly one string "
194 "argument.");
195 return (-1);
196 }
198 if (ci->children_num < 1)
199 {
200 WARNING ("threshold values: The `Type' block needs at least one option.");
201 return (-1);
202 }
204 memcpy (&th, th_orig, sizeof (th));
205 strncpy (th.type, ci->values[0].value.string, sizeof (th.type));
206 th.type[sizeof (th.type) - 1] = '\0';
208 th.min = NAN;
209 th.max = NAN;
211 for (i = 0; i < ci->children_num; i++)
212 {
213 oconfig_item_t *option = ci->children + i;
214 status = 0;
216 if (strcasecmp ("Instance", option->key) == 0)
217 status = ut_config_type_instance (&th, option);
218 else if (strcasecmp ("Max", option->key) == 0)
219 status = ut_config_type_max (&th, option);
220 else if (strcasecmp ("Min", option->key) == 0)
221 status = ut_config_type_min (&th, option);
222 else if (strcasecmp ("Invert", option->key) == 0)
223 status = ut_config_type_invert (&th, option);
224 else
225 {
226 WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
227 "block.", option->key);
228 status = -1;
229 }
231 if (status != 0)
232 break;
233 }
235 if (status == 0)
236 {
237 status = ut_threshold_add (&th);
238 }
240 return (status);
241 } /* int ut_config_type */
243 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
244 {
245 if ((ci->values_num != 1)
246 || (ci->values[0].type != OCONFIG_TYPE_STRING))
247 {
248 WARNING ("threshold values: The `Instance' option needs exactly one "
249 "string argument.");
250 return (-1);
251 }
253 strncpy (th->plugin_instance, ci->values[0].value.string,
254 sizeof (th->plugin_instance));
255 th->plugin_instance[sizeof (th->plugin_instance) - 1] = '\0';
257 return (0);
258 } /* int ut_config_plugin_instance */
260 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
261 {
262 int i;
263 threshold_t th;
264 int status = 0;
266 if ((ci->values_num != 1)
267 || (ci->values[0].type != OCONFIG_TYPE_STRING))
268 {
269 WARNING ("threshold values: The `Plugin' block needs exactly one string "
270 "argument.");
271 return (-1);
272 }
274 if (ci->children_num < 1)
275 {
276 WARNING ("threshold values: The `Plugin' block needs at least one nested "
277 "block.");
278 return (-1);
279 }
281 memcpy (&th, th_orig, sizeof (th));
282 strncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
283 th.plugin[sizeof (th.plugin) - 1] = '\0';
285 for (i = 0; i < ci->children_num; i++)
286 {
287 oconfig_item_t *option = ci->children + i;
288 status = 0;
290 if (strcasecmp ("Type", option->key) == 0)
291 status = ut_config_type (&th, option);
292 else if (strcasecmp ("Instance", option->key) == 0)
293 status = ut_config_plugin_instance (&th, option);
294 else
295 {
296 WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
297 "block.", option->key);
298 status = -1;
299 }
301 if (status != 0)
302 break;
303 }
305 return (status);
306 } /* int ut_config_plugin */
308 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
309 {
310 int i;
311 threshold_t th;
312 int status = 0;
314 if ((ci->values_num != 1)
315 || (ci->values[0].type != OCONFIG_TYPE_STRING))
316 {
317 WARNING ("threshold values: The `Host' block needs exactly one string "
318 "argument.");
319 return (-1);
320 }
322 if (ci->children_num < 1)
323 {
324 WARNING ("threshold values: The `Host' block needs at least one nested "
325 "block.");
326 return (-1);
327 }
329 memcpy (&th, th_orig, sizeof (th));
330 strncpy (th.host, ci->values[0].value.string, sizeof (th.host));
331 th.host[sizeof (th.host) - 1] = '\0';
333 for (i = 0; i < ci->children_num; i++)
334 {
335 oconfig_item_t *option = ci->children + i;
336 status = 0;
338 if (strcasecmp ("Type", option->key) == 0)
339 status = ut_config_type (&th, option);
340 else if (strcasecmp ("Plugin", option->key) == 0)
341 status = ut_config_plugin (&th, option);
342 else
343 {
344 WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
345 "block.", option->key);
346 status = -1;
347 }
349 if (status != 0)
350 break;
351 }
353 return (status);
354 } /* int ut_config_host */
356 int ut_config (const oconfig_item_t *ci)
357 {
358 int i;
359 int status = 0;
361 threshold_t th;
363 if (threshold_tree == NULL)
364 {
365 threshold_tree = avl_create ((void *) strcmp);
366 if (threshold_tree == NULL)
367 {
368 ERROR ("ut_config: avl_create failed.");
369 return (-1);
370 }
371 }
373 memset (&th, '\0', sizeof (th));
374 th.min = NAN;
375 th.max = NAN;
377 for (i = 0; i < ci->children_num; i++)
378 {
379 oconfig_item_t *option = ci->children + i;
380 status = 0;
382 if (strcasecmp ("Type", option->key) == 0)
383 status = ut_config_type (&th, option);
384 else if (strcasecmp ("Plugin", option->key) == 0)
385 status = ut_config_plugin (&th, option);
386 else if (strcasecmp ("Host", option->key) == 0)
387 status = ut_config_host (&th, option);
388 else
389 {
390 WARNING ("threshold values: Option `%s' not allowed here.", option->key);
391 status = -1;
392 }
394 if (status != 0)
395 break;
396 }
398 return (status);
399 } /* int um_config */
400 /*
401 * End of the functions used to configure threshold values.
402 */
403 /* }}} */
405 static threshold_t *threshold_get (const char *hostname,
406 const char *plugin, const char *plugin_instance,
407 const char *type, const char *type_instance)
408 {
409 char name[6 * DATA_MAX_NAME_LEN];
410 threshold_t *th = NULL;
412 format_name (name, sizeof (name),
413 (hostname == NULL) ? "" : hostname,
414 (plugin == NULL) ? "" : plugin, plugin_instance,
415 (type == NULL) ? "" : type, type_instance);
416 name[sizeof (name) - 1] = '\0';
418 if (avl_get (threshold_tree, name, (void *) &th) == 0)
419 return (th);
420 else
421 return (NULL);
422 } /* threshold_t *threshold_get */
424 static threshold_t *threshold_search (const data_set_t *ds,
425 const value_list_t *vl)
426 {
427 threshold_t *th;
429 if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
430 ds->type, vl->type_instance)) != NULL)
431 return (th);
432 else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
433 ds->type, NULL)) != NULL)
434 return (th);
435 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
436 ds->type, vl->type_instance)) != NULL)
437 return (th);
438 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
439 ds->type, NULL)) != NULL)
440 return (th);
441 else if ((th = threshold_get (vl->host, "", NULL,
442 ds->type, vl->type_instance)) != NULL)
443 return (th);
444 else if ((th = threshold_get (vl->host, "", NULL,
445 ds->type, NULL)) != NULL)
446 return (th);
447 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
448 ds->type, vl->type_instance)) != NULL)
449 return (th);
450 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
451 ds->type, NULL)) != NULL)
452 return (th);
453 else if ((th = threshold_get ("", vl->plugin, NULL,
454 ds->type, vl->type_instance)) != NULL)
455 return (th);
456 else if ((th = threshold_get ("", vl->plugin, NULL,
457 ds->type, NULL)) != NULL)
458 return (th);
459 else if ((th = threshold_get ("", "", NULL,
460 ds->type, vl->type_instance)) != NULL)
461 return (th);
462 else if ((th = threshold_get ("", "", NULL,
463 ds->type, NULL)) != NULL)
464 return (th);
466 return (NULL);
467 } /* threshold_t *threshold_search */
469 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
470 {
471 threshold_t *th;
472 gauge_t *values;
473 int i;
475 if (threshold_tree == NULL)
476 return (0);
477 pthread_mutex_lock (&threshold_lock);
478 th = threshold_search (ds, vl);
479 pthread_mutex_unlock (&threshold_lock);
480 if (th == NULL)
481 return (0);
483 DEBUG ("ut_check_threshold: Found matching threshold");
485 values = uc_get_rate (ds, vl);
486 if (values == NULL)
487 return (0);
489 for (i = 0; i < ds->ds_num; i++)
490 {
491 int out_of_range = 0;
492 int is_inverted = 0;
494 if ((th->flags & UT_FLAG_INVERT) != 0)
495 is_inverted = 1;
496 if ((!isnan (th->min) && (th->min > values[i]))
497 || (!isnan (th->max) && (th->max < values[i])))
498 out_of_range = 1;
500 /* If only one of these conditions is true, there is a problem */
501 if ((out_of_range + is_inverted) == 1)
502 {
503 notification_t n;
504 char *buf;
505 size_t bufsize;
506 int status;
508 WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
509 ds->ds[i].name, th->min, values[i], th->max,
510 is_inverted ? "true" : "false");
512 buf = n.message;
513 bufsize = sizeof (n.message);
515 status = snprintf (buf, bufsize, "Host %s, plugin %s",
516 vl->host, vl->plugin);
517 buf += status;
518 bufsize -= status;
520 if (vl->plugin_instance[0] != '\0')
521 {
522 status = snprintf (buf, bufsize, " (instance %s)",
523 vl->plugin_instance);
524 buf += status;
525 bufsize -= status;
526 }
528 status = snprintf (buf, bufsize, " type %s", ds->type);
529 buf += status;
530 bufsize -= status;
532 if (vl->type_instance[0] != '\0')
533 {
534 status = snprintf (buf, bufsize, " (instance %s)",
535 vl->type_instance);
536 buf += status;
537 bufsize -= status;
538 }
540 if (is_inverted)
541 {
542 if (!isnan (th->min) && !isnan (th->max))
543 {
544 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
545 "%lf. That is within the critical region of %lf and %lf.",
546 ds->ds[i].name, values[i],
547 th->min, th->min);
548 }
549 else
550 {
551 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
552 "%lf. That is %s the configured threshold of %lf.",
553 ds->ds[i].name, values[i],
554 isnan (th->min) ? "below" : "above",
555 isnan (th->min) ? th->max : th->min);
556 }
557 }
558 else /* (!is_inverted) */
559 {
560 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
561 "%lf. That is %s the configured threshold of %lf.",
562 ds->ds[i].name, values[i],
563 (values[i] < th->min) ? "below" : "above",
564 (values[i] < th->min) ? th->min : th->max);
565 }
566 buf += status;
567 bufsize -= status;
569 n.severity = NOTIF_FAILURE;
570 n.time = vl->time;
572 strncpy (n.host, vl->host, sizeof (n.host));
573 n.host[sizeof (n.host) - 1] = '\0';
575 plugin_dispatch_notification (&n);
576 }
577 } /* for (i = 0; i < ds->ds_num; i++) */
579 sfree (values);
581 return (0);
582 } /* int ut_check_threshold */
584 int ut_check_interesting (const char *name)
585 {
586 char *name_copy = NULL;
587 char *host = NULL;
588 char *plugin = NULL;
589 char *plugin_instance = NULL;
590 char *type = NULL;
591 char *type_instance = NULL;
592 int status;
593 data_set_t ds;
594 value_list_t vl;
595 threshold_t *th;
597 /* If there is no tree nothing is interesting. */
598 if (threshold_tree == NULL)
599 return (0);
601 name_copy = strdup (name);
602 if (name_copy == NULL)
603 {
604 ERROR ("ut_check_interesting: strdup failed.");
605 return (-1);
606 }
608 status = parse_identifier (name_copy, &host,
609 &plugin, &plugin_instance, &type, &type_instance);
610 if (status != 0)
611 {
612 ERROR ("ut_check_interesting: parse_identifier failed.");
613 return (-1);
614 }
616 memset (&ds, '\0', sizeof (ds));
617 memset (&vl, '\0', sizeof (vl));
619 strncpy (vl.host, host, sizeof (vl.host));
620 vl.host[sizeof (vl.host) - 1] = '\0';
621 strncpy (vl.plugin, plugin, sizeof (vl.plugin));
622 vl.plugin[sizeof (vl.plugin) - 1] = '\0';
623 if (plugin_instance != NULL)
624 {
625 strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
626 vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
627 }
628 strncpy (ds.type, type, sizeof (ds.type));
629 ds.type[sizeof (ds.type) - 1] = '\0';
630 if (type_instance != NULL)
631 {
632 strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
633 vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
634 }
636 sfree (name_copy);
637 host = plugin = plugin_instance = type = type_instance = NULL;
639 th = threshold_search (&ds, &vl);
640 if (th != NULL)
641 return (1);
642 else
643 return (0);
644 } /* int ut_check_interesting */
646 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */