Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / write_riemann_threshold.c
1 /**
2  * collectd - src/threshold.c
3  * Copyright (C) 2007-2010  Florian Forster
4  * Copyright (C) 2008-2009  Sebastian Harl
5  * Copyright (C) 2009       Andrés J. Díaz
6  * Copyright (C) 2014       Pierre-Yves Ritschard
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Author:
22  *   Pierre-Yves Ritschard <pyr at spootnik.org>
23  *   Florian octo Forster <octo at collectd.org>
24  *   Sebastian Harl <sh at tokkee.org>
25  *   Andrés J. Díaz <ajdiaz at connectical.com>
26  **/
27
28 #include <assert.h>
29 #include <ltdl.h>
30 #include <pthread.h>
31
32 #include "collectd.h"
33 #include "common.h"
34 #include "plugin.h"
35 #include "utils_avltree.h"
36 #include "utils_cache.h"
37 #include "utils_threshold.h"
38 #include "write_riemann_threshold.h"
39
40 /*
41  * Threshold management
42  * ====================
43  * The following functions add, delete, etc. configured thresholds to
44  * the underlying AVL trees.
45  */
46
47 /*
48  * int ut_check_one_data_source
49  *
50  * Checks one data source against the given threshold configuration. If the
51  * `DataSource' option is set in the threshold, and the name does NOT match,
52  * `okay' is returned. If the threshold does match, its failure and warning
53  * min and max values are checked and `failure' or `warning' is returned if
54  * appropriate.
55  * Does not fail.
56  */
57 static int ut_check_one_data_source (const data_set_t *ds,
58     const value_list_t __attribute__((unused)) *vl,
59     const threshold_t *th,
60     const gauge_t *values,
61     int ds_index)
62 { /* {{{ */
63   const char *ds_name;
64   int is_warning = 0;
65   int is_failure = 0;
66   int prev_state = STATE_OKAY;
67
68   /* check if this threshold applies to this data source */
69   if (ds != NULL)
70   {
71     ds_name = ds->ds[ds_index].name;
72     if ((th->data_source[0] != 0)
73         && (strcmp (ds_name, th->data_source) != 0))
74       return (STATE_OKAY);
75   }
76
77   if ((th->flags & UT_FLAG_INVERT) != 0)
78   {
79     is_warning--;
80     is_failure--;
81   }
82
83   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
84    * and probably, do not work as you expect. Enjoy! :D */
85   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
86   {
87     switch(prev_state)
88     {
89       case STATE_ERROR:
90         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
91              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
92           return (STATE_OKAY);
93         else
94           is_failure++;
95       case STATE_WARNING:
96         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
97              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
98           return (STATE_OKAY);
99         else
100           is_warning++;
101      }
102   }
103   else { /* no hysteresis */
104     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
105         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
106       is_failure++;
107
108     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
109         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
110       is_warning++;
111  }
112
113   if (is_failure != 0)
114     return (STATE_ERROR);
115
116   if (is_warning != 0)
117     return (STATE_WARNING);
118
119   return (STATE_OKAY);
120 } /* }}} int ut_check_one_data_source */
121
122 /*
123  * int ut_check_one_threshold
124  *
125  * Checks all data sources of a value list against the given threshold, using
126  * the ut_check_one_data_source function above. Returns the worst status,
127  * which is `okay' if nothing has failed.
128  * Returns less than zero if the data set doesn't have any data sources.
129  */
130 static int ut_check_one_threshold (const data_set_t *ds,
131     const value_list_t *vl,
132     const threshold_t *th,
133     const gauge_t *values,
134     int *statuses)
135 { /* {{{ */
136   int ret = -1;
137   int i;
138   int status;
139   gauge_t values_copy[ds->ds_num];
140
141   memcpy (values_copy, values, sizeof (values_copy));
142
143   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
144   {
145     int num = 0;
146     gauge_t sum=0.0;
147
148     if (ds->ds_num == 1)
149     {
150       WARNING ("ut_check_one_threshold: The %s type has only one data "
151           "source, but you have configured to check this as a percentage. "
152           "That doesn't make much sense, because the percentage will always "
153           "be 100%%!", ds->type);
154     }
155
156     /* Prepare `sum' and `num'. */
157     for (i = 0; i < ds->ds_num; i++)
158       if (!isnan (values[i]))
159       {
160         num++;
161         sum += values[i];
162       }
163
164     if ((num == 0) /* All data sources are undefined. */
165         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
166     {
167       for (i = 0; i < ds->ds_num; i++)
168         values_copy[i] = NAN;
169     }
170     else /* We can actually calculate the percentage. */
171     {
172       for (i = 0; i < ds->ds_num; i++)
173         values_copy[i] = 100.0 * values[i] / sum;
174     }
175   } /* if (UT_FLAG_PERCENTAGE) */
176
177   for (i = 0; i < ds->ds_num; i++)
178   {
179     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
180     if (status != -1) {
181             ret = 0;
182             if (statuses[i] < status)
183                     statuses[i] = status;
184     }
185   } /* for (ds->ds_num) */
186
187   return (ret);
188 } /* }}} int ut_check_one_threshold */
189
190 /*
191  * int ut_check_threshold
192  *
193  * Gets a list of matching thresholds and searches for the worst status by one
194  * of the thresholds. Then reports that status using the ut_report_state
195  * function above.
196  * Returns zero on success and if no threshold has been configured. Returns
197  * less than zero on failure.
198  */
199 int write_riemann_threshold_check (const data_set_t *ds, const value_list_t *vl,
200                                    int *statuses)
201 { /* {{{ */
202   threshold_t *th;
203   gauge_t *values;
204   int status;
205
206   memset(statuses, 0, vl->values_len * sizeof(*statuses));
207   if (threshold_tree == NULL)
208           return 0;
209
210   /* Is this lock really necessary? So far, thresholds are only inserted at
211    * startup. -octo */
212   pthread_mutex_lock (&threshold_lock);
213   th = threshold_search (vl);
214   pthread_mutex_unlock (&threshold_lock);
215   if (th == NULL)
216           return (0);
217
218   DEBUG ("ut_check_threshold: Found matching threshold(s)");
219
220   values = uc_get_rate (ds, vl);
221   if (values == NULL)
222           return (0);
223
224   while (th != NULL)
225   {
226     status = ut_check_one_threshold (ds, vl, th, values, statuses);
227     if (status < 0)
228     {
229       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
230       sfree (values);
231       return (-1);
232     }
233
234     th = th->next;
235   } /* while (th) */
236
237   sfree (values);
238
239   return (0);
240 } /* }}} int ut_check_threshold */
241
242
243 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */