mongodb plugin: Refactored many of the other handle_* functions.
[collectd.git] / src / mongodb.c
1 /**
2  * collectd - src/mongo.c
3  * Copyright (C) 2010 Ryan Cox 
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Ryan Cox <ryan.a.cox@gmail.com> 
20  **/
21
22 #include "collectd.h"
23 #include "common.h" 
24 #include "plugin.h" 
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30
31 #if HAVE_STDINT_H
32 # define MONGO_HAVE_STDINT 1
33 #else
34 # define MONGO_USE_LONG_LONG_INT 1
35 #endif
36 #include <mongo.h>
37
38 #define MC_PLUGIN_NAME "mongo"
39 #define MC_MONGO_DEF_HOST "127.0.0.1"
40 #define MC_MONGO_DEF_PORT 27017 
41 #define MC_MONGO_DEF_DB "admin"
42 #define MC_MIN_PORT 1
43 #define MC_MAX_PORT 65535 
44 #define SUCCESS 0 
45 #define FAILURE -1 
46
47 static char *mc_user = NULL;
48 static char *mc_password = NULL;
49 static char *mc_db = NULL; 
50 static char *mc_host = NULL;
51 static int  mc_port = MC_MONGO_DEF_PORT;
52
53 static mongo_connection mc_connection;
54 static _Bool mc_have_connection = 0;
55
56 static const char *config_keys[] = {
57         "User",
58         "Password",
59         "Database",
60         "Host",
61         "Port"
62 };
63 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
64
65 static void submit (const char *type, const char *instance,
66         value_t *values, size_t values_len)
67 {
68     value_list_t v = VALUE_LIST_INIT;
69
70     v.values = values;
71     v.values_len = values_len;
72
73     sstrncpy (v.host, hostname_g, sizeof(v.host));
74     sstrncpy (v.plugin, MC_PLUGIN_NAME, sizeof(v.plugin));
75     ssnprintf (v.plugin_instance, sizeof (v.plugin_instance), "%i", mc_port);
76     sstrncpy (v.type, type, sizeof(v.type));
77
78     if (instance != NULL)
79         sstrncpy (v.type_instance, instance, sizeof (v.type_instance)); 
80
81     plugin_dispatch_values (&v);
82 }
83
84 static void submit_gauge (const char *type, const char *instance, /* {{{ */
85         gauge_t gauge)
86 {
87         value_t v;
88
89         v.gauge = gauge;
90         submit(type, instance, &v, /* values_len = */ 1);
91 } /* }}} void submit_gauge */
92
93 static void submit_derive (const char *type, const char *instance, /* {{{ */
94         derive_t derive)
95 {
96         value_t v;
97
98         v.derive = derive;
99         submit(type, instance, &v, /* values_len = */ 1);
100 } /* }}} void submit_derive */
101
102 static int handle_field (bson *obj, const char *field, /* {{{ */
103         int (*func) (bson_iterator *))
104 {
105     bson_type type;
106     bson subobj;
107     bson_iterator i;
108     bson_iterator j;
109     int status = 0;
110
111     type = bson_find (&i, obj, field);
112     if (type != bson_object)
113         return (EINVAL);
114
115     bson_iterator_subobject (&i, &subobj);
116     bson_iterator_init (&j, subobj.data);
117     while (bson_iterator_next (&j))
118     {
119         status = (*func) (&j);
120         if (status != 0)
121             break;
122     }
123     bson_destroy (&subobj);
124
125     return (status);
126 } /* }}} int handle_field */
127
128 static int handle_opcounters (bson_iterator *iter) /* {{{ */
129 {
130     bson_type type;
131     const char *key;
132     derive_t value;
133
134     type = bson_iterator_type (iter);
135     if ((type != bson_long) && (type != bson_int))
136         return (0);
137
138     key = bson_iterator_key (iter);
139     if (key == NULL)
140         return (0);
141
142     value = (derive_t) bson_iterator_long (iter);
143
144     submit_derive ("total_operations", key, value);
145     return (0);
146 } /* }}} int handle_opcounters */
147
148 static int handle_mem (bson_iterator *iter) /* {{{ */
149 {
150     bson_type type;
151     const char *key;
152     gauge_t value;
153
154     type = bson_iterator_type (iter);
155     if ((type != bson_double) && (type != bson_long) && (type != bson_int))
156         return (0);
157
158     key = bson_iterator_key (iter);
159     if (key == NULL)
160         return (0);
161
162     /* Is "virtual" really interesting?
163      * What exactly does "mapped" mean? */
164     if ((strcasecmp ("mapped", key) != 0)
165             && (strcasecmp ("resident", key) != 0)
166             && (strcasecmp ("virtual", key) != 0))
167         return (0);
168
169     value = (gauge_t) bson_iterator_double (iter);
170     /* All values are in MByte */
171     value *= 1048576.0;
172
173     submit_gauge ("memory", key, value);
174     return (0);
175 } /* }}} int handle_mem */
176
177 static int handle_connections (bson_iterator *iter) /* {{{ */
178 {
179     bson_type type;
180     const char *key;
181     gauge_t value;
182
183     type = bson_iterator_type (iter);
184     if ((type != bson_double) && (type != bson_long) && (type != bson_int))
185         return (0);
186
187     key = bson_iterator_key (iter);
188     if (key == NULL)
189         return (0);
190
191     if (strcmp ("current", key) != 0)
192         return (0);
193
194     value = (gauge_t) bson_iterator_double (iter);
195
196     submit_gauge ("current_connections", NULL, value);
197     return (0);
198 } /* }}} int handle_connections */
199
200 static int handle_lock (bson_iterator *iter) /* {{{ */
201 {
202     bson_type type;
203     const char *key;
204     derive_t value;
205
206     type = bson_iterator_type (iter);
207     if ((type != bson_double) && (type != bson_long) && (type != bson_int))
208         return (0);
209
210     key = bson_iterator_key (iter);
211     if (key == NULL)
212         return (0);
213
214     if (strcmp ("lockTime", key) != 0)
215         return (0);
216
217     value = (derive_t) bson_iterator_long (iter);
218     /* The time is measured in microseconds (us). We convert it to
219      * milliseconds (ms). */
220     value = value / 1000;
221
222     submit_derive ("total_time_in_ms", "lock_held", value);
223     return (0);
224 } /* }}} int handle_lock */
225
226 static int handle_btree (bson *obj) /* {{{ */
227 {
228     bson_iterator i;
229
230     bson_iterator_init (&i, obj->data);
231     while (bson_iterator_next (&i))
232     {
233         bson_type type;
234         const char *key;
235         gauge_t value;
236
237         type = bson_iterator_type (&i);
238         if ((type != bson_double) && (type != bson_long) && (type != bson_int))
239             continue;
240
241         key = bson_iterator_key (&i);
242         if (key == NULL)
243             continue;
244
245         value = (gauge_t) bson_iterator_double (&i);
246
247         if (strcmp ("hits", key) == 0)
248             submit_gauge ("cache_result", "hit", value);
249         else if (strcmp ("misses", key) != 0)
250             submit_gauge ("cache_result", "miss", value);
251     }
252
253     return (0);
254 } /* }}} int handle_btree */
255
256 static int handle_index_counters (bson_iterator *iter) /* {{{ */
257 {
258     bson_type type;
259     const char *key;
260     bson subobj;
261     int status;
262
263     type = bson_iterator_type (iter);
264     if (type != bson_object)
265         return (0);
266
267     key = bson_iterator_key (iter);
268     if (key == NULL)
269         return (0);
270
271     if (strcmp ("btree", key) != 0)
272         return (0);
273
274     bson_iterator_subobject (iter, &subobj);
275     status = handle_btree (&subobj);
276     bson_destroy (&subobj);
277
278     return (status);
279 } /* }}} int handle_index_counters */
280
281 static int handle_dbstats (bson *obj)
282 {
283     bson_iterator i;
284
285     bson_iterator_init (&i, obj->data);
286     while (bson_iterator_next (&i))
287     {
288         bson_type type;
289         const char *key;
290         gauge_t value;
291
292         type = bson_iterator_type (&i);
293         if ((type != bson_double) && (type != bson_long) && (type != bson_int))
294             return (0);
295
296         key = bson_iterator_key (&i);
297         if (key == NULL)
298             return (0);
299
300         value = (gauge_t) bson_iterator_double (&i);
301
302         /* counts */
303         if (strcmp ("collections", key) == 0)
304             submit_gauge ("gauge", "collections", value);
305         else if (strcmp ("objects", key) == 0)
306             submit_gauge ("gauge", "objects", value);
307         else if (strcmp ("numExtents", key) == 0)
308             submit_gauge ("gauge", "num_extents", value);
309         else if (strcmp ("indexes", key) == 0)
310             submit_gauge ("gauge", "indexes", value);
311         /* sizes */
312         else if (strcmp ("dataSize", key) == 0)
313             submit_gauge ("bytes", "data", value);
314         else if (strcmp ("storageSize", key) == 0)
315             submit_gauge ("bytes", "storage", value);
316         else if (strcmp ("indexSize", key) == 0)
317             submit_gauge ("bytes", "index", value);
318     }
319
320     return (0);
321 } /* }}} int handle_dbstats */
322
323 static int do_stats (void) /* {{{ */
324 {
325     bson obj;
326
327     /* TODO: 
328
329         change this to raw runCommand
330              db.runCommand( { dbstats : 1 } ); 
331              succeeds but is getting back all zeros !?!
332         modify bson_print to print type 18
333         repro problem w/o db name - show dbs doesn't work again
334         why does db.admin.dbstats() work fine in shell?
335         implement retries ? noticed that if db is unavailable, collectd dies
336     */
337
338     if( !mongo_simple_int_command(&mc_connection, mc_db, "dbstats", 1, &obj) ) {
339         ERROR("Mongo: failed to call stats Host [%s] Port [%d] User [%s]", 
340             mc_host, mc_port, mc_user);
341         return FAILURE;
342     }
343     
344     handle_dbstats (&obj);
345
346     bson_destroy (&obj);
347
348     return (0);
349 } /* }}} int do_stats */
350
351 static int do_server_status (void) /* {{{ */
352 {
353     bson obj;
354     bson_bool_t status;
355
356     status = mongo_simple_int_command (&mc_connection, /* db = */ mc_db,
357             /* command = */ "serverStatus", /* arg = */ 1, /* out = */ &obj);
358     if (!status)
359     {
360         ERROR("mongodb plugin: mongo_simple_int_command (%s:%i, serverStatus) "
361                 "failed.", mc_host, mc_port);
362         return (-1);
363     }
364
365     handle_field (&obj, "opcounters",    handle_opcounters);
366     handle_field (&obj, "mem",           handle_mem);
367     handle_field (&obj, "connections",   handle_connections);
368     handle_field (&obj, "globalLock",    handle_lock);
369     handle_field (&obj, "indexCounters", handle_index_counters);
370
371     bson_destroy(&obj);
372     return (0);
373 } /* }}} int do_server_status */
374
375 static int mc_read(void) {
376     DEBUG("Mongo: mongo driver read"); 
377
378     if(do_server_status() != SUCCESS) {
379         ERROR("Mongo: do server status failed"); 
380         return FAILURE;
381     }
382
383     if(do_stats() != SUCCESS) {
384         ERROR("Mongo: do stats status failed"); 
385         return FAILURE;
386     }
387
388     return SUCCESS;
389 }
390
391
392 static void config_set(char** dest, const char* src ) {
393         if( *dest ) {
394             sfree(*dest);
395         }
396                 *dest= malloc(strlen(src)+1);
397         sstrncpy(*dest,src,strlen(src)+1);
398 }
399
400 static int mc_config(const char *key, const char *value)
401
402     DEBUG("Mongo: config key [%s] value [%s]", key, value); 
403
404     if(strcasecmp("Host", key) == 0) {
405         config_set(&mc_host,value);
406     }
407     else if(strcasecmp("Port", key) == 0)
408     {
409         int tmp;
410         
411         tmp = service_name_to_port_number (value);
412         if (tmp > 0)
413             mc_port = tmp;
414         else
415         {
416             ERROR("mongodb plugin: failed to parse Port value: %s", value);
417             return (-1);
418         }
419     }
420     else if(strcasecmp("User", key) == 0) {
421         config_set(&mc_user,value);
422     }
423     else if(strcasecmp("Password", key) == 0) {
424         config_set(&mc_password,value);
425     }
426     else if(strcasecmp("Database", key) == 0) {
427         config_set(&mc_db,value);
428     }
429     else
430     {
431         ERROR ("mongodb plugin: Unknown config option: %s", key);
432         return (-1);
433     }
434
435     return SUCCESS;
436
437
438 static int mc_init(void)
439 {
440     if (mc_have_connection)
441         return (0);
442
443     DEBUG("mongo driver initializing"); 
444     if( !mc_host) {
445         DEBUG("Mongo: Host not specified. Using default [%s]",MC_MONGO_DEF_HOST); 
446         config_set(&mc_host, MC_MONGO_DEF_HOST);
447     }
448
449     if( !mc_db) {
450         DEBUG("Mongo: Database not specified. Using default [%s]",MC_MONGO_DEF_DB); 
451         config_set(&mc_db, MC_MONGO_DEF_DB);
452     }
453
454     mongo_connection_options opts;
455     sstrncpy(opts.host, mc_host, sizeof(opts.host));
456     opts.port = mc_port; 
457
458     if(mongo_connect(&mc_connection, &opts )){
459         ERROR("Mongo: driver failed to connect. Host [%s] Port [%d] User [%s]", 
460                 mc_host, mc_port, mc_user);
461         return FAILURE;     
462     }
463
464     mc_have_connection = 1;
465     return SUCCESS;
466
467
468 static int mc_shutdown(void)
469 {
470     DEBUG("Mongo: driver shutting down"); 
471
472     if (mc_have_connection) {
473         mongo_disconnect (&mc_connection);
474         mongo_destroy (&mc_connection);
475         mc_have_connection = 0;
476     }
477
478     sfree(mc_user);
479     sfree(mc_password);
480     sfree(mc_db);
481     sfree(mc_host);
482
483     return (0);
484 }
485
486
487 void module_register(void)  {       
488     plugin_register_config (MC_PLUGIN_NAME, mc_config,
489             config_keys, config_keys_num);
490     plugin_register_read (MC_PLUGIN_NAME, mc_read);
491     plugin_register_init (MC_PLUGIN_NAME, mc_init);
492     plugin_register_shutdown (MC_PLUGIN_NAME, mc_shutdown);
493 }
494
495 /* vim: set sw=4 sts=4 et fdm=marker : */