static int lua_cb_dispatch_values(lua_State *L) /* {{{ */ { int nargs = lua_gettop(L); if (nargs != 1) return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs); luaL_checktype(L, 1, LUA_TTABLE); value_list_t *vl = luaC_tovaluelist(L, -1); if (vl == NULL) return luaL_error(L, "%s", "luaC_tovaluelist failed"); #if COLLECT_DEBUG char identifier[6 * DATA_MAX_NAME_LEN]; FORMAT_VL(identifier, sizeof(identifier), vl); DEBUG("Lua plugin: collectd.dispatch_values(): Received value list \"%s\", " "time %.3f, interval %.3f.", identifier, CDTIME_T_TO_DOUBLE(vl->time), CDTIME_T_TO_DOUBLE(vl->interval)); #endif plugin_dispatch_values(vl); sfree(vl->values); sfree(vl); return 0; } /* }}} lua_cb_dispatch_values */
int create_putval (char *ret, size_t ret_len, /* {{{ */ const data_set_t *ds, const value_list_t *vl) { char buffer_ident[6 * DATA_MAX_NAME_LEN]; char buffer_values[1024]; int status; status = FORMAT_VL (buffer_ident, sizeof (buffer_ident), vl); if (status != 0) return (status); escape_string (buffer_ident, sizeof (buffer_ident)); status = format_values (buffer_values, sizeof (buffer_values), ds, vl, /* store rates = */ 0); if (status != 0) return (status); escape_string (buffer_values, sizeof (buffer_values)); ssnprintf (ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident, (vl->interval > 0) ? CDTIME_T_TO_DOUBLE (vl->interval) : CDTIME_T_TO_DOUBLE (interval_g), buffer_values); return (0); } /* }}} int create_putval */
/* XXX: This function will acquire `cache_lock' but will not free it! */ static meta_data_t *uc_get_meta (const value_list_t *vl) /* {{{ */ { char name[6 * DATA_MAX_NAME_LEN]; cache_entry_t *ce = NULL; int status; status = FORMAT_VL (name, sizeof (name), vl); if (status != 0) { ERROR ("utils_cache: uc_get_meta: FORMAT_VL failed."); return (NULL); } pthread_mutex_lock (&cache_lock); status = c_avl_get (cache_tree, name, (void *) &ce); if (status != 0) { pthread_mutex_unlock (&cache_lock); return (NULL); } assert (ce != NULL); if (ce->meta == NULL) ce->meta = meta_data_create (); if (ce->meta == NULL) pthread_mutex_unlock (&cache_lock); return (ce->meta); } /* }}} meta_data_t *uc_get_meta */
gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl) { char name[6 * DATA_MAX_NAME_LEN]; gauge_t *ret = NULL; size_t ret_num = 0; int status; if (FORMAT_VL (name, sizeof (name), vl) != 0) { ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed."); return (NULL); } status = uc_get_rate_by_name (name, &ret, &ret_num); if (status != 0) return (NULL); /* This is important - the caller has no other way of knowing how many * values are returned. */ if (ret_num != (size_t) ds->ds_num) { ERROR ("utils_cache: uc_get_rate: ds[%s] has %i values, " "but uc_get_rate_by_name returned %zu.", ds->type, ds->ds_num, ret_num); sfree (ret); return (NULL); } return (ret); } /* gauge_t *uc_get_rate */
int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step) { char name[6 * DATA_MAX_NAME_LEN]; cache_entry_t *ce = NULL; int ret = -1; if (FORMAT_VL (name, sizeof (name), vl) != 0) { ERROR ("uc_get_state: FORMAT_VL failed."); return (STATE_ERROR); } pthread_mutex_lock (&cache_lock); if (c_avl_get (cache_tree, name, (void *) &ce) == 0) { assert (ce != NULL); ret = ce->hits; ce->hits = ret + step; } pthread_mutex_unlock (&cache_lock); return (ret); } /* int uc_inc_hits */
static int value_list_to_filename(char *buffer, size_t buffer_size, value_list_t const *vl) { char const suffix[] = ".rrd"; int status; size_t len; if (datadir != NULL) { size_t datadir_len = strlen(datadir) + 1; if (datadir_len >= buffer_size) return ENOMEM; sstrncpy(buffer, datadir, buffer_size); buffer[datadir_len - 1] = '/'; buffer[datadir_len] = 0; buffer += datadir_len; buffer_size -= datadir_len; } status = FORMAT_VL(buffer, buffer_size, vl); if (status != 0) return status; len = strlen(buffer); assert(len < buffer_size); buffer += len; buffer_size -= len; if (buffer_size <= sizeof(suffix)) return ENOMEM; memcpy(buffer, suffix, sizeof(suffix)); return 0; } /* int value_list_to_filename */
int uc_get_history (const data_set_t *ds, const value_list_t *vl, gauge_t *ret_history, size_t num_steps, size_t num_ds) { char name[6 * DATA_MAX_NAME_LEN]; if (FORMAT_VL (name, sizeof (name), vl) != 0) { ERROR ("utils_cache: uc_get_history: FORMAT_VL failed."); return (-1); } return (uc_get_history_by_name (name, ret_history, num_steps, num_ds)); } /* int uc_get_history */
/* Update the num, sum, min, max, ... fields of the aggregation instance, if * the rate of the value list is available. Value lists with more than one data * source are not supported and will return an error. Returns zero on success * and non-zero otherwise. */ static int agg_instance_update (agg_instance_t *inst, /* {{{ */ data_set_t const *ds, value_list_t const *vl) { gauge_t *rate; if (ds->ds_num != 1) { ERROR ("aggregation plugin: The \"%s\" type (data set) has more than one " "data source. This is currently not supported by this plugin. " "Sorry.", ds->type); return (EINVAL); } rate = uc_get_rate (ds, vl); if (rate == NULL) { char ident[6 * DATA_MAX_NAME_LEN]; FORMAT_VL (ident, sizeof (ident), vl); ERROR ("aggregation plugin: Unable to read the current rate of \"%s\".", ident); return (ENOENT); } if (isnan (rate[0])) { sfree (rate); return (0); } pthread_mutex_lock (&inst->lock); inst->num++; inst->sum += rate[0]; inst->squares_sum += (rate[0] * rate[0]); if (isnan (inst->min) || (inst->min > rate[0])) inst->min = rate[0]; if (isnan (inst->max) || (inst->max < rate[0])) inst->max = rate[0]; pthread_mutex_unlock (&inst->lock); sfree (rate); return (0); } /* }}} int agg_instance_update */
/* * Functions */ static int wr_write (const data_set_t *ds, /* {{{ */ const value_list_t *vl, user_data_t *ud) { wr_node_t *node = ud->data; char ident[512]; char key[512]; char value[512]; char time[24]; size_t value_size; char *value_ptr; int status; redisReply *rr; status = FORMAT_VL (ident, sizeof (ident), vl); if (status != 0) return (status); ssnprintf (key, sizeof (key), "%s%s", (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX, ident); ssnprintf (time, sizeof (time), "%.9f", CDTIME_T_TO_DOUBLE(vl->time)); memset (value, 0, sizeof (value)); value_size = sizeof (value); value_ptr = &value[0]; status = format_values (value_ptr, value_size, ds, vl, node->store_rates); if (status != 0) return (status); pthread_mutex_lock (&node->lock); if (node->conn == NULL) { node->conn = redisConnectWithTimeout ((char *)node->host, node->port, node->timeout); if (node->conn == NULL) { ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: Unkown reason", (node->host != NULL) ? node->host : "localhost", (node->port != 0) ? node->port : 6379); pthread_mutex_unlock (&node->lock); return (-1); } else if (node->conn->err) { ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: %s", (node->host != NULL) ? node->host : "localhost", (node->port != 0) ? node->port : 6379, node->conn->errstr); pthread_mutex_unlock (&node->lock); return (-1); } rr = redisCommand(node->conn, "SELECT %d", node->database); if (rr == NULL) WARNING("SELECT command error. database:%d message:%s", node->database, node->conn->errstr); else freeReplyObject (rr); } rr = redisCommand (node->conn, "ZADD %s %s %s", key, time, value); if (rr == NULL) WARNING("ZADD command error. key:%s message:%s", key, node->conn->errstr); else freeReplyObject (rr); if (node->max_set_size >= 0) { rr = redisCommand (node->conn, "ZREMRANGEBYRANK %s %d %d", key, 0, (-1 * node->max_set_size) - 1); if (rr == NULL) WARNING("ZREMRANGEBYRANK command error. key:%s message:%s", key, node->conn->errstr); else freeReplyObject (rr); } /* TODO(octo): This is more overhead than necessary. Use the cache and * metadata to determine if it is a new metric and call SADD only once for * each metric. */ rr = redisCommand (node->conn, "SADD %svalues %s", (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX, ident); if (rr==NULL) WARNING("SADD command error. ident:%s message:%s", ident, node->conn->errstr); else freeReplyObject (rr); pthread_mutex_unlock (&node->lock); return (0); } /* }}} int wr_write */
int uc_update (const data_set_t *ds, const value_list_t *vl) { char name[6 * DATA_MAX_NAME_LEN]; cache_entry_t *ce = NULL; int send_okay_notification = 0; time_t update_delay = 0; notification_t n; int status; int i; if (FORMAT_VL (name, sizeof (name), vl) != 0) { ERROR ("uc_update: FORMAT_VL failed."); return (-1); } pthread_mutex_lock (&cache_lock); status = c_avl_get (cache_tree, name, (void *) &ce); if (status != 0) /* entry does not yet exist */ { status = uc_insert (ds, vl, name); pthread_mutex_unlock (&cache_lock); return (status); } assert (ce != NULL); assert (ce->values_num == ds->ds_num); if (ce->last_time >= vl->time) { pthread_mutex_unlock (&cache_lock); NOTICE ("uc_update: Value too old: name = %s; value time = %u; " "last cache update = %u;", name, (unsigned int) vl->time, (unsigned int) ce->last_time); return (-1); } /* Send a notification (after the lock has been released) if we switch the * state from something else to `okay'. */ if (ce->state == STATE_MISSING) { send_okay_notification = 1; ce->state = STATE_OKAY; update_delay = time (NULL) - ce->last_update; } for (i = 0; i < ds->ds_num; i++) { switch (ds->ds[i].type) { case DS_TYPE_COUNTER: { counter_t diff; /* check if the counter has wrapped around */ if (vl->values[i].counter < ce->values_raw[i].counter) { if (ce->values_raw[i].counter <= 4294967295U) diff = (4294967295U - ce->values_raw[i].counter) + vl->values[i].counter; else diff = (18446744073709551615ULL - ce->values_raw[i].counter) + vl->values[i].counter; } else /* counter has NOT wrapped around */ { diff = vl->values[i].counter - ce->values_raw[i].counter; } ce->values_gauge[i] = ((double) diff) / ((double) (vl->time - ce->last_time)); ce->values_raw[i].counter = vl->values[i].counter; } break; case DS_TYPE_GAUGE: ce->values_raw[i].gauge = vl->values[i].gauge; ce->values_gauge[i] = vl->values[i].gauge; break; case DS_TYPE_DERIVE: { derive_t diff; diff = vl->values[i].derive - ce->values_raw[i].derive; ce->values_gauge[i] = ((double) diff) / ((double) (vl->time - ce->last_time)); ce->values_raw[i].derive = vl->values[i].derive; } break; case DS_TYPE_ABSOLUTE: ce->values_gauge[i] = ((double) vl->values[i].absolute) / ((double) (vl->time - ce->last_time)); ce->values_raw[i].absolute = vl->values[i].absolute; break; default: /* This shouldn't happen. */ pthread_mutex_unlock (&cache_lock); ERROR ("uc_update: Don't know how to handle data source type %i.", ds->ds[i].type); return (-1); } /* switch (ds->ds[i].type) */ DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]); } /* for (i) */ /* Update the history if it exists. */ if (ce->history != NULL) { assert (ce->history_index < ce->history_length); for (i = 0; i < ce->values_num; i++) { size_t hist_idx = (ce->values_num * ce->history_index) + i; ce->history[hist_idx] = ce->values_gauge[i]; } assert (ce->history_length > 0); ce->history_index = (ce->history_index + 1) % ce->history_length; } /* Prune invalid gauge data */ uc_check_range (ds, ce); ce->last_time = vl->time; ce->last_update = time (NULL); ce->interval = vl->interval; pthread_mutex_unlock (&cache_lock); if (send_okay_notification == 0) return (0); /* Do not send okay notifications for uninteresting values, i. e. values for * which no threshold is configured. */ status = ut_check_interesting (name); if (status <= 0) return (0); /* Initialize the notification */ memset (&n, '\0', sizeof (n)); NOTIFICATION_INIT_VL (&n, vl, ds); n.severity = NOTIF_OKAY; n.time = vl->time; ssnprintf (n.message, sizeof (n.message), "Received a value for %s. It was missing for %u seconds.", name, (unsigned int) update_delay); plugin_dispatch_notification (&n); return (0); } /* int uc_update */
int uc_update (const data_set_t *ds, const value_list_t *vl) { char name[6 * DATA_MAX_NAME_LEN]; cache_entry_t *ce = NULL; int status; size_t i; if (FORMAT_VL (name, sizeof (name), vl) != 0) { ERROR ("uc_update: FORMAT_VL failed."); return (-1); } pthread_mutex_lock (&cache_lock); status = c_avl_get (cache_tree, name, (void *) &ce); if (status != 0) /* entry does not yet exist */ { status = uc_insert (ds, vl, name); pthread_mutex_unlock (&cache_lock); return (status); } assert (ce != NULL); assert (ce->values_num == ds->ds_num); if (ce->last_time >= vl->time) { pthread_mutex_unlock (&cache_lock); NOTICE ("uc_update: Value too old: name = %s; value time = %.3f; " "last cache update = %.3f;", name, CDTIME_T_TO_DOUBLE (vl->time), CDTIME_T_TO_DOUBLE (ce->last_time)); return (-1); } for (i = 0; i < ds->ds_num; i++) { switch (ds->ds[i].type) { case DS_TYPE_COUNTER: { counter_t diff = counter_diff (ce->values_raw[i].counter, vl->values[i].counter); ce->values_gauge[i] = ((double) diff) / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].counter = vl->values[i].counter; } break; case DS_TYPE_GAUGE: ce->values_raw[i].gauge = vl->values[i].gauge; ce->values_gauge[i] = vl->values[i].gauge; break; case DS_TYPE_DERIVE: { derive_t diff = vl->values[i].derive - ce->values_raw[i].derive; ce->values_gauge[i] = ((double) diff) / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].derive = vl->values[i].derive; } break; case DS_TYPE_ABSOLUTE: ce->values_gauge[i] = ((double) vl->values[i].absolute) / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].absolute = vl->values[i].absolute; break; default: /* This shouldn't happen. */ pthread_mutex_unlock (&cache_lock); ERROR ("uc_update: Don't know how to handle data source type %i.", ds->ds[i].type); return (-1); } /* switch (ds->ds[i].type) */ DEBUG ("uc_update: %s: ds[%zu] = %lf", name, i, ce->values_gauge[i]); } /* for (i) */ /* Update the history if it exists. */ if (ce->history != NULL) { assert (ce->history_index < ce->history_length); for (i = 0; i < ce->values_num; i++) { size_t hist_idx = (ce->values_num * ce->history_index) + i; ce->history[hist_idx] = ce->values_gauge[i]; } assert (ce->history_length > 0); ce->history_index = (ce->history_index + 1) % ce->history_length; } /* Prune invalid gauge data */ uc_check_range (ds, ce); ce->last_time = vl->time; ce->last_update = cdtime (); ce->interval = vl->interval; pthread_mutex_unlock (&cache_lock); return (0); } /* int uc_update */
static int network_dispatch_values (value_list_t *vl, /* {{{ */ const char *username) { int status; // DEBUG("host: %s", vl->host); // DEBUG("plugin: %s", vl->plugin); // DEBUG("plugin_instance: %s", vl->plugin_instance); // DEBUG("type: %s", vl->type); // DEBUG("type_instance: %s", vl->type_instance); if ((vl->time <= 0) || (strlen (vl->host) <= 0) || (strlen (vl->plugin) <= 0) || (strlen (vl->type) <= 0)) return (-EINVAL); if (!check_receive_okay (vl)) { #if COLLECT_DEBUG char name[6*DATA_MAX_NAME_LEN]; FORMAT_VL (name, sizeof (name), vl); name[sizeof (name) - 1] = 0; DEBUG ("network plugin: network_dispatch_values: " "NOT dispatching %s.", name); #endif stats_values_not_dispatched++; return (0); } assert (vl->meta == NULL); vl->meta = meta_data_create (); if (vl->meta == NULL) { ERROR ("network plugin: meta_data_create failed."); return (-ENOMEM); } status = meta_data_add_boolean (vl->meta, "zeromq:received", 1); if (status != 0) { ERROR ("network plugin: meta_data_add_boolean failed."); meta_data_destroy (vl->meta); vl->meta = NULL; return (status); } if (username != NULL) { status = meta_data_add_string (vl->meta, "zeromq:username", username); if (status != 0) { ERROR ("network plugin: meta_data_add_string failed."); meta_data_destroy (vl->meta); vl->meta = NULL; return (status); } } // DEBUG("dispatching %d values", vl->values_len); plugin_dispatch_values (vl); stats_values_dispatched++; meta_data_destroy (vl->meta); vl->meta = NULL; return (0); } /* }}} int network_dispatch_values */