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 **/
27 #include "collectd.h"
28 #include "common.h"
29 #include "filter_chain.h"
31 #include "utils_cache.h"
33 struct ts_data_s
34 {
35 double factor;
36 double offset;
38 char **data_sources;
39 size_t data_sources_num;
40 };
41 typedef struct ts_data_s ts_data_t;
43 static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
44 ts_data_t *data, int dsrc_index)
45 {
46 uint64_t curr_counter;
47 int status;
48 int failure;
50 /* Required meta data */
51 uint64_t prev_counter;
52 char key_prev_counter[128];
53 uint64_t int_counter;
54 char key_int_counter[128];
55 double int_fraction;
56 char key_int_fraction[128];
58 curr_counter = (uint64_t) vl->values[dsrc_index].counter;
60 ssnprintf (key_prev_counter, sizeof (key_prev_counter),
61 "target_scale[%p,%i]:prev_counter",
62 (void *) data, dsrc_index);
63 ssnprintf (key_int_counter, sizeof (key_int_counter),
64 "target_scale[%p,%i]:int_counter",
65 (void *) data, dsrc_index);
66 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
67 "target_scale[%p,%i]:int_fraction",
68 (void *) data, dsrc_index);
70 prev_counter = curr_counter;
71 int_counter = 0;
72 int_fraction = 0.0;
74 /* Query the meta data */
75 failure = 0;
77 status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
78 &prev_counter);
79 if (status != 0)
80 failure++;
82 status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
83 if (status != 0)
84 failure++;
86 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
87 if (status != 0)
88 failure++;
90 if (failure == 0)
91 {
92 uint64_t diff;
93 double rate;
95 diff = (uint64_t) counter_diff (prev_counter, curr_counter);
96 rate = ((double) diff) / CDTIME_T_TO_DOUBLE (vl->interval);
98 /* Modify the rate. */
99 if (!isnan (data->factor))
100 rate *= data->factor;
101 if (!isnan (data->offset))
102 rate += data->offset;
104 /* Calculate the internal counter. */
105 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
106 diff = (uint64_t) int_fraction;
107 int_fraction -= ((double) diff);
108 int_counter += diff;
110 assert (int_fraction >= 0.0);
111 assert (int_fraction < 1.0);
113 DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
114 "(+%g)",
115 curr_counter, rate, int_counter, int_fraction);
116 }
117 else /* (failure != 0) */
118 {
119 int_counter = 0;
120 int_fraction = 0.0;
121 }
123 vl->values[dsrc_index].counter = (counter_t) int_counter;
125 /* Update to the new counter value */
126 uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
127 uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
128 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
131 return (0);
132 } /* }}} int ts_invoke_counter */
134 static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
135 ts_data_t *data, int dsrc_index)
136 {
137 if (!isnan (data->factor))
138 vl->values[dsrc_index].gauge *= data->factor;
139 if (!isnan (data->offset))
140 vl->values[dsrc_index].gauge += data->offset;
142 return (0);
143 } /* }}} int ts_invoke_gauge */
145 static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
146 ts_data_t *data, int dsrc_index)
147 {
148 int64_t curr_derive;
149 int status;
150 int failure;
152 /* Required meta data */
153 int64_t prev_derive;
154 char key_prev_derive[128];
155 int64_t int_derive;
156 char key_int_derive[128];
157 double int_fraction;
158 char key_int_fraction[128];
160 curr_derive = (int64_t) vl->values[dsrc_index].derive;
162 ssnprintf (key_prev_derive, sizeof (key_prev_derive),
163 "target_scale[%p,%i]:prev_derive",
164 (void *) data, dsrc_index);
165 ssnprintf (key_int_derive, sizeof (key_int_derive),
166 "target_scale[%p,%i]:int_derive",
167 (void *) data, dsrc_index);
168 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
169 "target_scale[%p,%i]:int_fraction",
170 (void *) data, dsrc_index);
172 prev_derive = curr_derive;
173 int_derive = 0;
174 int_fraction = 0.0;
176 /* Query the meta data */
177 failure = 0;
179 status = uc_meta_data_get_signed_int (vl, key_prev_derive,
180 &prev_derive);
181 if (status != 0)
182 failure++;
184 status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
185 if (status != 0)
186 failure++;
188 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
189 if (status != 0)
190 failure++;
192 if (failure == 0)
193 {
194 int64_t difference;
195 double rate;
197 /* Calcualte the rate */
198 difference = curr_derive - prev_derive;
199 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
201 /* Modify the rate. */
202 if (!isnan (data->factor))
203 rate *= data->factor;
204 if (!isnan (data->offset))
205 rate += data->offset;
207 /* Calculate the internal derive. */
208 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
209 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
210 difference = ((int64_t) int_fraction) - 1;
211 else
212 difference = (int64_t) int_fraction;
213 int_fraction -= ((double) difference);
214 int_derive += difference;
216 assert (int_fraction >= 0.0);
217 assert (int_fraction < 1.0);
219 DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
220 "(+%g)",
221 curr_derive, rate, int_derive, int_fraction);
222 }
223 else /* (failure != 0) */
224 {
225 int_derive = 0;
226 int_fraction = 0.0;
227 }
229 vl->values[dsrc_index].derive = (derive_t) int_derive;
231 /* Update to the new derive value */
232 uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
233 uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
234 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
236 return (0);
237 } /* }}} int ts_invoke_derive */
239 static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
240 ts_data_t *data, int dsrc_index)
241 {
242 uint64_t curr_absolute;
243 double rate;
244 int status;
246 /* Required meta data */
247 double int_fraction;
248 char key_int_fraction[128];
250 curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
252 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
253 "target_scale[%p,%i]:int_fraction",
254 (void *) data, dsrc_index);
256 int_fraction = 0.0;
258 /* Query the meta data */
259 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
260 if (status != 0)
261 int_fraction = 0.0;
263 rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
265 /* Modify the rate. */
266 if (!isnan (data->factor))
267 rate *= data->factor;
268 if (!isnan (data->offset))
269 rate += data->offset;
271 /* Calculate the new absolute. */
272 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
273 curr_absolute = (uint64_t) int_fraction;
274 int_fraction -= ((double) curr_absolute);
276 vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
278 /* Update to the new absolute value */
279 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
281 return (0);
282 } /* }}} int ts_invoke_absolute */
284 static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
285 {
286 if ((ci->values_num != 1)
287 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
288 {
289 WARNING ("scale target: The `%s' config option needs "
290 "exactly one numeric argument.", ci->key);
291 return (-1);
292 }
294 *ret = ci->values[0].value.number;
295 DEBUG ("ts_config_set_double: *ret = %g", *ret);
297 return (0);
298 } /* }}} int ts_config_set_double */
300 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
301 oconfig_item_t *ci)
302 {
303 size_t new_data_sources_num;
304 char **temp;
305 int i;
307 /* Check number of arbuments. */
308 if (ci->values_num < 1)
309 {
310 ERROR ("`value' match: `%s' needs at least one argument.",
311 ci->key);
312 return (-1);
313 }
315 /* Check type of arguments */
316 for (i = 0; i < ci->values_num; i++)
317 {
318 if (ci->values[i].type == OCONFIG_TYPE_STRING)
319 continue;
321 ERROR ("`value' match: `%s' accepts only string arguments "
322 "(argument %i is a %s).",
323 ci->key, i + 1,
324 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
325 ? "truth value" : "number");
326 return (-1);
327 }
329 /* Allocate space for the char pointers */
330 new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
331 temp = (char **) realloc (data->data_sources,
332 new_data_sources_num * sizeof (char *));
333 if (temp == NULL)
334 {
335 ERROR ("`value' match: realloc failed.");
336 return (-1);
337 }
338 data->data_sources = temp;
340 /* Copy the strings, allocating memory as needed. */
341 for (i = 0; i < ci->values_num; i++)
342 {
343 size_t j;
345 /* If we get here, there better be memory for us to write to. */
346 assert (data->data_sources_num < new_data_sources_num);
348 j = data->data_sources_num;
349 data->data_sources[j] = sstrdup (ci->values[i].value.string);
350 if (data->data_sources[j] == NULL)
351 {
352 ERROR ("`value' match: sstrdup failed.");
353 continue;
354 }
355 data->data_sources_num++;
356 }
358 return (0);
359 } /* }}} int ts_config_add_data_source */
361 static int ts_destroy (void **user_data) /* {{{ */
362 {
363 ts_data_t *data;
365 if (user_data == NULL)
366 return (-EINVAL);
368 data = (ts_data_t *) *user_data;
370 if ((data != NULL) && (data->data_sources != NULL))
371 {
372 size_t i;
373 for (i = 0; i < data->data_sources_num; i++)
374 sfree (data->data_sources[i]);
375 sfree (data->data_sources);
376 }
378 sfree (data);
379 *user_data = NULL;
381 return (0);
382 } /* }}} int ts_destroy */
384 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
385 {
386 ts_data_t *data;
387 int status;
388 int i;
390 data = calloc (1, sizeof (*data));
391 if (data == NULL)
392 {
393 ERROR ("ts_create: calloc failed.");
394 return (-ENOMEM);
395 }
397 data->factor = NAN;
398 data->offset = NAN;
400 status = 0;
401 for (i = 0; i < ci->children_num; i++)
402 {
403 oconfig_item_t *child = ci->children + i;
405 if (strcasecmp ("Factor", child->key) == 0)
406 status = ts_config_set_double (&data->factor, child);
407 else if (strcasecmp ("Offset", child->key) == 0)
408 status = ts_config_set_double (&data->offset, child);
409 else if (strcasecmp ("DataSource", child->key) == 0)
410 status = ts_config_add_data_source(data, child);
411 else
412 {
413 ERROR ("Target `scale': The `%s' configuration option is not understood "
414 "and will be ignored.", child->key);
415 status = 0;
416 }
418 if (status != 0)
419 break;
420 }
422 /* Additional sanity-checking */
423 while (status == 0)
424 {
425 if (isnan (data->factor) && isnan (data->offset))
426 {
427 ERROR ("Target `scale': You need to at least set either the `Factor' "
428 "or `Offset' option!");
429 status = -1;
430 }
432 break;
433 }
435 if (status != 0)
436 {
437 ts_destroy ((void *) &data);
438 return (status);
439 }
441 *user_data = data;
442 return (0);
443 } /* }}} int ts_create */
445 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
446 notification_meta_t __attribute__((unused)) **meta, void **user_data)
447 {
448 ts_data_t *data;
449 size_t i;
451 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
452 return (-EINVAL);
454 data = *user_data;
455 if (data == NULL)
456 {
457 ERROR ("Target `scale': Invoke: `data' is NULL.");
458 return (-EINVAL);
459 }
461 for (i = 0; i < ds->ds_num; i++)
462 {
463 /* If we've got a list of data sources, is it in the list? */
464 if (data->data_sources) {
465 size_t j;
466 for (j = 0; j < data->data_sources_num; j++)
467 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
468 break;
470 /* No match, ignore */
471 if (j >= data->data_sources_num)
472 continue;
473 }
475 if (ds->ds[i].type == DS_TYPE_COUNTER)
476 ts_invoke_counter (ds, vl, data, i);
477 else if (ds->ds[i].type == DS_TYPE_GAUGE)
478 ts_invoke_gauge (ds, vl, data, i);
479 else if (ds->ds[i].type == DS_TYPE_DERIVE)
480 ts_invoke_derive (ds, vl, data, i);
481 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
482 ts_invoke_absolute (ds, vl, data, i);
483 else
484 ERROR ("Target `scale': Ignoring unknown data source type %i",
485 ds->ds[i].type);
486 }
488 return (FC_TARGET_CONTINUE);
489 } /* }}} int ts_invoke */
491 void module_register (void)
492 {
493 target_proc_t tproc;
495 memset (&tproc, 0, sizeof (tproc));
496 tproc.create = ts_create;
497 tproc.destroy = ts_destroy;
498 tproc.invoke = ts_invoke;
499 fc_register_target ("scale", tproc);
500 } /* module_register */
502 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */