From 8621957617d92057ceae6e4f1528bc4b0886700c Mon Sep 17 00:00:00 2001 From: Ryan Cox Date: Wed, 3 Nov 2010 14:06:17 +0100 Subject: [PATCH 1/1] mongo plugin: Initial checkin. --- configure.in | 2 + src/mongo.c | 473 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mongo.h | 0 3 files changed, 475 insertions(+) create mode 100644 src/mongo.c create mode 100644 src/mongo.h diff --git a/configure.in b/configure.in index e8a6b12b..156b3769 100644 --- a/configure.in +++ b/configure.in @@ -4685,6 +4685,7 @@ AC_PLUGIN([mbmon], [yes], [Query mbmond]) AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) +AC_PLUGIN([mongo], [$with_libmongoc], [MongoDB statistics]) AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin]) AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values]) AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics]) @@ -5009,6 +5010,7 @@ Configuration: memcached . . . . . . $enable_memcached memory . . . . . . . $enable_memory modbus . . . . . . . $enable_modbus + mongo . . . . . . . $enable_mongo multimeter . . . . . $enable_multimeter mysql . . . . . . . . $enable_mysql netapp . . . . . . . $enable_netapp diff --git a/src/mongo.c b/src/mongo.c new file mode 100644 index 00000000..7d720635 --- /dev/null +++ b/src/mongo.c @@ -0,0 +1,473 @@ +/** + * collectd - src/mongo.c + * Copyright (C) 2010 Ryan Cox + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Ryan Cox + **/ + +#include +#include +#include +#include +#include +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#define MC_PLUGIN_NAME "mongo" +#define MC_MONGO_DEF_HOST "127.0.0.1" +#define MC_MONGO_DEF_PORT 27017 +#define MC_MONGO_DEF_DB "admin" +#define MC_MIN_PORT 1 +#define MC_MAX_PORT 65535 +#define SUCCESS 0 +#define FAILURE -1 + +static char *mc_user = NULL; +static char *mc_password = NULL; +static char *mc_db = NULL; +static char *mc_host = NULL; +static int mc_port = MC_MONGO_DEF_PORT; + +static mongo_connection conn[1]; + +static const char *config_keys[] = { + "User", + "Password", + "Database", + "Host", + "Port" +}; + +static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); + +static void submit(const char *type, const char *instance, value_t *values, size_t values_len) { + INFO("Submiting [%d] for type [%s]",values_len,type); + value_list_t v = VALUE_LIST_INIT; + + v.values = values; + v.values_len = values_len; + + if( instance != NULL ) { + sstrncpy(v.type_instance,instance,sizeof(v.type_instance)); + } + + sstrncpy(v.host, hostname_g, sizeof(v.host)); + sstrncpy(v.plugin, MC_PLUGIN_NAME, sizeof(v.plugin)); + char port[12]; + sprintf(port, "%d",mc_port); + sstrncpy(v.plugin_instance, port, sizeof(v.plugin_instance)); + sstrncpy(v.type, type, sizeof(v.type)); + + plugin_dispatch_values(&v); +} + +static void submit_counter(const char *type, const char *instance, int64_t counter ) { + value_t v[1]; + v[0].counter = counter; + submit(type, instance, v, STATIC_ARRAY_SIZE (v)); +} + +static void submit_gauge(const char *type, const char *instance, gauge_t gauge ) { + value_t v[1]; + v[0].gauge = gauge; + submit(type, instance, v, STATIC_ARRAY_SIZE (v)); +} + + +static void handle_opcounters(bson* obj) { + INFO("handle op counters"); + bson_iterator it; + if(bson_find(&it, obj, "opcounters")) { + bson subobj; + bson_iterator_subobject(&it, &subobj); + bson_iterator it2; + bson_iterator_init(&it2, subobj.data); + + int64_t insert = 0; + int64_t query = 0; + int64_t delete = 0; + int64_t getmore = 0; + int64_t command = 0; + + while(bson_iterator_next(&it2)){ + if(strcmp(bson_iterator_key(&it2),"insert") == 0) { + insert = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"query") == 0) { + query = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"delete") == 0) { + delete = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"getmore") == 0) { + getmore = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"command") == 0) { + command = bson_iterator_long(&it2); + } + } + + bson_destroy(&subobj); + + submit_gauge("mongo_counter", "indert", insert ); + submit_gauge("mongo_counter", "query", query ); + submit_gauge("mongo_counter", "delete", delete ); + submit_gauge("mongo_counter", "getmore", getmore ); + submit_gauge("mongo_counter", "command", command ); + submit_gauge("mongo_counter", "insert", insert ); + } +} + +static void handle_mem(bson* obj) { + INFO("handle mem"); + bson_iterator it; + if(bson_find(&it, obj, "mem")) { + bson subobj; + bson_iterator_subobject(&it, &subobj); + bson_iterator it2; + bson_iterator_init(&it2, subobj.data); + int64_t resident = 0; + int64_t virtual = 0; + int64_t mapped = 0; + while(bson_iterator_next(&it2)) { + if(strcmp(bson_iterator_key(&it2),"resident") == 0) { + resident = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"virtual") == 0) { + virtual = bson_iterator_long(&it2); + } + if(strcmp(bson_iterator_key(&it2),"mapped") == 0) { + mapped = bson_iterator_long(&it2); + } + } + value_t values[3]; + values[0].gauge = resident; + values[1].gauge = virtual; + values[2].gauge = mapped; + submit_gauge("memory", "resident", resident ); + submit_gauge("memory", "virtual", virtual ); + submit_gauge("memory", "mapped", mapped ); + bson_destroy(&subobj); + } +} + +static void handle_connections(bson* obj) { + INFO("handle connections"); + bson_iterator it; + if(bson_find(&it, obj, "connections")) { + bson subobj; + bson_iterator_subobject(&it, &subobj); + bson_iterator it2; + bson_iterator_init(&it2, subobj.data); + while(bson_iterator_next(&it2)) { + if(strcmp(bson_iterator_key(&it2),"current") == 0) { + submit_gauge("connections", "connections", bson_iterator_int(&it2)); + break; + } + } + bson_destroy(&subobj); + } +} + +static void handle_lock(bson* obj) { + INFO("handle lock"); + bson_iterator it; + if(bson_find(&it, obj, "globalLock")) { + bson subobj; + bson_iterator_subobject(&it, &subobj); + bson_iterator it2; + bson_iterator_init(&it2, subobj.data); + while(bson_iterator_next(&it2)) { + if(strcmp(bson_iterator_key(&it2),"ratio") == 0) { + submit_gauge("percent", "lock_ratio", bson_iterator_double(&it2)); + } + } + bson_destroy(&subobj); + } +} + +static void handle_index_counters(bson* obj) { + INFO("handle index counters"); + bson_iterator icit; + if(bson_find(&icit, obj, "indexCounters")) { + bson oic; + bson_iterator_subobject(&icit, &oic); + bson_iterator bit; + if(bson_find(&icit, &oic, "btree")) { + bson obt; + bson_iterator_subobject(&icit, &obt); + bson_iterator bit; + bson_iterator_init(&bit, oic.data); + int accesses; + int misses; + while(bson_iterator_next(&bit)) { + if(strcmp(bson_iterator_key(&bit),"accesses") == 0) { + accesses = bson_iterator_int(&bit); + } + if(strcmp(bson_iterator_key(&bit),"misses") == 0) { + misses = bson_iterator_int(&bit); + } + } + + double ratio = 0; + if( misses > 0 ) { + double ratio = accesses/misses; + } + submit_gauge("cache_ratio", "cache_misses", ratio ); + bson_destroy(&obt); + } + bson_destroy(&oic); + } +} + +static void handle_stats_counts(bson* obj) { + + INFO("handle stats counts"); + bson_iterator it; + bson_iterator_init(&it, obj->data); + int64_t collections = 0; + int64_t objects = 0; + int64_t numExtents = 0; + int64_t indexes = 0; + + while(bson_iterator_next(&it)) { + if(strcmp(bson_iterator_key(&it),"collections") == 0) { + collections = bson_iterator_long(&it); + } + if(strcmp(bson_iterator_key(&it),"objects") == 0) { + objects = bson_iterator_long(&it); + } + if(strcmp(bson_iterator_key(&it),"numExtents") == 0) { + numExtents = bson_iterator_long(&it); + } + if(strcmp(bson_iterator_key(&it),"indexes") == 0) { + indexes = bson_iterator_long(&it); + } + } + + submit_gauge("counter","object_count",objects); + + submit_gauge("counter", "collections",collections); + submit_gauge("counter", "num_extents",numExtents); + submit_gauge("counter", "indexes",indexes); +} + +static void handle_stats_sizes(bson* obj) { + bson_iterator it; + bson_iterator_init(&it, obj->data); + int64_t storageSize = 0; + int64_t dataSize = 0; + int64_t indexSize = 0; + while(bson_iterator_next(&it)) { + if(strcmp(bson_iterator_key(&it),"storageSize") == 0) { + storageSize = bson_iterator_long(&it); + } + if(strcmp(bson_iterator_key(&it),"dataSize") == 0) { + dataSize = bson_iterator_long(&it); + } + if(strcmp(bson_iterator_key(&it),"indexSize") == 0) { + indexSize = bson_iterator_long(&it); + } + } + + value_t values[3]; + submit_gauge("file_size", "storage",storageSize ); + submit_gauge("file_size", "index",indexSize ); + submit_gauge("file_size", "data",dataSize ); +} + +static int do_stats(void) { + bson obj; + + /* TODO: + + change this to raw runCommand + db.runCommand( { dbstats : 1 } ); + succeeds but is getting back all zeros !?! + modify bson_print to print type 18 + repro problem w/o db name - show dbs doesn't work again + why does db.admin.dbstats() work fine in shell? + implement retries ? noticed that if db is unavailable, collectd dies + */ + + if( !mongo_simple_int_command(conn, mc_db, "dbstats", 1, &obj) ) { + ERROR("Mongo: failed to call stats Host [%s] Port [%d] User [%s]", + mc_host, mc_port, mc_user); + return FAILURE; + } + + bson_print(&obj); + + handle_stats_sizes(&obj); + handle_stats_counts(&obj); + + + bson_destroy(&obj); + + return SUCCESS; +} + + +static int do_server_status(void) { + bson obj; + + if( !mongo_simple_int_command(conn, mc_db, "serverStatus", 1, &obj) ) { + ERROR("Mongo: failed to call serverStatus Host [%s] Port [%d] User [%s]", + mc_host, mc_port, mc_user); + return FAILURE; + } + + bson_print(&obj); + + handle_opcounters(&obj); + handle_mem(&obj); + handle_connections(&obj); + handle_index_counters(&obj); + handle_lock(&obj); + + bson_destroy(&obj); + return SUCCESS; +} + +static int mc_read(void) { + DEBUG("Mongo: mongo driver read"); + + if(do_server_status() != SUCCESS) { + ERROR("Mongo: do server status failed"); + return FAILURE; + } + + if(do_stats() != SUCCESS) { + ERROR("Mongo: do stats status failed"); + return FAILURE; + } + + return SUCCESS; +} + + +static void config_set(char** dest, const char* src ) { + if( *dest ) { + sfree(*dest); + } + *dest= malloc(strlen(src)+1); + sstrncpy(*dest,src,strlen(src)+1); +} + +static int mc_config(const char *key, const char *value) { + + DEBUG("Mongo: config key [%s] value [%s]", key, value); + if(strcasecmp("Host", key) == 0) { + config_set(&mc_host,value); + return SUCCESS; + } + + if(strcasecmp("Port", key) == 0) { + long l = strtol(value,NULL,10); + + if((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN)) + || (errno != 0 && l == 0)) { + ERROR("Mongo: failed to parse Port value [%s]", value); + return FAILURE; + } + + if( l < MC_MIN_PORT || l > MC_MAX_PORT ) { + ERROR("Mongo: failed Port value [%s] outside range [1..65535]", value); + return FAILURE; + } + + mc_port = l; + return SUCCESS; + } + + if(strcasecmp("User", key) == 0) { + config_set(&mc_user,value); + return SUCCESS; + } + + if(strcasecmp("Password", key) == 0) { + config_set(&mc_password,value); + return SUCCESS; + } + + if(strcasecmp("Database", key) == 0) { + config_set(&mc_db,value); + return SUCCESS; + } + + return SUCCESS; +} + +static int mc_init(void) { + + DEBUG("mongo driver initializing"); + if( !mc_host) { + DEBUG("Mongo: Host not specified. Using default [%s]",MC_MONGO_DEF_HOST); + config_set(&mc_host, MC_MONGO_DEF_HOST); + } + + if( !mc_db) { + DEBUG("Mongo: Database not specified. Using default [%s]",MC_MONGO_DEF_DB); + config_set(&mc_db, MC_MONGO_DEF_DB); + } + + mongo_connection_options opts; + sstrncpy(opts.host, mc_host, sizeof(opts.host)); + opts.port = mc_port; + + if(mongo_connect( conn , &opts )){ + ERROR("Mongo: driver failed to connect. Host [%s] Port [%d] User [%s]", + mc_host, mc_port, mc_user); + return FAILURE; + } + + return SUCCESS; +} + +static int mc_shutdown(void) { + DEBUG("Mongo: driver shutting down"); + + if( conn ) { + mongo_disconnect(conn); + mongo_destroy(conn); + } + if( mc_user ) { + sfree(mc_user); + } + + if( mc_password ) { + sfree(mc_password); + } + + if( mc_db ) { + sfree(mc_db); + } + + if( mc_host ) { + sfree(mc_host); + } +} + + +void module_register(void) { + plugin_register_config(MC_PLUGIN_NAME, mc_config,config_keys,config_keys_num); + plugin_register_read(MC_PLUGIN_NAME, mc_read); + plugin_register_init(MC_PLUGIN_NAME, mc_init); + plugin_register_shutdown(MC_PLUGIN_NAME, mc_shutdown); + DEBUG("Mongo: driver registered"); +} diff --git a/src/mongo.h b/src/mongo.h new file mode 100644 index 00000000..e69de29b -- 2.11.0