Merge branch 'collectd-5.7' into collectd-5.8
[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 "collectd.h"
29
30 #include "common.h"
31 #include "plugin.h"
32 #include "utils_avltree.h"
33 #include "utils_cache.h"
34 #include "utils_threshold.h"
35 #include "write_riemann_threshold.h"
36
37 /*
38  * Threshold management
39  * ====================
40  * The following functions add, delete, etc. configured thresholds to
41  * the underlying AVL trees.
42  */
43
44 /*
45  * int ut_check_one_data_source
46  *
47  * Checks one data source against the given threshold configuration. If the
48  * `DataSource' option is set in the threshold, and the name does NOT match,
49  * `okay' is returned. If the threshold does match, its failure and warning
50  * min and max values are checked and `failure' or `warning' is returned if
51  * appropriate.
52  * Does not fail.
53  */
54 static int ut_check_one_data_source(
55     const data_set_t *ds, const value_list_t __attribute__((unused)) * vl,
56     const threshold_t *th, const gauge_t *values, int ds_index) { /* {{{ */
57   const char *ds_name;
58   int is_warning = 0;
59   int is_failure = 0;
60   int prev_state = STATE_OKAY;
61
62   /* check if this threshold applies to this data source */
63   if (ds != NULL) {
64     ds_name = ds->ds[ds_index].name;
65     if ((th->data_source[0] != 0) && (strcmp(ds_name, th->data_source) != 0))
66       return STATE_OKAY;
67   }
68
69   if ((th->flags & UT_FLAG_INVERT) != 0) {
70     is_warning--;
71     is_failure--;
72   }
73
74   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
75    * and probably, do not work as you expect. Enjoy! :D */
76   if ((th->hysteresis > 0) &&
77       ((prev_state = uc_get_state(ds, vl)) != STATE_OKAY)) {
78     switch (prev_state) {
79     case STATE_ERROR:
80       if ((!isnan(th->failure_min) &&
81            ((th->failure_min + th->hysteresis) < values[ds_index])) ||
82           (!isnan(th->failure_max) &&
83            ((th->failure_max - th->hysteresis) > values[ds_index])))
84         return STATE_OKAY;
85       else
86         is_failure++;
87     case STATE_WARNING:
88       if ((!isnan(th->warning_min) &&
89            ((th->warning_min + th->hysteresis) < values[ds_index])) ||
90           (!isnan(th->warning_max) &&
91            ((th->warning_max - th->hysteresis) > values[ds_index])))
92         return STATE_OKAY;
93       else
94         is_warning++;
95     }
96   } else { /* no hysteresis */
97     if ((!isnan(th->failure_min) && (th->failure_min > values[ds_index])) ||
98         (!isnan(th->failure_max) && (th->failure_max < values[ds_index])))
99       is_failure++;
100
101     if ((!isnan(th->warning_min) && (th->warning_min > values[ds_index])) ||
102         (!isnan(th->warning_max) && (th->warning_max < values[ds_index])))
103       is_warning++;
104   }
105
106   if (is_failure != 0)
107     return STATE_ERROR;
108
109   if (is_warning != 0)
110     return STATE_WARNING;
111
112   return STATE_OKAY;
113 } /* }}} int ut_check_one_data_source */
114
115 /*
116  * int ut_check_one_threshold
117  *
118  * Checks all data sources of a value list against the given threshold, using
119  * the ut_check_one_data_source function above. Returns the worst status,
120  * which is `okay' if nothing has failed.
121  * Returns less than zero if the data set doesn't have any data sources.
122  */
123 static int ut_check_one_threshold(const data_set_t *ds, const value_list_t *vl,
124                                   const threshold_t *th, const gauge_t *values,
125                                   int *statuses) { /* {{{ */
126   int ret = -1;
127   int status;
128   gauge_t values_copy[ds->ds_num];
129
130   memcpy(values_copy, values, sizeof(values_copy));
131
132   if ((th->flags & UT_FLAG_PERCENTAGE) != 0) {
133     int num = 0;
134     gauge_t sum = 0.0;
135
136     if (ds->ds_num == 1) {
137       WARNING(
138           "ut_check_one_threshold: The %s type has only one data "
139           "source, but you have configured to check this as a percentage. "
140           "That doesn't make much sense, because the percentage will always "
141           "be 100%%!",
142           ds->type);
143     }
144
145     /* Prepare `sum' and `num'. */
146     for (size_t i = 0; i < ds->ds_num; i++)
147       if (!isnan(values[i])) {
148         num++;
149         sum += values[i];
150       }
151
152     if ((num == 0)       /* All data sources are undefined. */
153         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
154     {
155       for (size_t i = 0; i < ds->ds_num; i++)
156         values_copy[i] = NAN;
157     } else /* We can actually calculate the percentage. */
158     {
159       for (size_t i = 0; i < ds->ds_num; i++)
160         values_copy[i] = 100.0 * values[i] / sum;
161     }
162   } /* if (UT_FLAG_PERCENTAGE) */
163
164   for (size_t i = 0; i < ds->ds_num; i++) {
165     status = ut_check_one_data_source(ds, vl, th, values_copy, i);
166     if (status != -1) {
167       ret = 0;
168       if (statuses[i] < status)
169         statuses[i] = status;
170     }
171   } /* for (ds->ds_num) */
172
173   return ret;
174 } /* }}} int ut_check_one_threshold */
175
176 /*
177  * int ut_check_threshold
178  *
179  * Gets a list of matching thresholds and searches for the worst status by one
180  * of the thresholds. Then reports that status using the ut_report_state
181  * function above.
182  * Returns zero on success and if no threshold has been configured. Returns
183  * less than zero on failure.
184  */
185 int write_riemann_threshold_check(const data_set_t *ds, const value_list_t *vl,
186                                   int *statuses) { /* {{{ */
187   threshold_t *th;
188   gauge_t *values;
189   int status;
190
191   assert(vl->values_len > 0);
192   memset(statuses, 0, vl->values_len * sizeof(*statuses));
193
194   if (threshold_tree == NULL)
195     return 0;
196
197   /* Is this lock really necessary? So far, thresholds are only inserted at
198    * startup. -octo */
199   pthread_mutex_lock(&threshold_lock);
200   th = threshold_search(vl);
201   pthread_mutex_unlock(&threshold_lock);
202   if (th == NULL)
203     return 0;
204
205   DEBUG("ut_check_threshold: Found matching threshold(s)");
206
207   values = uc_get_rate(ds, vl);
208   if (values == NULL)
209     return 0;
210
211   while (th != NULL) {
212     status = ut_check_one_threshold(ds, vl, th, values, statuses);
213     if (status < 0) {
214       ERROR("ut_check_threshold: ut_check_one_threshold failed.");
215       sfree(values);
216       return -1;
217     }
218
219     th = th->next;
220   } /* while (th) */
221
222   sfree(values);
223
224   return 0;
225 } /* }}} int ut_check_threshold */