15ccf9c3709cede5e6da488fac10b11db7c539d2
[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 (ci->values_num != 0)
384   {
385     ERROR ("threshold values: The `Threshold' block may not have any "
386         "arguments.");
387     return (-1);
388   }
389
390   if (threshold_tree == NULL)
391   {
392     threshold_tree = c_avl_create ((void *) strcmp);
393     if (threshold_tree == NULL)
394     {
395       ERROR ("ut_config: c_avl_create failed.");
396       return (-1);
397     }
398   }
399
400   memset (&th, '\0', sizeof (th));
401   th.min = NAN;
402   th.max = NAN;
403     
404   for (i = 0; i < ci->children_num; i++)
405   {
406     oconfig_item_t *option = ci->children + i;
407     status = 0;
408
409     if (strcasecmp ("Type", option->key) == 0)
410       status = ut_config_type (&th, option);
411     else if (strcasecmp ("Plugin", option->key) == 0)
412       status = ut_config_plugin (&th, option);
413     else if (strcasecmp ("Host", option->key) == 0)
414       status = ut_config_host (&th, option);
415     else
416     {
417       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
418       status = -1;
419     }
420
421     if (status != 0)
422       break;
423   }
424
425   return (status);
426 } /* int um_config */
427 /*
428  * End of the functions used to configure threshold values.
429  */
430 /* }}} */
431
432 static threshold_t *threshold_get (const char *hostname,
433     const char *plugin, const char *plugin_instance,
434     const char *type, const char *type_instance)
435 {
436   char name[6 * DATA_MAX_NAME_LEN];
437   threshold_t *th = NULL;
438
439   format_name (name, sizeof (name),
440       (hostname == NULL) ? "" : hostname,
441       (plugin == NULL) ? "" : plugin, plugin_instance,
442       (type == NULL) ? "" : type, type_instance);
443   name[sizeof (name) - 1] = '\0';
444
445   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
446     return (th);
447   else
448     return (NULL);
449 } /* threshold_t *threshold_get */
450
451 static threshold_t *threshold_search (const data_set_t *ds,
452     const value_list_t *vl)
453 {
454   threshold_t *th;
455
456   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
457           ds->type, vl->type_instance)) != NULL)
458     return (th);
459   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
460           ds->type, NULL)) != NULL)
461     return (th);
462   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
463           ds->type, vl->type_instance)) != NULL)
464     return (th);
465   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
466           ds->type, NULL)) != NULL)
467     return (th);
468   else if ((th = threshold_get (vl->host, "", NULL,
469           ds->type, vl->type_instance)) != NULL)
470     return (th);
471   else if ((th = threshold_get (vl->host, "", NULL,
472           ds->type, NULL)) != NULL)
473     return (th);
474   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
475           ds->type, vl->type_instance)) != NULL)
476     return (th);
477   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
478           ds->type, NULL)) != NULL)
479     return (th);
480   else if ((th = threshold_get ("", vl->plugin, NULL,
481           ds->type, vl->type_instance)) != NULL)
482     return (th);
483   else if ((th = threshold_get ("", vl->plugin, NULL,
484           ds->type, NULL)) != NULL)
485     return (th);
486   else if ((th = threshold_get ("", "", NULL,
487           ds->type, vl->type_instance)) != NULL)
488     return (th);
489   else if ((th = threshold_get ("", "", NULL,
490           ds->type, NULL)) != NULL)
491     return (th);
492
493   return (NULL);
494 } /* threshold_t *threshold_search */
495
496 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
497 {
498   threshold_t *th;
499   gauge_t *values;
500   int i;
501
502   if (threshold_tree == NULL)
503     return (0);
504   pthread_mutex_lock (&threshold_lock);
505   th = threshold_search (ds, vl);
506   pthread_mutex_unlock (&threshold_lock);
507   if (th == NULL)
508     return (0);
509
510   DEBUG ("ut_check_threshold: Found matching threshold");
511
512   values = uc_get_rate (ds, vl);
513   if (values == NULL)
514     return (0);
515
516   for (i = 0; i < ds->ds_num; i++)
517   {
518     int out_of_range = 0;
519     int is_inverted = 0;
520
521     if ((th->flags & UT_FLAG_INVERT) != 0)
522       is_inverted = 1;
523     if ((!isnan (th->min) && (th->min > values[i]))
524         || (!isnan (th->max) && (th->max < values[i])))
525       out_of_range = 1;
526
527     /* If only one of these conditions is true, there is a problem */
528     if ((out_of_range + is_inverted) == 1)
529     {
530       notification_t n;
531       char *buf;
532       size_t bufsize;
533       int status;
534
535       WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf (invert: %s)",
536           ds->ds[i].name, th->min, values[i], th->max,
537           is_inverted ? "true" : "false");
538
539       /* Copy the associative members */
540       NOTIFICATION_INIT_VL (&n, vl, ds);
541
542       n.severity = NOTIF_FAILURE;
543       n.time = vl->time;
544
545       buf = n.message;
546       bufsize = sizeof (n.message);
547
548       status = snprintf (buf, bufsize, "Host %s, plugin %s",
549           vl->host, vl->plugin);
550       buf += status;
551       bufsize -= status;
552
553       if (vl->plugin_instance[0] != '\0')
554       {
555         status = snprintf (buf, bufsize, " (instance %s)",
556             vl->plugin_instance);
557         buf += status;
558         bufsize -= status;
559       }
560
561       status = snprintf (buf, bufsize, " type %s", ds->type);
562       buf += status;
563       bufsize -= status;
564
565       if (vl->type_instance[0] != '\0')
566       {
567         status = snprintf (buf, bufsize, " (instance %s)",
568             vl->type_instance);
569         buf += status;
570         bufsize -= status;
571       }
572
573       if (is_inverted)
574       {
575         if (!isnan (th->min) && !isnan (th->max))
576         {
577           status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
578               "%lf. That is within the critical region of %lf and %lf.",
579               ds->ds[i].name, values[i],
580               th->min, th->min);
581         }
582         else
583         {
584           status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
585               "%lf. That is %s the configured threshold of %lf.",
586               ds->ds[i].name, values[i],
587               isnan (th->min) ? "below" : "above",
588               isnan (th->min) ? th->max : th->min);
589         }
590       }
591       else /* (!is_inverted) */
592       {
593         status = snprintf (buf, bufsize, ": Data source \"%s\" is currently "
594             "%lf. That is %s the configured threshold of %lf.",
595             ds->ds[i].name, values[i],
596             (values[i] < th->min) ? "below" : "above",
597             (values[i] < th->min) ? th->min : th->max);
598       }
599       buf += status;
600       bufsize -= status;
601
602       plugin_dispatch_notification (&n);
603     }
604   } /* for (i = 0; i < ds->ds_num; i++) */
605
606   sfree (values);
607
608   return (0);
609 } /* int ut_check_threshold */
610
611 int ut_check_interesting (const char *name)
612 {
613   char *name_copy = NULL;
614   char *host = NULL;
615   char *plugin = NULL;
616   char *plugin_instance = NULL;
617   char *type = NULL;
618   char *type_instance = NULL;
619   int status;
620   data_set_t ds;
621   value_list_t vl;
622   threshold_t *th;
623
624   /* If there is no tree nothing is interesting. */
625   if (threshold_tree == NULL)
626     return (0);
627
628   name_copy = strdup (name);
629   if (name_copy == NULL)
630   {
631     ERROR ("ut_check_interesting: strdup failed.");
632     return (-1);
633   }
634
635   status = parse_identifier (name_copy, &host,
636       &plugin, &plugin_instance, &type, &type_instance);
637   if (status != 0)
638   {
639     ERROR ("ut_check_interesting: parse_identifier failed.");
640     return (-1);
641   }
642
643   memset (&ds, '\0', sizeof (ds));
644   memset (&vl, '\0', sizeof (vl));
645
646   strncpy (vl.host, host, sizeof (vl.host));
647   vl.host[sizeof (vl.host) - 1] = '\0';
648   strncpy (vl.plugin, plugin, sizeof (vl.plugin));
649   vl.plugin[sizeof (vl.plugin) - 1] = '\0';
650   if (plugin_instance != NULL)
651   {
652     strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
653     vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
654   }
655   strncpy (ds.type, type, sizeof (ds.type));
656   ds.type[sizeof (ds.type) - 1] = '\0';
657   if (type_instance != NULL)
658   {
659     strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
660     vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
661   }
662
663   sfree (name_copy);
664   host = plugin = plugin_instance = type = type_instance = NULL;
665
666   th = threshold_search (&ds, &vl);
667   if (th == NULL)
668     return (0);
669   if ((th->flags & UT_FLAG_PERSIST) == 0)
670     return (1);
671   return (2);
672 } /* int ut_check_interesting */
673
674 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */