d7792f7c6176f3b415be52ef718173031102b85a
[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 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 = 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: 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 (const threshold_t *th_orig, oconfig_item_t *ci)
185 {
186   int i;
187   threshold_t th;
188   int status = 0;
189
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   }
197
198   if (ci->children_num < 1)
199   {
200     WARNING ("threshold values: The `Type' block needs at least one option.");
201     return (-1);
202   }
203
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';
207
208   th.min = NAN;
209   th.max = NAN;
210
211   for (i = 0; i < ci->children_num; i++)
212   {
213     oconfig_item_t *option = ci->children + i;
214     status = 0;
215
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     }
230
231     if (status != 0)
232       break;
233   }
234
235   if (status == 0)
236   {
237     status = ut_threshold_add (&th);
238   }
239
240   return (status);
241 } /* int ut_config_type */
242
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   }
252
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';
256
257   return (0);
258 } /* int ut_config_plugin_instance */
259
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;
265
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   }
273
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   }
280
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';
284
285   for (i = 0; i < ci->children_num; i++)
286   {
287     oconfig_item_t *option = ci->children + i;
288     status = 0;
289
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     }
300
301     if (status != 0)
302       break;
303   }
304
305   return (status);
306 } /* int ut_config_plugin */
307
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;
313
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   }
321
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   }
328
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';
332
333   for (i = 0; i < ci->children_num; i++)
334   {
335     oconfig_item_t *option = ci->children + i;
336     status = 0;
337
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     }
348
349     if (status != 0)
350       break;
351   }
352
353   return (status);
354 } /* int ut_config_host */
355
356 int ut_config (const oconfig_item_t *ci)
357 {
358   int i;
359   int status = 0;
360
361   threshold_t th;
362
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   }
372
373   memset (&th, '\0', sizeof (th));
374   th.min = NAN;
375   th.max = NAN;
376     
377   for (i = 0; i < ci->children_num; i++)
378   {
379     oconfig_item_t *option = ci->children + i;
380     status = 0;
381
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     }
393
394     if (status != 0)
395       break;
396   }
397
398   return (status);
399 } /* int um_config */
400 /*
401  * End of the functions used to configure threshold values.
402  */
403 /* }}} */
404
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;
411
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';
417
418   if (avl_get (threshold_tree, name, (void *) &th) == 0)
419     return (th);
420   else
421     return (NULL);
422 } /* threshold_t *threshold_get */
423
424 static threshold_t *threshold_search (const data_set_t *ds,
425     const value_list_t *vl)
426 {
427   threshold_t *th;
428
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);
465
466   return (NULL);
467 } /* threshold_t *threshold_search */
468
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;
474
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);
482
483   DEBUG ("ut_check_threshold: Found matching threshold");
484
485   values = uc_get_rate (ds, vl);
486   if (values == NULL)
487     return (0);
488
489   for (i = 0; i < ds->ds_num; i++)
490   {
491     int out_of_range = 0;
492     int is_inverted = 0;
493
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;
499
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;
507
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");
511
512       buf = n.message;
513       bufsize = sizeof (n.message);
514
515       status = snprintf (buf, bufsize, "Host %s, plugin %s",
516           vl->host, vl->plugin);
517       buf += status;
518       bufsize -= status;
519
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       }
527
528       status = snprintf (buf, bufsize, " type %s", ds->type);
529       buf += status;
530       bufsize -= status;
531
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       }
539
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;
568
569       n.severity = NOTIF_FAILURE;
570       n.time = vl->time;
571
572       strncpy (n.host, vl->host, sizeof (n.host));
573       n.host[sizeof (n.host) - 1] = '\0';
574
575       plugin_dispatch_notification (&n);
576     }
577   } /* for (i = 0; i < ds->ds_num; i++) */
578
579   sfree (values);
580
581   return (0);
582 } /* int ut_check_threshold */
583
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;
596
597   /* If there is no tree nothing is interesting. */
598   if (threshold_tree == NULL)
599     return (0);
600
601   name_copy = strdup (name);
602   if (name_copy == NULL)
603   {
604     ERROR ("ut_check_interesting: strdup failed.");
605     return (-1);
606   }
607
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   }
615
616   memset (&ds, '\0', sizeof (ds));
617   memset (&vl, '\0', sizeof (vl));
618
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   }
635
636   sfree (name_copy);
637   host = plugin = plugin_instance = type = type_instance = NULL;
638
639   th = threshold_search (&ds, &vl);
640   if (th != NULL)
641     return (1);
642   else
643     return (0);
644 } /* int ut_check_interesting */
645
646 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */