Merge branch 'collectd-4.2'
[collectd.git] / src / utils_threshold.c
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  **/
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
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 /* }}} */
49
50 /*
51  * Private (static) variables
52  * {{{ */
53 static c_avl_tree_t   *threshold_tree = NULL;
54 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
55 /* }}} */
56
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;
69
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   }
77
78   name_copy = strdup (name);
79   if (name_copy == NULL)
80   {
81     ERROR ("ut_threshold_add: strdup failed.");
82     return (-1);
83   }
84
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));
93
94   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
95
96   pthread_mutex_lock (&threshold_lock);
97   status = c_avl_insert (threshold_tree, name_copy, th_copy);
98   pthread_mutex_unlock (&threshold_lock);
99
100   if (status != 0)
101   {
102     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
103     sfree (name_copy);
104     sfree (th_copy);
105   }
106
107   return (status);
108 } /* int ut_threshold_add */
109 /*
110  * End of the threshold management functions
111  * }}} */
112
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   }
128
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';
132
133   return (0);
134 } /* int ut_config_type_instance */
135
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   }
145
146   th->max = ci->values[0].value.number;
147
148   return (0);
149 } /* int ut_config_type_max */
150
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   }
160
161   th->min = ci->values[0].value.number;
162
163   return (0);
164 } /* int ut_config_type_min */
165
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   }
175
176   if (ci->values[0].value.boolean)
177     th->flags |= UT_FLAG_INVERT;
178   else
179     th->flags &= ~UT_FLAG_INVERT;
180
181   return (0);
182 } /* int ut_config_type_invert */
183
184 static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
185 {
186   if ((ci->values_num != 1)
187       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
188   {
189     WARNING ("threshold values: The `Persist' option needs exactly one "
190         "boolean argument.");
191     return (-1);
192   }
193
194   if (ci->values[0].value.boolean)
195     th->flags |= UT_FLAG_PERSIST;
196   else
197     th->flags &= ~UT_FLAG_PERSIST;
198
199   return (0);
200 } /* int ut_config_type_persist */
201
202 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
203 {
204   int i;
205   threshold_t th;
206   int status = 0;
207
208   if ((ci->values_num != 1)
209       || (ci->values[0].type != OCONFIG_TYPE_STRING))
210   {
211     WARNING ("threshold values: The `Type' block needs exactly one string "
212         "argument.");
213     return (-1);
214   }
215
216   if (ci->children_num < 1)
217   {
218     WARNING ("threshold values: The `Type' block needs at least one option.");
219     return (-1);
220   }
221
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';
225
226   th.min = NAN;
227   th.max = NAN;
228
229   for (i = 0; i < ci->children_num; i++)
230   {
231     oconfig_item_t *option = ci->children + i;
232     status = 0;
233
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);
244     else
245     {
246       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
247           "block.", option->key);
248       status = -1;
249     }
250
251     if (status != 0)
252       break;
253   }
254
255   if (status == 0)
256   {
257     status = ut_threshold_add (&th);
258   }
259
260   return (status);
261 } /* int ut_config_type */
262
263 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
264 {
265   if ((ci->values_num != 1)
266       || (ci->values[0].type != OCONFIG_TYPE_STRING))
267   {
268     WARNING ("threshold values: The `Instance' option needs exactly one "
269         "string argument.");
270     return (-1);
271   }
272
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';
276
277   return (0);
278 } /* int ut_config_plugin_instance */
279
280 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
281 {
282   int i;
283   threshold_t th;
284   int status = 0;
285
286   if ((ci->values_num != 1)
287       || (ci->values[0].type != OCONFIG_TYPE_STRING))
288   {
289     WARNING ("threshold values: The `Plugin' block needs exactly one string "
290         "argument.");
291     return (-1);
292   }
293
294   if (ci->children_num < 1)
295   {
296     WARNING ("threshold values: The `Plugin' block needs at least one nested "
297         "block.");
298     return (-1);
299   }
300
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';
304
305   for (i = 0; i < ci->children_num; i++)
306   {
307     oconfig_item_t *option = ci->children + i;
308     status = 0;
309
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);
314     else
315     {
316       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
317           "block.", option->key);
318       status = -1;
319     }
320
321     if (status != 0)
322       break;
323   }
324
325   return (status);
326 } /* int ut_config_plugin */
327
328 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
329 {
330   int i;
331   threshold_t th;
332   int status = 0;
333
334   if ((ci->values_num != 1)
335       || (ci->values[0].type != OCONFIG_TYPE_STRING))
336   {
337     WARNING ("threshold values: The `Host' block needs exactly one string "
338         "argument.");
339     return (-1);
340   }
341
342   if (ci->children_num < 1)
343   {
344     WARNING ("threshold values: The `Host' block needs at least one nested "
345         "block.");
346     return (-1);
347   }
348
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';
352
353   for (i = 0; i < ci->children_num; i++)
354   {
355     oconfig_item_t *option = ci->children + i;
356     status = 0;
357
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);
362     else
363     {
364       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
365           "block.", option->key);
366       status = -1;
367     }
368
369     if (status != 0)
370       break;
371   }
372
373   return (status);
374 } /* int ut_config_host */
375
376 int ut_config (const oconfig_item_t *ci)
377 {
378   int i;
379   int status = 0;
380
381   threshold_t th;
382
383   if (threshold_tree == NULL)
384   {
385     threshold_tree = c_avl_create ((void *) strcmp);
386     if (threshold_tree == NULL)
387     {
388       ERROR ("ut_config: c_avl_create failed.");
389       return (-1);
390     }
391   }
392
393   memset (&th, '\0', sizeof (th));
394   th.min = NAN;
395   th.max = NAN;
396     
397   for (i = 0; i < ci->children_num; i++)
398   {
399     oconfig_item_t *option = ci->children + i;
400     status = 0;
401
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);
408     else
409     {
410       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
411       status = -1;
412     }
413
414     if (status != 0)
415       break;
416   }
417
418   return (status);
419 } /* int um_config */
420 /*
421  * End of the functions used to configure threshold values.
422  */
423 /* }}} */
424
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)
428 {
429   char name[6 * DATA_MAX_NAME_LEN];
430   threshold_t *th = NULL;
431
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';
437
438   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
439     return (th);
440   else
441     return (NULL);
442 } /* threshold_t *threshold_get */
443
444 static threshold_t *threshold_search (const data_set_t *ds,
445     const value_list_t *vl)
446 {
447   threshold_t *th;
448
449   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
450           ds->type, vl->type_instance)) != NULL)
451     return (th);
452   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
453           ds->type, NULL)) != NULL)
454     return (th);
455   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
456           ds->type, vl->type_instance)) != NULL)
457     return (th);
458   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
459           ds->type, NULL)) != NULL)
460     return (th);
461   else if ((th = threshold_get (vl->host, "", NULL,
462           ds->type, vl->type_instance)) != NULL)
463     return (th);
464   else if ((th = threshold_get (vl->host, "", NULL,
465           ds->type, NULL)) != NULL)
466     return (th);
467   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
468           ds->type, vl->type_instance)) != NULL)
469     return (th);
470   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
471           ds->type, NULL)) != NULL)
472     return (th);
473   else if ((th = threshold_get ("", vl->plugin, NULL,
474           ds->type, vl->type_instance)) != NULL)
475     return (th);
476   else if ((th = threshold_get ("", vl->plugin, NULL,
477           ds->type, NULL)) != NULL)
478     return (th);
479   else if ((th = threshold_get ("", "", NULL,
480           ds->type, vl->type_instance)) != NULL)
481     return (th);
482   else if ((th = threshold_get ("", "", NULL,
483           ds->type, NULL)) != NULL)
484     return (th);
485
486   return (NULL);
487 } /* threshold_t *threshold_search */
488
489 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
490 {
491   threshold_t *th;
492   gauge_t *values;
493   int i;
494
495   if (threshold_tree == NULL)
496     return (0);
497   pthread_mutex_lock (&threshold_lock);
498   th = threshold_search (ds, vl);
499   pthread_mutex_unlock (&threshold_lock);
500   if (th == NULL)
501     return (0);
502
503   DEBUG ("ut_check_threshold: Found matching threshold");
504
505   values = uc_get_rate (ds, vl);
506   if (values == NULL)
507     return (0);
508
509   for (i = 0; i < ds->ds_num; i++)
510   {
511     int out_of_range = 0;
512     int is_inverted = 0;
513
514     if ((th->flags & UT_FLAG_INVERT) != 0)
515       is_inverted = 1;
516     if ((!isnan (th->min) && (th->min > values[i]))
517         || (!isnan (th->max) && (th->max < values[i])))
518       out_of_range = 1;
519
520     /* If only one of these conditions is true, there is a problem */
521     if ((out_of_range + is_inverted) == 1)
522     {
523       notification_t n;
524       char *buf;
525       size_t bufsize;
526       int status;
527
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");
531
532       buf = n.message;
533       bufsize = sizeof (n.message);
534
535       status = snprintf (buf, bufsize, "Host %s, plugin %s",
536           vl->host, vl->plugin);
537       buf += status;
538       bufsize -= status;
539
540       if (vl->plugin_instance[0] != '\0')
541       {
542         status = snprintf (buf, bufsize, " (instance %s)",
543             vl->plugin_instance);
544         buf += status;
545         bufsize -= status;
546       }
547
548       status = snprintf (buf, bufsize, " type %s", ds->type);
549       buf += status;
550       bufsize -= status;
551
552       if (vl->type_instance[0] != '\0')
553       {
554         status = snprintf (buf, bufsize, " (instance %s)",
555             vl->type_instance);
556         buf += status;
557         bufsize -= status;
558       }
559
560       if (is_inverted)
561       {
562         if (!isnan (th->min) && !isnan (th->max))
563         {
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],
567               th->min, th->min);
568         }
569         else
570         {
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);
576         }
577       }
578       else /* (!is_inverted) */
579       {
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);
585       }
586       buf += status;
587       bufsize -= status;
588
589       n.severity = NOTIF_FAILURE;
590       n.time = vl->time;
591
592       strncpy (n.host, vl->host, sizeof (n.host));
593       n.host[sizeof (n.host) - 1] = '\0';
594
595       plugin_dispatch_notification (&n);
596     }
597   } /* for (i = 0; i < ds->ds_num; i++) */
598
599   sfree (values);
600
601   return (0);
602 } /* int ut_check_threshold */
603
604 int ut_check_interesting (const char *name)
605 {
606   char *name_copy = NULL;
607   char *host = NULL;
608   char *plugin = NULL;
609   char *plugin_instance = NULL;
610   char *type = NULL;
611   char *type_instance = NULL;
612   int status;
613   data_set_t ds;
614   value_list_t vl;
615   threshold_t *th;
616
617   /* If there is no tree nothing is interesting. */
618   if (threshold_tree == NULL)
619     return (0);
620
621   name_copy = strdup (name);
622   if (name_copy == NULL)
623   {
624     ERROR ("ut_check_interesting: strdup failed.");
625     return (-1);
626   }
627
628   status = parse_identifier (name_copy, &host,
629       &plugin, &plugin_instance, &type, &type_instance);
630   if (status != 0)
631   {
632     ERROR ("ut_check_interesting: parse_identifier failed.");
633     return (-1);
634   }
635
636   memset (&ds, '\0', sizeof (ds));
637   memset (&vl, '\0', sizeof (vl));
638
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)
644   {
645     strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
646     vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
647   }
648   strncpy (ds.type, type, sizeof (ds.type));
649   ds.type[sizeof (ds.type) - 1] = '\0';
650   if (type_instance != NULL)
651   {
652     strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
653     vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
654   }
655
656   sfree (name_copy);
657   host = plugin = plugin_instance = type = type_instance = NULL;
658
659   th = threshold_search (&ds, &vl);
660   if (th == NULL)
661     return (0);
662   if ((th->flags & UT_FLAG_PERSIST) == 0)
663     return (1);
664   return (2);
665 } /* int ut_check_interesting */
666
667 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */