8659c8d7ba63fdaed6658ab48fb6cbea041ac49f
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"
29 #include "common.h"
30 #include "filter_chain.h"
32 #include "utils_cache.h"
34 struct ts_data_s
35 {
36 double factor;
37 double offset;
39 char **data_sources;
40 size_t data_sources_num;
41 };
42 typedef struct ts_data_s ts_data_t;
44 static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */
45 ts_data_t *data, int dsrc_index)
46 {
47 uint64_t curr_counter;
48 int status;
49 int failure;
51 /* Required meta data */
52 uint64_t prev_counter;
53 char key_prev_counter[128];
54 uint64_t int_counter;
55 char key_int_counter[128];
56 double int_fraction;
57 char key_int_fraction[128];
59 curr_counter = (uint64_t) vl->values[dsrc_index].counter;
61 ssnprintf (key_prev_counter, sizeof (key_prev_counter),
62 "target_scale[%p,%i]:prev_counter",
63 (void *) data, dsrc_index);
64 ssnprintf (key_int_counter, sizeof (key_int_counter),
65 "target_scale[%p,%i]:int_counter",
66 (void *) data, dsrc_index);
67 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
68 "target_scale[%p,%i]:int_fraction",
69 (void *) data, dsrc_index);
71 prev_counter = curr_counter;
72 int_counter = 0;
73 int_fraction = 0.0;
75 /* Query the meta data */
76 failure = 0;
78 status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
79 &prev_counter);
80 if (status != 0)
81 failure++;
83 status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
84 if (status != 0)
85 failure++;
87 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
88 if (status != 0)
89 failure++;
91 if (failure == 0)
92 {
93 uint64_t diff;
94 double rate;
96 diff = (uint64_t) counter_diff (prev_counter, curr_counter);
97 rate = ((double) diff) / CDTIME_T_TO_DOUBLE (vl->interval);
99 /* Modify the rate. */
100 if (!isnan (data->factor))
101 rate *= data->factor;
102 if (!isnan (data->offset))
103 rate += data->offset;
105 /* Calculate the internal counter. */
106 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
107 diff = (uint64_t) int_fraction;
108 int_fraction -= ((double) diff);
109 int_counter += diff;
111 assert (int_fraction >= 0.0);
112 assert (int_fraction < 1.0);
114 DEBUG ("Target `scale': ts_invoke_counter: %"PRIu64" -> %g -> %"PRIu64
115 "(+%g)",
116 curr_counter, rate, int_counter, int_fraction);
117 }
118 else /* (failure != 0) */
119 {
120 int_counter = 0;
121 int_fraction = 0.0;
122 }
124 vl->values[dsrc_index].counter = (counter_t) int_counter;
126 /* Update to the new counter value */
127 uc_meta_data_add_unsigned_int (vl, key_prev_counter, curr_counter);
128 uc_meta_data_add_unsigned_int (vl, key_int_counter, int_counter);
129 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
132 return (0);
133 } /* }}} int ts_invoke_counter */
135 static int ts_invoke_gauge (const data_set_t *ds, value_list_t *vl, /* {{{ */
136 ts_data_t *data, int dsrc_index)
137 {
138 if (!isnan (data->factor))
139 vl->values[dsrc_index].gauge *= data->factor;
140 if (!isnan (data->offset))
141 vl->values[dsrc_index].gauge += data->offset;
143 return (0);
144 } /* }}} int ts_invoke_gauge */
146 static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */
147 ts_data_t *data, int dsrc_index)
148 {
149 int64_t curr_derive;
150 int status;
151 int failure;
153 /* Required meta data */
154 int64_t prev_derive;
155 char key_prev_derive[128];
156 int64_t int_derive;
157 char key_int_derive[128];
158 double int_fraction;
159 char key_int_fraction[128];
161 curr_derive = (int64_t) vl->values[dsrc_index].derive;
163 ssnprintf (key_prev_derive, sizeof (key_prev_derive),
164 "target_scale[%p,%i]:prev_derive",
165 (void *) data, dsrc_index);
166 ssnprintf (key_int_derive, sizeof (key_int_derive),
167 "target_scale[%p,%i]:int_derive",
168 (void *) data, dsrc_index);
169 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
170 "target_scale[%p,%i]:int_fraction",
171 (void *) data, dsrc_index);
173 prev_derive = curr_derive;
174 int_derive = 0;
175 int_fraction = 0.0;
177 /* Query the meta data */
178 failure = 0;
180 status = uc_meta_data_get_signed_int (vl, key_prev_derive,
181 &prev_derive);
182 if (status != 0)
183 failure++;
185 status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
186 if (status != 0)
187 failure++;
189 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
190 if (status != 0)
191 failure++;
193 if (failure == 0)
194 {
195 int64_t difference;
196 double rate;
198 /* Calcualte the rate */
199 difference = curr_derive - prev_derive;
200 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
202 /* Modify the rate. */
203 if (!isnan (data->factor))
204 rate *= data->factor;
205 if (!isnan (data->offset))
206 rate += data->offset;
208 /* Calculate the internal derive. */
209 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
210 if (int_fraction < 0.0) /* handle negative integer rounding correctly */
211 difference = ((int64_t) int_fraction) - 1;
212 else
213 difference = (int64_t) int_fraction;
214 int_fraction -= ((double) difference);
215 int_derive += difference;
217 assert (int_fraction >= 0.0);
218 assert (int_fraction < 1.0);
220 DEBUG ("Target `scale': ts_invoke_derive: %"PRIu64" -> %g -> %"PRIu64
221 "(+%g)",
222 curr_derive, rate, int_derive, int_fraction);
223 }
224 else /* (failure != 0) */
225 {
226 int_derive = 0;
227 int_fraction = 0.0;
228 }
230 vl->values[dsrc_index].derive = (derive_t) int_derive;
232 /* Update to the new derive value */
233 uc_meta_data_add_signed_int (vl, key_prev_derive, curr_derive);
234 uc_meta_data_add_signed_int (vl, key_int_derive, int_derive);
235 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
237 return (0);
238 } /* }}} int ts_invoke_derive */
240 static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */
241 ts_data_t *data, int dsrc_index)
242 {
243 uint64_t curr_absolute;
244 double rate;
245 int status;
247 /* Required meta data */
248 double int_fraction;
249 char key_int_fraction[128];
251 curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
253 ssnprintf (key_int_fraction, sizeof (key_int_fraction),
254 "target_scale[%p,%i]:int_fraction",
255 (void *) data, dsrc_index);
257 int_fraction = 0.0;
259 /* Query the meta data */
260 status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
261 if (status != 0)
262 int_fraction = 0.0;
264 rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
266 /* Modify the rate. */
267 if (!isnan (data->factor))
268 rate *= data->factor;
269 if (!isnan (data->offset))
270 rate += data->offset;
272 /* Calculate the new absolute. */
273 int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval));
274 curr_absolute = (uint64_t) int_fraction;
275 int_fraction -= ((double) curr_absolute);
277 vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
279 /* Update to the new absolute value */
280 uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
282 return (0);
283 } /* }}} int ts_invoke_absolute */
285 static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
286 {
287 if ((ci->values_num != 1)
288 || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
289 {
290 WARNING ("scale target: The `%s' config option needs "
291 "exactly one numeric argument.", ci->key);
292 return (-1);
293 }
295 *ret = ci->values[0].value.number;
296 DEBUG ("ts_config_set_double: *ret = %g", *ret);
298 return (0);
299 } /* }}} int ts_config_set_double */
301 static int ts_config_add_data_source(ts_data_t *data, /* {{{ */
302 oconfig_item_t *ci)
303 {
304 size_t new_data_sources_num;
305 char **temp;
306 int i;
308 /* Check number of arbuments. */
309 if (ci->values_num < 1)
310 {
311 ERROR ("`value' match: `%s' needs at least one argument.",
312 ci->key);
313 return (-1);
314 }
316 /* Check type of arguments */
317 for (i = 0; i < ci->values_num; i++)
318 {
319 if (ci->values[i].type == OCONFIG_TYPE_STRING)
320 continue;
322 ERROR ("`value' match: `%s' accepts only string arguments "
323 "(argument %i is a %s).",
324 ci->key, i + 1,
325 (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
326 ? "truth value" : "number");
327 return (-1);
328 }
330 /* Allocate space for the char pointers */
331 new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
332 temp = realloc (data->data_sources,
333 new_data_sources_num * sizeof (char *));
334 if (temp == NULL)
335 {
336 ERROR ("`value' match: realloc failed.");
337 return (-1);
338 }
339 data->data_sources = temp;
341 /* Copy the strings, allocating memory as needed. */
342 for (i = 0; i < ci->values_num; i++)
343 {
344 size_t j;
346 /* If we get here, there better be memory for us to write to. */
347 assert (data->data_sources_num < new_data_sources_num);
349 j = data->data_sources_num;
350 data->data_sources[j] = sstrdup (ci->values[i].value.string);
351 if (data->data_sources[j] == NULL)
352 {
353 ERROR ("`value' match: sstrdup failed.");
354 continue;
355 }
356 data->data_sources_num++;
357 }
359 return (0);
360 } /* }}} int ts_config_add_data_source */
362 static int ts_destroy (void **user_data) /* {{{ */
363 {
364 ts_data_t *data;
366 if (user_data == NULL)
367 return (-EINVAL);
369 data = (ts_data_t *) *user_data;
371 if ((data != NULL) && (data->data_sources != NULL))
372 {
373 size_t i;
374 for (i = 0; i < data->data_sources_num; i++)
375 sfree (data->data_sources[i]);
376 sfree (data->data_sources);
377 }
379 sfree (data);
380 *user_data = NULL;
382 return (0);
383 } /* }}} int ts_destroy */
385 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
386 {
387 ts_data_t *data;
388 int status;
389 int i;
391 data = calloc (1, sizeof (*data));
392 if (data == NULL)
393 {
394 ERROR ("ts_create: calloc failed.");
395 return (-ENOMEM);
396 }
398 data->factor = NAN;
399 data->offset = NAN;
401 status = 0;
402 for (i = 0; i < ci->children_num; i++)
403 {
404 oconfig_item_t *child = ci->children + i;
406 if (strcasecmp ("Factor", child->key) == 0)
407 status = ts_config_set_double (&data->factor, child);
408 else if (strcasecmp ("Offset", child->key) == 0)
409 status = ts_config_set_double (&data->offset, child);
410 else if (strcasecmp ("DataSource", child->key) == 0)
411 status = ts_config_add_data_source(data, child);
412 else
413 {
414 ERROR ("Target `scale': The `%s' configuration option is not understood "
415 "and will be ignored.", child->key);
416 status = 0;
417 }
419 if (status != 0)
420 break;
421 }
423 /* Additional sanity-checking */
424 while (status == 0)
425 {
426 if (isnan (data->factor) && isnan (data->offset))
427 {
428 ERROR ("Target `scale': You need to at least set either the `Factor' "
429 "or `Offset' option!");
430 status = -1;
431 }
433 break;
434 }
436 if (status != 0)
437 {
438 ts_destroy ((void *) &data);
439 return (status);
440 }
442 *user_data = data;
443 return (0);
444 } /* }}} int ts_create */
446 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
447 notification_meta_t __attribute__((unused)) **meta, void **user_data)
448 {
449 ts_data_t *data;
450 size_t i;
452 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
453 return (-EINVAL);
455 data = *user_data;
456 if (data == NULL)
457 {
458 ERROR ("Target `scale': Invoke: `data' is NULL.");
459 return (-EINVAL);
460 }
462 for (i = 0; i < ds->ds_num; i++)
463 {
464 /* If we've got a list of data sources, is it in the list? */
465 if (data->data_sources) {
466 size_t j;
467 for (j = 0; j < data->data_sources_num; j++)
468 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
469 break;
471 /* No match, ignore */
472 if (j >= data->data_sources_num)
473 continue;
474 }
476 if (ds->ds[i].type == DS_TYPE_COUNTER)
477 ts_invoke_counter (ds, vl, data, i);
478 else if (ds->ds[i].type == DS_TYPE_GAUGE)
479 ts_invoke_gauge (ds, vl, data, i);
480 else if (ds->ds[i].type == DS_TYPE_DERIVE)
481 ts_invoke_derive (ds, vl, data, i);
482 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
483 ts_invoke_absolute (ds, vl, data, i);
484 else
485 ERROR ("Target `scale': Ignoring unknown data source type %i",
486 ds->ds[i].type);
487 }
489 return (FC_TARGET_CONTINUE);
490 } /* }}} int ts_invoke */
492 void module_register (void)
493 {
494 target_proc_t tproc = { 0 };
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 : */