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 typedef struct threshold_s
35 {
36 char host[DATA_MAX_NAME_LEN];
37 char plugin[DATA_MAX_NAME_LEN];
38 char plugin_instance[DATA_MAX_NAME_LEN];
39 char type[DATA_MAX_NAME_LEN];
40 char type_instance[DATA_MAX_NAME_LEN];
41 gauge_t min;
42 gauge_t max;
43 int invert;
44 } threshold_t;
45 /* }}} */
47 /*
48 * Private (static) variables
49 * {{{ */
50 static avl_tree_t *threshold_tree = NULL;
51 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
52 /* }}} */
54 /*
55 * Threshold management
56 * ====================
57 * The following functions add, delete, search, etc. configured thresholds to
58 * the underlying AVL trees.
59 * {{{ */
60 static int ut_threshold_add (const threshold_t *th)
61 {
62 char name[6 * DATA_MAX_NAME_LEN];
63 char *name_copy;
64 threshold_t *th_copy;
65 int status = 0;
67 if (format_name (name, sizeof (name), th->host,
68 th->plugin, th->plugin_instance,
69 th->type, th->type_instance) != 0)
70 {
71 ERROR ("ut_threshold_add: format_name failed.");
72 return (-1);
73 }
75 name_copy = strdup (name);
76 if (name_copy == NULL)
77 {
78 ERROR ("ut_threshold_add: strdup failed.");
79 return (-1);
80 }
82 th_copy = (threshold_t *) malloc (sizeof (threshold_t));
83 if (th_copy == NULL)
84 {
85 sfree (name_copy);
86 ERROR ("ut_threshold_add: malloc failed.");
87 return (-1);
88 }
89 memcpy (th_copy, th, sizeof (threshold_t));
91 DEBUG ("ut_threshold_add: Adding entry `%s'", name);
93 pthread_mutex_lock (&threshold_lock);
94 status = avl_insert (threshold_tree, name_copy, th_copy);
95 pthread_mutex_unlock (&threshold_lock);
97 if (status != 0)
98 {
99 ERROR ("ut_threshold_add: avl_insert (%s) failed.", name);
100 sfree (name_copy);
101 sfree (th_copy);
102 }
104 return (status);
105 } /* int ut_threshold_add */
106 /*
107 * End of the threshold management functions
108 * }}} */
110 /*
111 * Configuration
112 * =============
113 * The following approximately two hundred functions are used to handle the
114 * configuration and fill the threshold list.
115 * {{{ */
116 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
117 {
118 if ((ci->values_num != 1)
119 || (ci->values[0].type != OCONFIG_TYPE_STRING))
120 {
121 WARNING ("threshold values: The `Instance' option needs exactly one "
122 "string argument.");
123 return (-1);
124 }
126 strncpy (th->type_instance, ci->values[0].value.string,
127 sizeof (th->type_instance));
128 th->type_instance[sizeof (th->type_instance) - 1] = '\0';
130 return (0);
131 } /* int ut_config_type_instance */
133 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
134 {
135 if ((ci->values_num != 1)
136 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
137 {
138 WARNING ("threshold values: The `Max' option needs exactly one "
139 "number argument.");
140 return (-1);
141 }
143 th->max = ci->values[0].value.number;
145 return (0);
146 } /* int ut_config_type_max */
148 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
149 {
150 if ((ci->values_num != 1)
151 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
152 {
153 WARNING ("threshold values: The `Min' option needs exactly one "
154 "number argument.");
155 return (-1);
156 }
158 th->min = ci->values[0].value.number;
160 return (0);
161 } /* int ut_config_type_min */
163 static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
164 {
165 if ((ci->values_num != 1)
166 || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
167 {
168 WARNING ("threshold values: The `Invert' option needs exactly one "
169 "boolean argument.");
170 return (-1);
171 }
173 th->invert = (ci->values[0].value.boolean) ? 1 : 0;
175 return (0);
176 } /* int ut_config_type_invert */
178 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
179 {
180 int i;
181 threshold_t th;
182 int status = 0;
184 if ((ci->values_num != 1)
185 || (ci->values[0].type != OCONFIG_TYPE_STRING))
186 {
187 WARNING ("threshold values: The `Type' block needs exactly one string "
188 "argument.");
189 return (-1);
190 }
192 if (ci->children_num < 1)
193 {
194 WARNING ("threshold values: The `Type' block needs at least one option.");
195 return (-1);
196 }
198 memcpy (&th, th_orig, sizeof (th));
199 strncpy (th.type, ci->values[0].value.string, sizeof (th.type));
200 th.type[sizeof (th.type) - 1] = '\0';
202 for (i = 0; i < ci->children_num; i++)
203 {
204 oconfig_item_t *option = ci->children + i;
205 status = 0;
207 if (strcasecmp ("Instance", option->key) == 0)
208 status = ut_config_type_instance (&th, option);
209 else if (strcasecmp ("Max", option->key) == 0)
210 status = ut_config_type_max (&th, option);
211 else if (strcasecmp ("Min", option->key) == 0)
212 status = ut_config_type_min (&th, option);
213 else if (strcasecmp ("Invert", option->key) == 0)
214 status = ut_config_type_invert (&th, option);
215 else
216 {
217 WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
218 "block.", option->key);
219 status = -1;
220 }
222 if (status != 0)
223 break;
224 }
226 if (status == 0)
227 {
228 status = ut_threshold_add (&th);
229 }
231 return (status);
232 } /* int ut_config_type */
234 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
235 {
236 if ((ci->values_num != 1)
237 || (ci->values[0].type != OCONFIG_TYPE_STRING))
238 {
239 WARNING ("threshold values: The `Instance' option needs exactly one "
240 "string argument.");
241 return (-1);
242 }
244 strncpy (th->plugin_instance, ci->values[0].value.string,
245 sizeof (th->plugin_instance));
246 th->plugin_instance[sizeof (th->plugin_instance) - 1] = '\0';
248 return (0);
249 } /* int ut_config_plugin_instance */
251 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
252 {
253 int i;
254 threshold_t th;
255 int status = 0;
257 if ((ci->values_num != 1)
258 || (ci->values[0].type != OCONFIG_TYPE_STRING))
259 {
260 WARNING ("threshold values: The `Plugin' block needs exactly one string "
261 "argument.");
262 return (-1);
263 }
265 if (ci->children_num < 1)
266 {
267 WARNING ("threshold values: The `Plugin' block needs at least one nested "
268 "block.");
269 return (-1);
270 }
272 memcpy (&th, th_orig, sizeof (th));
273 strncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
274 th.plugin[sizeof (th.plugin) - 1] = '\0';
276 for (i = 0; i < ci->children_num; i++)
277 {
278 oconfig_item_t *option = ci->children + i;
279 status = 0;
281 if (strcasecmp ("Type", option->key) == 0)
282 status = ut_config_type (&th, option);
283 else if (strcasecmp ("Instance", option->key) == 0)
284 status = ut_config_plugin_instance (&th, option);
285 else
286 {
287 WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
288 "block.", option->key);
289 status = -1;
290 }
292 if (status != 0)
293 break;
294 }
296 return (status);
297 } /* int ut_config_plugin */
299 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
300 {
301 int i;
302 threshold_t th;
303 int status = 0;
305 if ((ci->values_num != 1)
306 || (ci->values[0].type != OCONFIG_TYPE_STRING))
307 {
308 WARNING ("threshold values: The `Host' block needs exactly one string "
309 "argument.");
310 return (-1);
311 }
313 if (ci->children_num < 1)
314 {
315 WARNING ("threshold values: The `Host' block needs at least one nested "
316 "block.");
317 return (-1);
318 }
320 memcpy (&th, th_orig, sizeof (th));
321 strncpy (th.host, ci->values[0].value.string, sizeof (th.host));
322 th.host[sizeof (th.host) - 1] = '\0';
324 for (i = 0; i < ci->children_num; i++)
325 {
326 oconfig_item_t *option = ci->children + i;
327 status = 0;
329 if (strcasecmp ("Type", option->key) == 0)
330 status = ut_config_type (&th, option);
331 else if (strcasecmp ("Plugin", option->key) == 0)
332 status = ut_config_plugin (&th, option);
333 else
334 {
335 WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
336 "block.", option->key);
337 status = -1;
338 }
340 if (status != 0)
341 break;
342 }
344 return (status);
345 } /* int ut_config_host */
347 int ut_config (const oconfig_item_t *ci)
348 {
349 int i;
350 int status = 0;
352 threshold_t th;
354 if (threshold_tree == NULL)
355 {
356 threshold_tree = avl_create ((void *) strcmp);
357 if (threshold_tree == NULL)
358 {
359 ERROR ("ut_config: avl_create failed.");
360 return (-1);
361 }
362 }
364 memset (&th, '\0', sizeof (th));
365 th.min = NAN;
366 th.max = NAN;
368 for (i = 0; i < ci->children_num; i++)
369 {
370 oconfig_item_t *option = ci->children + i;
371 status = 0;
373 if (strcasecmp ("Type", option->key) == 0)
374 status = ut_config_type (&th, option);
375 else if (strcasecmp ("Plugin", option->key) == 0)
376 status = ut_config_plugin (&th, option);
377 else if (strcasecmp ("Host", option->key) == 0)
378 status = ut_config_host (&th, option);
379 else
380 {
381 WARNING ("threshold values: Option `%s' not allowed here.", option->key);
382 status = -1;
383 }
385 if (status != 0)
386 break;
387 }
389 return (status);
390 } /* int um_config */
391 /*
392 * End of the functions used to configure threshold values.
393 */
394 /* }}} */
396 static threshold_t *threshold_get (const char *hostname,
397 const char *plugin, const char *plugin_instance,
398 const char *type, const char *type_instance)
399 {
400 char name[6 * DATA_MAX_NAME_LEN];
401 threshold_t *th = NULL;
403 format_name (name, sizeof (name),
404 (hostname == NULL) ? "" : hostname,
405 (plugin == NULL) ? "" : plugin, plugin_instance,
406 (type == NULL) ? "" : type, type_instance);
407 name[sizeof (name) - 1] = '\0';
409 if (avl_get (threshold_tree, name, (void *) &th) == 0)
410 return (th);
411 else
412 return (NULL);
413 } /* threshold_t *threshold_get */
415 static threshold_t *threshold_search (const data_set_t *ds,
416 const value_list_t *vl)
417 {
418 threshold_t *th;
420 if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
421 ds->type, vl->type_instance)) != NULL)
422 return (th);
423 else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
424 ds->type, NULL)) != NULL)
425 return (th);
426 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
427 ds->type, vl->type_instance)) != NULL)
428 return (th);
429 else if ((th = threshold_get (vl->host, vl->plugin, NULL,
430 ds->type, NULL)) != NULL)
431 return (th);
432 else if ((th = threshold_get (vl->host, "", NULL,
433 ds->type, vl->type_instance)) != NULL)
434 return (th);
435 else if ((th = threshold_get (vl->host, "", NULL,
436 ds->type, NULL)) != NULL)
437 return (th);
438 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
439 ds->type, vl->type_instance)) != NULL)
440 return (th);
441 else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
442 ds->type, NULL)) != NULL)
443 return (th);
444 else if ((th = threshold_get ("", vl->plugin, NULL,
445 ds->type, vl->type_instance)) != NULL)
446 return (th);
447 else if ((th = threshold_get ("", vl->plugin, NULL,
448 ds->type, NULL)) != NULL)
449 return (th);
450 else if ((th = threshold_get ("", "", NULL,
451 ds->type, vl->type_instance)) != NULL)
452 return (th);
453 else if ((th = threshold_get ("", "", NULL,
454 ds->type, NULL)) != NULL)
455 return (th);
457 return (NULL);
458 } /* threshold_t *threshold_search */
460 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
461 {
462 threshold_t *th;
463 gauge_t *values;
464 int i;
466 if (threshold_tree == NULL)
467 return (0);
468 pthread_mutex_lock (&threshold_lock);
469 th = threshold_search (ds, vl);
470 pthread_mutex_unlock (&threshold_lock);
471 if (th == NULL)
472 return (0);
474 DEBUG ("Found matching threshold");
476 values = uc_get_rate (ds, vl);
477 if (values == NULL)
478 return (0);
480 for (i = 0; i < ds->ds_num; i++)
481 {
482 if ((th->min > values[i]) || (th->max < values[i]))
483 {
484 notification_t n;
485 char *buf;
486 size_t bufsize;
487 int status;
489 WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf",
490 ds->ds[i].name, th->min, values[i], th->max);
492 buf = n.message;
493 bufsize = sizeof (n.message);
495 status = snprintf (buf, bufsize, "Host %s, plugin %s",
496 vl->host, vl->plugin);
497 buf += status;
498 bufsize -= status;
500 if (vl->plugin_instance[0] != '\0')
501 {
502 status = snprintf (buf, bufsize, " (instance %s)",
503 vl->plugin_instance);
504 buf += status;
505 bufsize -= status;
506 }
508 status = snprintf (buf, bufsize, " type %s", ds->type);
509 buf += status;
510 bufsize -= status;
512 if (vl->type_instance[0] != '\0')
513 {
514 status = snprintf (buf, bufsize, " (instance %s)",
515 vl->type_instance);
516 buf += status;
517 bufsize -= status;
518 }
520 status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
521 "%lf. That is %s the configured threshold of %lf.",
522 ds->ds[i].name, values[i],
523 (values[i] < th->min) ? "below" : "above",
524 (values[i] < th->min) ? th->min : th->max);
525 buf += status;
526 bufsize -= status;
528 n.severity = NOTIF_FAILURE;
529 n.time = vl->time;
531 strncpy (n.host, vl->host, sizeof (n.host));
532 n.host[sizeof (n.host) - 1] = '\0';
534 plugin_dispatch_notification (&n);
535 }
536 } /* for (i = 0; i < ds->ds_num; i++) */
538 sfree (values);
540 return (0);
541 } /* int ut_check_threshold */
543 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */