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