1 /**
2 * collectd - src/target_scale.c
3 * Copyright (C) 2008-2009 Florian Forster
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Florian Forster <octo at collectd.org>
25 **/
26
27 #include "collectd.h"
28
29 #include "filter_chain.h"
30 #include "utils/common/common.h"
31
32 #include "utils_cache.h"
33
34 struct ts_data_s {
35 double factor;
36 double offset;
37
38 char **data_sources;
39 size_t data_sources_num;
40 };
41 typedef struct ts_data_s ts_data_t;
42
ts_invoke_counter(const data_set_t * ds,value_list_t * vl,ts_data_t * data,int dsrc_index)43 static int ts_invoke_counter(const data_set_t *ds, value_list_t *vl, /* {{{ */
44 ts_data_t *data, int dsrc_index) {
45 uint64_t curr_counter;
46 int status;
47 int failure;
48
49 /* Required meta data */
50 uint64_t prev_counter;
51 char key_prev_counter[128];
52 uint64_t int_counter;
53 char key_int_counter[128];
54 double int_fraction;
55 char key_int_fraction[128];
56
57 curr_counter = (uint64_t)vl->values[dsrc_index].counter;
58
59 snprintf(key_prev_counter, sizeof(key_prev_counter),
60 "target_scale[%p,%i]:prev_counter", (void *)data, dsrc_index);
61 snprintf(key_int_counter, sizeof(key_int_counter),
62 "target_scale[%p,%i]:int_counter", (void *)data, dsrc_index);
63 snprintf(key_int_fraction, sizeof(key_int_fraction),
64 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
65
66 prev_counter = curr_counter;
67 int_counter = 0;
68 int_fraction = 0.0;
69
70 /* Query the meta data */
71 failure = 0;
72
73 status = uc_meta_data_get_unsigned_int(vl, key_prev_counter, &prev_counter);
74 if (status != 0)
75 failure++;
76
77 status = uc_meta_data_get_unsigned_int(vl, key_int_counter, &int_counter);
78 if (status != 0)
79 failure++;
80
81 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
82 if (status != 0)
83 failure++;
84
85 if (failure == 0) {
86 uint64_t diff;
87 double rate;
88
89 diff = (uint64_t)counter_diff(prev_counter, curr_counter);
90 rate = ((double)diff) / CDTIME_T_TO_DOUBLE(vl->interval);
91
92 /* Modify the rate. */
93 if (!isnan(data->factor))
94 rate *= data->factor;
95 if (!isnan(data->offset))
96 rate += data->offset;
97
98 /* Calculate the internal counter. */
99 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
100 diff = (uint64_t)int_fraction;
101 int_fraction -= ((double)diff);
102 int_counter += diff;
103
104 assert(int_fraction >= 0.0);
105 assert(int_fraction < 1.0);
106
107 DEBUG("Target `scale': ts_invoke_counter: %" PRIu64 " -> %g -> %" PRIu64
108 "(+%g)",
109 curr_counter, rate, int_counter, int_fraction);
110 } else /* (failure != 0) */
111 {
112 int_counter = 0;
113 int_fraction = 0.0;
114 }
115
116 vl->values[dsrc_index].counter = (counter_t)int_counter;
117
118 /* Update to the new counter value */
119 uc_meta_data_add_unsigned_int(vl, key_prev_counter, curr_counter);
120 uc_meta_data_add_unsigned_int(vl, key_int_counter, int_counter);
121 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
122
123 return 0;
124 } /* }}} int ts_invoke_counter */
125
ts_invoke_gauge(const data_set_t * ds,value_list_t * vl,ts_data_t * data,int dsrc_index)126 static int ts_invoke_gauge(const data_set_t *ds, value_list_t *vl, /* {{{ */
127 ts_data_t *data, int dsrc_index) {
128 if (!isnan(data->factor))
129 vl->values[dsrc_index].gauge *= data->factor;
130 if (!isnan(data->offset))
131 vl->values[dsrc_index].gauge += data->offset;
132
133 return 0;
134 } /* }}} int ts_invoke_gauge */
135
ts_invoke_derive(const data_set_t * ds,value_list_t * vl,ts_data_t * data,int dsrc_index)136 static int ts_invoke_derive(const data_set_t *ds, value_list_t *vl, /* {{{ */
137 ts_data_t *data, int dsrc_index) {
138 int64_t curr_derive;
139 int status;
140 int failure;
141
142 /* Required meta data */
143 int64_t prev_derive;
144 char key_prev_derive[128];
145 int64_t int_derive;
146 char key_int_derive[128];
147 double int_fraction;
148 char key_int_fraction[128];
149
150 curr_derive = (int64_t)vl->values[dsrc_index].derive;
151
152 snprintf(key_prev_derive, sizeof(key_prev_derive),
153 "target_scale[%p,%i]:prev_derive", (void *)data, dsrc_index);
154 snprintf(key_int_derive, sizeof(key_int_derive),
155 "target_scale[%p,%i]:int_derive", (void *)data, dsrc_index);
156 snprintf(key_int_fraction, sizeof(key_int_fraction),
157 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
158
159 prev_derive = curr_derive;
160 int_derive = 0;
161 int_fraction = 0.0;
162
163 /* Query the meta data */
164 failure = 0;
165
166 status = uc_meta_data_get_signed_int(vl, key_prev_derive, &prev_derive);
167 if (status != 0)
168 failure++;
169
170 status = uc_meta_data_get_signed_int(vl, key_int_derive, &int_derive);
171 if (status != 0)
172 failure++;
173
174 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
175 if (status != 0)
176 failure++;
177
178 if (failure == 0) {
179 int64_t difference;
180 double rate;
181
182 /* Calcualte the rate */
183 difference = curr_derive - prev_derive;
184 rate = ((double)difference) / CDTIME_T_TO_DOUBLE(vl->interval);
185
186 /* Modify the rate. */
187 if (!isnan(data->factor))
188 rate *= data->factor;
189 if (!isnan(data->offset))
190 rate += data->offset;
191
192 /* Calculate the internal derive. */
193 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
194 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
195 difference = ((int64_t)int_fraction) - 1;
196 else
197 difference = (int64_t)int_fraction;
198 int_fraction -= ((double)difference);
199 int_derive += difference;
200
201 assert(int_fraction >= 0.0);
202 assert(int_fraction < 1.0);
203
204 DEBUG("Target `scale': ts_invoke_derive: %" PRIu64 " -> %g -> %" PRIu64
205 "(+%g)",
206 curr_derive, rate, int_derive, int_fraction);
207 } else /* (failure != 0) */
208 {
209 int_derive = 0;
210 int_fraction = 0.0;
211 }
212
213 vl->values[dsrc_index].derive = (derive_t)int_derive;
214
215 /* Update to the new derive value */
216 uc_meta_data_add_signed_int(vl, key_prev_derive, curr_derive);
217 uc_meta_data_add_signed_int(vl, key_int_derive, int_derive);
218 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
219
220 return 0;
221 } /* }}} int ts_invoke_derive */
222
ts_invoke_absolute(const data_set_t * ds,value_list_t * vl,ts_data_t * data,int dsrc_index)223 static int ts_invoke_absolute(const data_set_t *ds, value_list_t *vl, /* {{{ */
224 ts_data_t *data, int dsrc_index) {
225 uint64_t curr_absolute;
226 double rate;
227 int status;
228
229 /* Required meta data */
230 double int_fraction;
231 char key_int_fraction[128];
232
233 curr_absolute = (uint64_t)vl->values[dsrc_index].absolute;
234
235 snprintf(key_int_fraction, sizeof(key_int_fraction),
236 "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index);
237
238 int_fraction = 0.0;
239
240 /* Query the meta data */
241 status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction);
242 if (status != 0)
243 int_fraction = 0.0;
244
245 rate = ((double)curr_absolute) / CDTIME_T_TO_DOUBLE(vl->interval);
246
247 /* Modify the rate. */
248 if (!isnan(data->factor))
249 rate *= data->factor;
250 if (!isnan(data->offset))
251 rate += data->offset;
252
253 /* Calculate the new absolute. */
254 int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval));
255 curr_absolute = (uint64_t)int_fraction;
256 int_fraction -= ((double)curr_absolute);
257
258 vl->values[dsrc_index].absolute = (absolute_t)curr_absolute;
259
260 /* Update to the new absolute value */
261 uc_meta_data_add_double(vl, key_int_fraction, int_fraction);
262
263 return 0;
264 } /* }}} int ts_invoke_absolute */
265
ts_config_set_double(double * ret,oconfig_item_t * ci)266 static int ts_config_set_double(double *ret, oconfig_item_t *ci) /* {{{ */
267 {
268 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
269 WARNING("scale target: The `%s' config option needs "
270 "exactly one numeric argument.",
271 ci->key);
272 return -1;
273 }
274
275 *ret = ci->values[0].value.number;
276 DEBUG("ts_config_set_double: *ret = %g", *ret);
277
278 return 0;
279 } /* }}} int ts_config_set_double */
280
ts_config_add_data_source(ts_data_t * data,oconfig_item_t * ci)281 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
282 oconfig_item_t *ci) {
283 size_t new_data_sources_num;
284 char **temp;
285
286 /* Check number of arbuments. */
287 if (ci->values_num < 1) {
288 ERROR("`value' match: `%s' needs at least one argument.", ci->key);
289 return -1;
290 }
291
292 /* Check type of arguments */
293 for (int i = 0; i < ci->values_num; i++) {
294 if (ci->values[i].type == OCONFIG_TYPE_STRING)
295 continue;
296
297 ERROR("`value' match: `%s' accepts only string arguments "
298 "(argument %i is a %s).",
299 ci->key, i + 1,
300 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) ? "truth value"
301 : "number");
302 return -1;
303 }
304
305 /* Allocate space for the char pointers */
306 new_data_sources_num = data->data_sources_num + ((size_t)ci->values_num);
307 temp = realloc(data->data_sources, new_data_sources_num * sizeof(char *));
308 if (temp == NULL) {
309 ERROR("`value' match: realloc failed.");
310 return -1;
311 }
312 data->data_sources = temp;
313
314 /* Copy the strings, allocating memory as needed. */
315 for (int i = 0; i < ci->values_num; i++) {
316 size_t j;
317
318 /* If we get here, there better be memory for us to write to. */
319 assert(data->data_sources_num < new_data_sources_num);
320
321 j = data->data_sources_num;
322 data->data_sources[j] = sstrdup(ci->values[i].value.string);
323 if (data->data_sources[j] == NULL) {
324 ERROR("`value' match: sstrdup failed.");
325 continue;
326 }
327 data->data_sources_num++;
328 }
329
330 return 0;
331 } /* }}} int ts_config_add_data_source */
332
ts_destroy(void ** user_data)333 static int ts_destroy(void **user_data) /* {{{ */
334 {
335 ts_data_t *data;
336
337 if (user_data == NULL)
338 return -EINVAL;
339
340 data = (ts_data_t *)*user_data;
341
342 if ((data != NULL) && (data->data_sources != NULL)) {
343 for (size_t i = 0; i < data->data_sources_num; i++)
344 sfree(data->data_sources[i]);
345 sfree(data->data_sources);
346 }
347
348 sfree(data);
349 *user_data = NULL;
350
351 return 0;
352 } /* }}} int ts_destroy */
353
ts_create(const oconfig_item_t * ci,void ** user_data)354 static int ts_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
355 {
356 ts_data_t *data;
357 int status;
358
359 data = calloc(1, sizeof(*data));
360 if (data == NULL) {
361 ERROR("ts_create: calloc failed.");
362 return -ENOMEM;
363 }
364
365 data->factor = NAN;
366 data->offset = NAN;
367
368 status = 0;
369 for (int i = 0; i < ci->children_num; i++) {
370 oconfig_item_t *child = ci->children + i;
371
372 if (strcasecmp("Factor", child->key) == 0)
373 status = ts_config_set_double(&data->factor, child);
374 else if (strcasecmp("Offset", child->key) == 0)
375 status = ts_config_set_double(&data->offset, child);
376 else if (strcasecmp("DataSource", child->key) == 0)
377 status = ts_config_add_data_source(data, child);
378 else {
379 ERROR("Target `scale': The `%s' configuration option is not understood "
380 "and will be ignored.",
381 child->key);
382 status = 0;
383 }
384
385 if (status != 0)
386 break;
387 }
388
389 /* Additional sanity-checking */
390 while (status == 0) {
391 if (isnan(data->factor) && isnan(data->offset)) {
392 ERROR("Target `scale': You need to at least set either the `Factor' "
393 "or `Offset' option!");
394 status = -1;
395 }
396
397 break;
398 }
399
400 if (status != 0) {
401 ts_destroy((void *)&data);
402 return status;
403 }
404
405 *user_data = data;
406 return 0;
407 } /* }}} int ts_create */
408
ts_invoke(const data_set_t * ds,value_list_t * vl,notification_meta_t ** meta,void ** user_data)409 static int ts_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */
410 notification_meta_t __attribute__((unused)) * *meta,
411 void **user_data) {
412 ts_data_t *data;
413
414 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
415 return -EINVAL;
416
417 data = *user_data;
418 if (data == NULL) {
419 ERROR("Target `scale': Invoke: `data' is NULL.");
420 return -EINVAL;
421 }
422
423 for (size_t i = 0; i < ds->ds_num; i++) {
424 /* If we've got a list of data sources, is it in the list? */
425 if (data->data_sources) {
426 size_t j;
427 for (j = 0; j < data->data_sources_num; j++)
428 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
429 break;
430
431 /* No match, ignore */
432 if (j >= data->data_sources_num)
433 continue;
434 }
435
436 if (ds->ds[i].type == DS_TYPE_COUNTER)
437 ts_invoke_counter(ds, vl, data, i);
438 else if (ds->ds[i].type == DS_TYPE_GAUGE)
439 ts_invoke_gauge(ds, vl, data, i);
440 else if (ds->ds[i].type == DS_TYPE_DERIVE)
441 ts_invoke_derive(ds, vl, data, i);
442 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
443 ts_invoke_absolute(ds, vl, data, i);
444 else
445 ERROR("Target `scale': Ignoring unknown data source type %i",
446 ds->ds[i].type);
447 }
448
449 return FC_TARGET_CONTINUE;
450 } /* }}} int ts_invoke */
451
module_register(void)452 void module_register(void) {
453 target_proc_t tproc = {0};
454
455 tproc.create = ts_create;
456 tproc.destroy = ts_destroy;
457 tproc.invoke = ts_invoke;
458 fc_register_target("scale", tproc);
459 } /* module_register */
460