/** * collectd - src/target_scale.c * Copyright (C) 2008-2009 Florian Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Florian Forster **/ #include "collectd.h" #include "filter_chain.h" #include "utils/common/common.h" #include "utils_cache.h" struct ts_data_s { double factor; double offset; char **data_sources; size_t data_sources_num; }; typedef struct ts_data_s ts_data_t; static int ts_invoke_counter(const data_set_t *ds, value_list_t *vl, /* {{{ */ ts_data_t *data, int dsrc_index) { uint64_t curr_counter; int status; int failure; /* Required meta data */ uint64_t prev_counter; char key_prev_counter[128]; uint64_t int_counter; char key_int_counter[128]; double int_fraction; char key_int_fraction[128]; curr_counter = (uint64_t)vl->values[dsrc_index].counter; snprintf(key_prev_counter, sizeof(key_prev_counter), "target_scale[%p,%i]:prev_counter", (void *)data, dsrc_index); snprintf(key_int_counter, sizeof(key_int_counter), "target_scale[%p,%i]:int_counter", (void *)data, dsrc_index); snprintf(key_int_fraction, sizeof(key_int_fraction), "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index); prev_counter = curr_counter; int_counter = 0; int_fraction = 0.0; /* Query the meta data */ failure = 0; status = uc_meta_data_get_unsigned_int(vl, key_prev_counter, &prev_counter); if (status != 0) failure++; status = uc_meta_data_get_unsigned_int(vl, key_int_counter, &int_counter); if (status != 0) failure++; status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction); if (status != 0) failure++; if (failure == 0) { uint64_t diff; double rate; diff = (uint64_t)counter_diff(prev_counter, curr_counter); rate = ((double)diff) / CDTIME_T_TO_DOUBLE(vl->interval); /* Modify the rate. */ if (!isnan(data->factor)) rate *= data->factor; if (!isnan(data->offset)) rate += data->offset; /* Calculate the internal counter. */ int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval)); diff = (uint64_t)int_fraction; int_fraction -= ((double)diff); int_counter += diff; assert(int_fraction >= 0.0); assert(int_fraction < 1.0); DEBUG("Target `scale': ts_invoke_counter: %" PRIu64 " -> %g -> %" PRIu64 "(+%g)", curr_counter, rate, int_counter, int_fraction); } else /* (failure != 0) */ { int_counter = 0; int_fraction = 0.0; } vl->values[dsrc_index].counter = (counter_t)int_counter; /* Update to the new counter value */ uc_meta_data_add_unsigned_int(vl, key_prev_counter, curr_counter); uc_meta_data_add_unsigned_int(vl, key_int_counter, int_counter); uc_meta_data_add_double(vl, key_int_fraction, int_fraction); return 0; } /* }}} int ts_invoke_counter */ static int ts_invoke_gauge(const data_set_t *ds, value_list_t *vl, /* {{{ */ ts_data_t *data, int dsrc_index) { if (!isnan(data->factor)) vl->values[dsrc_index].gauge *= data->factor; if (!isnan(data->offset)) vl->values[dsrc_index].gauge += data->offset; return 0; } /* }}} int ts_invoke_gauge */ static int ts_invoke_derive(const data_set_t *ds, value_list_t *vl, /* {{{ */ ts_data_t *data, int dsrc_index) { int64_t curr_derive; int status; int failure; /* Required meta data */ int64_t prev_derive; char key_prev_derive[128]; int64_t int_derive; char key_int_derive[128]; double int_fraction; char key_int_fraction[128]; curr_derive = (int64_t)vl->values[dsrc_index].derive; snprintf(key_prev_derive, sizeof(key_prev_derive), "target_scale[%p,%i]:prev_derive", (void *)data, dsrc_index); snprintf(key_int_derive, sizeof(key_int_derive), "target_scale[%p,%i]:int_derive", (void *)data, dsrc_index); snprintf(key_int_fraction, sizeof(key_int_fraction), "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index); prev_derive = curr_derive; int_derive = 0; int_fraction = 0.0; /* Query the meta data */ failure = 0; status = uc_meta_data_get_signed_int(vl, key_prev_derive, &prev_derive); if (status != 0) failure++; status = uc_meta_data_get_signed_int(vl, key_int_derive, &int_derive); if (status != 0) failure++; status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction); if (status != 0) failure++; if (failure == 0) { int64_t difference; double rate; /* Calcualte the rate */ difference = curr_derive - prev_derive; rate = ((double)difference) / CDTIME_T_TO_DOUBLE(vl->interval); /* Modify the rate. */ if (!isnan(data->factor)) rate *= data->factor; if (!isnan(data->offset)) rate += data->offset; /* Calculate the internal derive. */ int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval)); if (int_fraction < 0.0) /* handle negative integer rounding correctly */ difference = ((int64_t)int_fraction) - 1; else difference = (int64_t)int_fraction; int_fraction -= ((double)difference); int_derive += difference; assert(int_fraction >= 0.0); assert(int_fraction < 1.0); DEBUG("Target `scale': ts_invoke_derive: %" PRIu64 " -> %g -> %" PRIu64 "(+%g)", curr_derive, rate, int_derive, int_fraction); } else /* (failure != 0) */ { int_derive = 0; int_fraction = 0.0; } vl->values[dsrc_index].derive = (derive_t)int_derive; /* Update to the new derive value */ uc_meta_data_add_signed_int(vl, key_prev_derive, curr_derive); uc_meta_data_add_signed_int(vl, key_int_derive, int_derive); uc_meta_data_add_double(vl, key_int_fraction, int_fraction); return 0; } /* }}} int ts_invoke_derive */ static int ts_invoke_absolute(const data_set_t *ds, value_list_t *vl, /* {{{ */ ts_data_t *data, int dsrc_index) { uint64_t curr_absolute; double rate; int status; /* Required meta data */ double int_fraction; char key_int_fraction[128]; curr_absolute = (uint64_t)vl->values[dsrc_index].absolute; snprintf(key_int_fraction, sizeof(key_int_fraction), "target_scale[%p,%i]:int_fraction", (void *)data, dsrc_index); int_fraction = 0.0; /* Query the meta data */ status = uc_meta_data_get_double(vl, key_int_fraction, &int_fraction); if (status != 0) int_fraction = 0.0; rate = ((double)curr_absolute) / CDTIME_T_TO_DOUBLE(vl->interval); /* Modify the rate. */ if (!isnan(data->factor)) rate *= data->factor; if (!isnan(data->offset)) rate += data->offset; /* Calculate the new absolute. */ int_fraction += (rate * CDTIME_T_TO_DOUBLE(vl->interval)); curr_absolute = (uint64_t)int_fraction; int_fraction -= ((double)curr_absolute); vl->values[dsrc_index].absolute = (absolute_t)curr_absolute; /* Update to the new absolute value */ uc_meta_data_add_double(vl, key_int_fraction, int_fraction); return 0; } /* }}} int ts_invoke_absolute */ static int ts_config_set_double(double *ret, oconfig_item_t *ci) /* {{{ */ { if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { WARNING("scale target: The `%s' config option needs " "exactly one numeric argument.", ci->key); return -1; } *ret = ci->values[0].value.number; DEBUG("ts_config_set_double: *ret = %g", *ret); return 0; } /* }}} int ts_config_set_double */ static int ts_config_add_data_source(ts_data_t *data, /* {{{ */ oconfig_item_t *ci) { size_t new_data_sources_num; char **temp; /* Check number of arbuments. */ if (ci->values_num < 1) { ERROR("`value' match: `%s' needs at least one argument.", ci->key); return -1; } /* Check type of arguments */ for (int i = 0; i < ci->values_num; i++) { if (ci->values[i].type == OCONFIG_TYPE_STRING) continue; ERROR("`value' match: `%s' accepts only string arguments " "(argument %i is a %s).", ci->key, i + 1, (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) ? "truth value" : "number"); return -1; } /* Allocate space for the char pointers */ new_data_sources_num = data->data_sources_num + ((size_t)ci->values_num); temp = realloc(data->data_sources, new_data_sources_num * sizeof(char *)); if (temp == NULL) { ERROR("`value' match: realloc failed."); return -1; } data->data_sources = temp; /* Copy the strings, allocating memory as needed. */ for (int i = 0; i < ci->values_num; i++) { size_t j; /* If we get here, there better be memory for us to write to. */ assert(data->data_sources_num < new_data_sources_num); j = data->data_sources_num; data->data_sources[j] = sstrdup(ci->values[i].value.string); if (data->data_sources[j] == NULL) { ERROR("`value' match: sstrdup failed."); continue; } data->data_sources_num++; } return 0; } /* }}} int ts_config_add_data_source */ static int ts_destroy(void **user_data) /* {{{ */ { ts_data_t *data; if (user_data == NULL) return -EINVAL; data = (ts_data_t *)*user_data; if ((data != NULL) && (data->data_sources != NULL)) { for (size_t i = 0; i < data->data_sources_num; i++) sfree(data->data_sources[i]); sfree(data->data_sources); } sfree(data); *user_data = NULL; return 0; } /* }}} int ts_destroy */ static int ts_create(const oconfig_item_t *ci, void **user_data) /* {{{ */ { ts_data_t *data; int status; data = calloc(1, sizeof(*data)); if (data == NULL) { ERROR("ts_create: calloc failed."); return -ENOMEM; } data->factor = NAN; data->offset = NAN; status = 0; for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; if (strcasecmp("Factor", child->key) == 0) status = ts_config_set_double(&data->factor, child); else if (strcasecmp("Offset", child->key) == 0) status = ts_config_set_double(&data->offset, child); else if (strcasecmp("DataSource", child->key) == 0) status = ts_config_add_data_source(data, child); else { ERROR("Target `scale': The `%s' configuration option is not understood " "and will be ignored.", child->key); status = 0; } if (status != 0) break; } /* Additional sanity-checking */ while (status == 0) { if (isnan(data->factor) && isnan(data->offset)) { ERROR("Target `scale': You need to at least set either the `Factor' " "or `Offset' option!"); status = -1; } break; } if (status != 0) { ts_destroy((void *)&data); return status; } *user_data = data; return 0; } /* }}} int ts_create */ static int ts_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */ notification_meta_t __attribute__((unused)) * *meta, void **user_data) { ts_data_t *data; if ((ds == NULL) || (vl == NULL) || (user_data == NULL)) return -EINVAL; data = *user_data; if (data == NULL) { ERROR("Target `scale': Invoke: `data' is NULL."); return -EINVAL; } for (size_t i = 0; i < ds->ds_num; i++) { /* If we've got a list of data sources, is it in the list? */ if (data->data_sources) { size_t j; for (j = 0; j < data->data_sources_num; j++) if (strcasecmp(ds->ds[i].name, data->data_sources[j]) == 0) break; /* No match, ignore */ if (j >= data->data_sources_num) continue; } if (ds->ds[i].type == DS_TYPE_COUNTER) ts_invoke_counter(ds, vl, data, i); else if (ds->ds[i].type == DS_TYPE_GAUGE) ts_invoke_gauge(ds, vl, data, i); else if (ds->ds[i].type == DS_TYPE_DERIVE) ts_invoke_derive(ds, vl, data, i); else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) ts_invoke_absolute(ds, vl, data, i); else ERROR("Target `scale': Ignoring unknown data source type %i", ds->ds[i].type); } return FC_TARGET_CONTINUE; } /* }}} int ts_invoke */ void module_register(void) { target_proc_t tproc = {0}; tproc.create = ts_create; tproc.destroy = ts_destroy; tproc.invoke = ts_invoke; fc_register_target("scale", tproc); } /* module_register */