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 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 /* }}} */
46
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 /* }}} */
53
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;
66
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   }
74
75   name_copy = strdup (name);
76   if (name_copy == NULL)
77   {
78     ERROR ("ut_threshold_add: strdup failed.");
79     return (-1);
80   }
81
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));
90
91   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
92
93   pthread_mutex_lock (&threshold_lock);
94   status = avl_insert (threshold_tree, name_copy, th_copy);
95   pthread_mutex_unlock (&threshold_lock);
96
97   if (status != 0)
98   {
99     ERROR ("ut_threshold_add: avl_insert (%s) failed.", name);
100     sfree (name_copy);
101     sfree (th_copy);
102   }
103
104   return (status);
105 } /* int ut_threshold_add */
106 /*
107  * End of the threshold management functions
108  * }}} */
109
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   }
125
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';
129
130   return (0);
131 } /* int ut_config_type_instance */
132
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   }
142
143   th->max = ci->values[0].value.number;
144
145   return (0);
146 } /* int ut_config_type_max */
147
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   }
157
158   th->min = ci->values[0].value.number;
159
160   return (0);
161 } /* int ut_config_type_min */
162
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   }
172
173   th->invert = (ci->values[0].value.boolean) ? 1 : 0;
174
175   return (0);
176 } /* int ut_config_type_invert */
177
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;
183
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   }
191
192   if (ci->children_num < 1)
193   {
194     WARNING ("threshold values: The `Type' block needs at least one option.");
195     return (-1);
196   }
197
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';
201
202   for (i = 0; i < ci->children_num; i++)
203   {
204     oconfig_item_t *option = ci->children + i;
205     status = 0;
206
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     }
221
222     if (status != 0)
223       break;
224   }
225
226   if (status == 0)
227   {
228     status = ut_threshold_add (&th);
229   }
230
231   return (status);
232 } /* int ut_config_type */
233
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   }
243
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';
247
248   return (0);
249 } /* int ut_config_plugin_instance */
250
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;
256
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   }
264
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   }
271
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';
275
276   for (i = 0; i < ci->children_num; i++)
277   {
278     oconfig_item_t *option = ci->children + i;
279     status = 0;
280
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     }
291
292     if (status != 0)
293       break;
294   }
295
296   return (status);
297 } /* int ut_config_plugin */
298
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;
304
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   }
312
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   }
319
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';
323
324   for (i = 0; i < ci->children_num; i++)
325   {
326     oconfig_item_t *option = ci->children + i;
327     status = 0;
328
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     }
339
340     if (status != 0)
341       break;
342   }
343
344   return (status);
345 } /* int ut_config_host */
346
347 int ut_config (const oconfig_item_t *ci)
348 {
349   int i;
350   int status = 0;
351
352   threshold_t th;
353
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   }
363
364   memset (&th, '\0', sizeof (th));
365   th.min = NAN;
366   th.max = NAN;
367     
368   for (i = 0; i < ci->children_num; i++)
369   {
370     oconfig_item_t *option = ci->children + i;
371     status = 0;
372
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     }
384
385     if (status != 0)
386       break;
387   }
388
389   return (status);
390 } /* int um_config */
391 /*
392  * End of the functions used to configure threshold values.
393  */
394 /* }}} */
395
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;
402
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';
408
409   if (avl_get (threshold_tree, name, (void *) &th) == 0)
410     return (th);
411   else
412     return (NULL);
413 } /* threshold_t *threshold_get */
414
415 static threshold_t *threshold_search (const data_set_t *ds,
416     const value_list_t *vl)
417 {
418   threshold_t *th;
419
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);
456
457   return (NULL);
458 } /* threshold_t *threshold_search */
459
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;
465
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);
473
474   DEBUG ("Found matching threshold");
475
476   values = uc_get_rate (ds, vl);
477   if (values == NULL)
478     return (0);
479
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;
488
489       WARNING ("ut_check_threshold: ds[%s]: %lf <= !%lf <= %lf",
490           ds->ds[i].name, th->min, values[i], th->max);
491
492       buf = n.message;
493       bufsize = sizeof (n.message);
494
495       status = snprintf (buf, bufsize, "Host %s, plugin %s",
496           vl->host, vl->plugin);
497       buf += status;
498       bufsize -= status;
499
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       }
507
508       status = snprintf (buf, bufsize, " type %s", ds->type);
509       buf += status;
510       bufsize -= status;
511
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       }
519
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;
527
528       n.severity = NOTIF_FAILURE;
529       n.time = vl->time;
530
531       strncpy (n.host, vl->host, sizeof (n.host));
532       n.host[sizeof (n.host) - 1] = '\0';
533
534       plugin_dispatch_notification (&n);
535     }
536   } /* for (i = 0; i < ds->ds_num; i++) */
537
538   sfree (values);
539
540   return (0);
541 } /* int ut_check_threshold */
542
543 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */