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 "plugin.h"
31 #include "utils/avltree/avltree.h"
32 #include "utils/common/common.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 */
ut_check_one_data_source(const data_set_t * ds,const value_list_t * vl,const threshold_t * th,const gauge_t * values,int ds_index)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_UNKNOWN;
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 prev_state = uc_get_state(ds, vl);
77 if ((th->hysteresis > 0) && (prev_state != STATE_OKAY) &&
78 (prev_state != STATE_UNKNOWN)) {
79 switch (prev_state) {
80 case STATE_ERROR:
81 if ((!isnan(th->failure_min) &&
82 ((th->failure_min + th->hysteresis) < values[ds_index])) ||
83 (!isnan(th->failure_max) &&
84 ((th->failure_max - th->hysteresis) > values[ds_index])))
85 return STATE_OKAY;
86 else
87 is_failure++;
88 case STATE_WARNING:
89 if ((!isnan(th->warning_min) &&
90 ((th->warning_min + th->hysteresis) < values[ds_index])) ||
91 (!isnan(th->warning_max) &&
92 ((th->warning_max - th->hysteresis) > values[ds_index])))
93 return STATE_OKAY;
94 else
95 is_warning++;
96 }
97 } else { /* no hysteresis */
98 if ((!isnan(th->failure_min) && (th->failure_min > values[ds_index])) ||
99 (!isnan(th->failure_max) && (th->failure_max < values[ds_index])))
100 is_failure++;
101
102 if ((!isnan(th->warning_min) && (th->warning_min > values[ds_index])) ||
103 (!isnan(th->warning_max) && (th->warning_max < values[ds_index])))
104 is_warning++;
105 }
106
107 if (is_failure != 0)
108 return STATE_ERROR;
109
110 if (is_warning != 0)
111 return STATE_WARNING;
112
113 return STATE_OKAY;
114 } /* }}} int ut_check_one_data_source */
115
116 /*
117 * int ut_check_one_threshold
118 *
119 * Checks all data sources of a value list against the given threshold, using
120 * the ut_check_one_data_source function above. Returns the worst status,
121 * which is `okay' if nothing has failed.
122 * Returns less than zero if the data set doesn't have any data sources.
123 */
ut_check_one_threshold(const data_set_t * ds,const value_list_t * vl,const threshold_t * th,const gauge_t * values,int * statuses)124 static int ut_check_one_threshold(const data_set_t *ds, const value_list_t *vl,
125 const threshold_t *th, const gauge_t *values,
126 int *statuses) { /* {{{ */
127 int ret = -1;
128 int status;
129 gauge_t values_copy[ds->ds_num];
130
131 memcpy(values_copy, values, sizeof(values_copy));
132
133 if ((th->flags & UT_FLAG_PERCENTAGE) != 0) {
134 int num = 0;
135 gauge_t sum = 0.0;
136
137 if (ds->ds_num == 1) {
138 WARNING(
139 "ut_check_one_threshold: The %s type has only one data "
140 "source, but you have configured to check this as a percentage. "
141 "That doesn't make much sense, because the percentage will always "
142 "be 100%%!",
143 ds->type);
144 }
145
146 /* Prepare `sum' and `num'. */
147 for (size_t i = 0; i < ds->ds_num; i++)
148 if (!isnan(values[i])) {
149 num++;
150 sum += values[i];
151 }
152
153 if ((num == 0) /* All data sources are undefined. */
154 || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
155 {
156 for (size_t i = 0; i < ds->ds_num; i++)
157 values_copy[i] = NAN;
158 } else /* We can actually calculate the percentage. */
159 {
160 for (size_t i = 0; i < ds->ds_num; i++)
161 values_copy[i] = 100.0 * values[i] / sum;
162 }
163 } /* if (UT_FLAG_PERCENTAGE) */
164
165 for (size_t i = 0; i < ds->ds_num; i++) {
166 status = ut_check_one_data_source(ds, vl, th, values_copy, i);
167 if (status != -1) {
168 ret = 0;
169 if (statuses[i] < status)
170 statuses[i] = status;
171 }
172 } /* for (ds->ds_num) */
173
174 return ret;
175 } /* }}} int ut_check_one_threshold */
176
177 /*
178 * int ut_check_threshold
179 *
180 * Gets a list of matching thresholds and searches for the worst status by one
181 * of the thresholds. Then reports that status using the ut_report_state
182 * function above.
183 * Returns zero on success and if no threshold has been configured. Returns
184 * less than zero on failure.
185 */
write_riemann_threshold_check(const data_set_t * ds,const value_list_t * vl,int * statuses)186 int write_riemann_threshold_check(const data_set_t *ds, const value_list_t *vl,
187 int *statuses) { /* {{{ */
188 threshold_t *th;
189 gauge_t *values;
190 int status;
191
192 assert(vl->values_len > 0);
193 memset(statuses, 0, vl->values_len * sizeof(*statuses));
194
195 if (threshold_tree == NULL)
196 return 0;
197
198 /* Is this lock really necessary? So far, thresholds are only inserted at
199 * startup. -octo */
200 pthread_mutex_lock(&threshold_lock);
201 th = threshold_search(vl);
202 pthread_mutex_unlock(&threshold_lock);
203 if (th == NULL)
204 return 0;
205
206 DEBUG("ut_check_threshold: Found matching threshold(s)");
207
208 values = uc_get_rate(ds, vl);
209 if (values == NULL)
210 return 0;
211
212 while (th != NULL) {
213 status = ut_check_one_threshold(ds, vl, th, values, statuses);
214 if (status < 0) {
215 ERROR("ut_check_threshold: ut_check_one_threshold failed.");
216 sfree(values);
217 return -1;
218 }
219
220 th = th->next;
221 } /* while (th) */
222
223 sfree(values);
224
225 return 0;
226 } /* }}} int ut_check_threshold */
227