Merge pull request #1830 from rubenk/move-collectd-header
[collectd.git] / src / target_scale.c
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 "common.h"
30 #include "filter_chain.h"
31
32 #include "utils_cache.h"
33
34 struct ts_data_s
35 {
36         double factor;
37         double offset;
38
39         char **data_sources;
40         size_t data_sources_num;
41 };
42 typedef struct ts_data_s ts_data_t;
43
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;
50
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];
58
59         curr_counter = (uint64_t) vl->values[dsrc_index].counter;
60
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);
70
71         prev_counter = curr_counter;
72         int_counter = 0;
73         int_fraction = 0.0;
74
75         /* Query the meta data */
76         failure = 0;
77
78         status = uc_meta_data_get_unsigned_int (vl, key_prev_counter,
79                         &prev_counter);
80         if (status != 0)
81                 failure++;
82
83         status = uc_meta_data_get_unsigned_int (vl, key_int_counter, &int_counter);
84         if (status != 0)
85                 failure++;
86
87         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
88         if (status != 0)
89                 failure++;
90
91         if (failure == 0)
92         {
93                 uint64_t diff;
94                 double rate;
95
96                 diff = (uint64_t) counter_diff (prev_counter, curr_counter);
97                 rate = ((double) diff) / CDTIME_T_TO_DOUBLE (vl->interval);
98
99                 /* Modify the rate. */
100                 if (!isnan (data->factor))
101                         rate *= data->factor;
102                 if (!isnan (data->offset))
103                         rate += data->offset;
104
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;
110
111                 assert (int_fraction >= 0.0);
112                 assert (int_fraction <  1.0);
113
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         }
123
124         vl->values[dsrc_index].counter = (counter_t) int_counter;
125
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);
130
131
132         return (0);
133 } /* }}} int ts_invoke_counter */
134
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;
142
143         return (0);
144 } /* }}} int ts_invoke_gauge */
145
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;
152
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];
160
161         curr_derive = (int64_t) vl->values[dsrc_index].derive;
162
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);
172
173         prev_derive = curr_derive;
174         int_derive = 0;
175         int_fraction = 0.0;
176
177         /* Query the meta data */
178         failure = 0;
179
180         status = uc_meta_data_get_signed_int (vl, key_prev_derive,
181                         &prev_derive);
182         if (status != 0)
183                 failure++;
184
185         status = uc_meta_data_get_signed_int (vl, key_int_derive, &int_derive);
186         if (status != 0)
187                 failure++;
188
189         status = uc_meta_data_get_double (vl, key_int_fraction, &int_fraction);
190         if (status != 0)
191                 failure++;
192
193         if (failure == 0)
194         {
195                 int64_t difference;
196                 double rate;
197
198                 /* Calcualte the rate */
199                 difference = curr_derive - prev_derive;
200                 rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval);
201
202                 /* Modify the rate. */
203                 if (!isnan (data->factor))
204                         rate *= data->factor;
205                 if (!isnan (data->offset))
206                         rate += data->offset;
207
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;
216
217                 assert (int_fraction >= 0.0);
218                 assert (int_fraction <  1.0);
219
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         }
229
230         vl->values[dsrc_index].derive = (derive_t) int_derive;
231
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);
236
237         return (0);
238 } /* }}} int ts_invoke_derive */
239
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;
246
247         /* Required meta data */
248         double int_fraction;
249         char key_int_fraction[128];
250
251         curr_absolute = (uint64_t) vl->values[dsrc_index].absolute;
252
253         ssnprintf (key_int_fraction, sizeof (key_int_fraction),
254                         "target_scale[%p,%i]:int_fraction",
255                         (void *) data, dsrc_index);
256
257         int_fraction = 0.0;
258
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;
263
264         rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval);
265
266         /* Modify the rate. */
267         if (!isnan (data->factor))
268                 rate *= data->factor;
269         if (!isnan (data->offset))
270                 rate += data->offset;
271
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);
276
277         vl->values[dsrc_index].absolute = (absolute_t) curr_absolute;
278
279         /* Update to the new absolute value */
280         uc_meta_data_add_double (vl, key_int_fraction, int_fraction);
281
282         return (0);
283 } /* }}} int ts_invoke_absolute */
284
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         }
294
295         *ret = ci->values[0].value.number;
296         DEBUG ("ts_config_set_double: *ret = %g", *ret);
297
298         return (0);
299 } /* }}} int ts_config_set_double */
300
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;
307
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         }
315
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;
321
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         }
329
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;
340
341         /* Copy the strings, allocating memory as needed.  */
342         for (i = 0; i < ci->values_num; i++)
343         {
344                 size_t j;
345
346                 /* If we get here, there better be memory for us to write to.  */
347                 assert (data->data_sources_num < new_data_sources_num);
348
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         }
358
359         return (0);
360 } /* }}} int ts_config_add_data_source */
361
362 static int ts_destroy (void **user_data) /* {{{ */
363 {
364         ts_data_t *data;
365
366         if (user_data == NULL)
367                 return (-EINVAL);
368
369         data = (ts_data_t *) *user_data;
370
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         }
378
379         sfree (data);
380         *user_data = NULL;
381
382         return (0);
383 } /* }}} int ts_destroy */
384
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;
390
391         data = calloc (1, sizeof (*data));
392         if (data == NULL)
393         {
394                 ERROR ("ts_create: calloc failed.");
395                 return (-ENOMEM);
396         }
397
398         data->factor = NAN;
399         data->offset = NAN;
400
401         status = 0;
402         for (i = 0; i < ci->children_num; i++)
403         {
404                 oconfig_item_t *child = ci->children + i;
405
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                 }
418
419                 if (status != 0)
420                         break;
421         }
422
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                 }
432
433                 break;
434         }
435
436         if (status != 0)
437         {
438                 ts_destroy ((void *) &data);
439                 return (status);
440         }
441
442         *user_data = data;
443         return (0);
444 } /* }}} int ts_create */
445
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;
451
452         if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
453                 return (-EINVAL);
454
455         data = *user_data;
456         if (data == NULL)
457         {
458                 ERROR ("Target `scale': Invoke: `data' is NULL.");
459                 return (-EINVAL);
460         }
461
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;
470
471                         /* No match, ignore */
472                         if (j >= data->data_sources_num)
473                                 continue;
474                 }
475
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         }
488
489         return (FC_TARGET_CONTINUE);
490 } /* }}} int ts_invoke */
491
492 void module_register (void)
493 {
494         target_proc_t tproc = { 0 };
495
496         tproc.create  = ts_create;
497         tproc.destroy = ts_destroy;
498         tproc.invoke  = ts_invoke;
499         fc_register_target ("scale", tproc);
500 } /* module_register */
501
502 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */
503