Merge branch 'pr/1649'
[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
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         }
314
315         /* Check type of arguments */
316         for (int i = 0; i < ci->values_num; i++)
317         {
318                 if (ci->values[i].type == OCONFIG_TYPE_STRING)
319                         continue;
320
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         }
328
329         /* Allocate space for the char pointers */
330         new_data_sources_num = data->data_sources_num + ((size_t) ci->values_num);
331         temp = 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;
339
340         /* Copy the strings, allocating memory as needed.  */
341         for (int i = 0; i < ci->values_num; i++)
342         {
343                 size_t j;
344
345                 /* If we get here, there better be memory for us to write to.  */
346                 assert (data->data_sources_num < new_data_sources_num);
347
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         }
357
358         return (0);
359 } /* }}} int ts_config_add_data_source */
360
361 static int ts_destroy (void **user_data) /* {{{ */
362 {
363         ts_data_t *data;
364
365         if (user_data == NULL)
366                 return (-EINVAL);
367
368         data = (ts_data_t *) *user_data;
369
370         if ((data != NULL) && (data->data_sources != NULL))
371         {
372                 for (size_t i = 0; i < data->data_sources_num; i++)
373                         sfree (data->data_sources[i]);
374                 sfree (data->data_sources);
375         }
376
377         sfree (data);
378         *user_data = NULL;
379
380         return (0);
381 } /* }}} int ts_destroy */
382
383 static int ts_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
384 {
385         ts_data_t *data;
386         int status;
387
388         data = calloc (1, sizeof (*data));
389         if (data == NULL)
390         {
391                 ERROR ("ts_create: calloc failed.");
392                 return (-ENOMEM);
393         }
394
395         data->factor = NAN;
396         data->offset = NAN;
397
398         status = 0;
399         for (int i = 0; i < ci->children_num; i++)
400         {
401                 oconfig_item_t *child = ci->children + i;
402
403                 if (strcasecmp ("Factor", child->key) == 0)
404                                 status = ts_config_set_double (&data->factor, child);
405                 else if (strcasecmp ("Offset", child->key) == 0)
406                                 status = ts_config_set_double (&data->offset, child);
407                 else if (strcasecmp ("DataSource", child->key) == 0)
408                                 status = ts_config_add_data_source(data, child);
409                 else
410                 {
411                         ERROR ("Target `scale': The `%s' configuration option is not understood "
412                                         "and will be ignored.", child->key);
413                         status = 0;
414                 }
415
416                 if (status != 0)
417                         break;
418         }
419
420         /* Additional sanity-checking */
421         while (status == 0)
422         {
423                 if (isnan (data->factor) && isnan (data->offset))
424                 {
425                         ERROR ("Target `scale': You need to at least set either the `Factor' "
426                                         "or `Offset' option!");
427                         status = -1;
428                 }
429
430                 break;
431         }
432
433         if (status != 0)
434         {
435                 ts_destroy ((void *) &data);
436                 return (status);
437         }
438
439         *user_data = data;
440         return (0);
441 } /* }}} int ts_create */
442
443 static int ts_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
444                 notification_meta_t __attribute__((unused)) **meta, void **user_data)
445 {
446         ts_data_t *data;
447
448         if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
449                 return (-EINVAL);
450
451         data = *user_data;
452         if (data == NULL)
453         {
454                 ERROR ("Target `scale': Invoke: `data' is NULL.");
455                 return (-EINVAL);
456         }
457
458         for (size_t i = 0; i < ds->ds_num; i++)
459         {
460                 /* If we've got a list of data sources, is it in the list? */
461                 if (data->data_sources) {
462                         size_t j;
463                         for (j = 0; j < data->data_sources_num; j++)
464                                 if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0)
465                                         break;
466
467                         /* No match, ignore */
468                         if (j >= data->data_sources_num)
469                                 continue;
470                 }
471
472                 if (ds->ds[i].type == DS_TYPE_COUNTER)
473                         ts_invoke_counter (ds, vl, data, i);
474                 else if (ds->ds[i].type == DS_TYPE_GAUGE)
475                         ts_invoke_gauge (ds, vl, data, i);
476                 else if (ds->ds[i].type == DS_TYPE_DERIVE)
477                         ts_invoke_derive (ds, vl, data, i);
478                 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
479                         ts_invoke_absolute (ds, vl, data, i);
480                 else
481                         ERROR ("Target `scale': Ignoring unknown data source type %i",
482                                         ds->ds[i].type);
483         }
484
485         return (FC_TARGET_CONTINUE);
486 } /* }}} int ts_invoke */
487
488 void module_register (void)
489 {
490         target_proc_t tproc = { 0 };
491
492         tproc.create  = ts_create;
493         tproc.destroy = ts_destroy;
494         tproc.invoke  = ts_invoke;
495         fc_register_target ("scale", tproc);
496 } /* module_register */
497
498 /* vim: set sw=2 ts=2 tw=78 fdm=marker : */
499