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