![]() ![]() | ![]() |
1.1 misho 1: /*
2: * out_db.c Database Output
3: *
4: * Copyright (c) 2001-2005 Thomas Graf <tgraf@suug.ch>
5: *
6: * Permission is hereby granted, free of charge, to any person obtaining a
7: * copy of this software and associated documentation files (the "Software"),
8: * to deal in the Software without restriction, including without limitation
9: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10: * and/or sell copies of the Software, and to permit persons to whom the
11: * Software is furnished to do so, subject to the following conditions:
12: *
13: * The above copyright notice and this permission notice shall be included
14: * in all copies or substantial portions of the Software.
15: *
16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22: * DEALINGS IN THE SOFTWARE.
23: */
24:
25: /*
26: * NOTE
27: *
28: * This whole code is a realy mess and written in a hurry, most interactions
29: * should be cached to save traffic. It really is meant as experimental
30: * features.
31: */
32:
33: #include <bmon/bmon.h>
34: #include <bmon/node.h>
35: #include <bmon/output.h>
36: #include <bmon/graph.h>
37: #include <bmon/input.h>
38: #include <bmon/utils.h>
39:
40: #include <dbi/dbi.h>
41:
42: static char *c_driverdir = NULL;
43: static char *c_driver = "mysql";
44: static char *c_host = "localhost";
45: static char *c_username = "bmon";
46: static char *c_dbname = "bmon";
47: static char *c_mask = "mhd";
48: static char *c_password;
49: static int c_interval = 3;
50:
51: static int do_read;
52: static int do_sec;
53: static int do_min;
54: static int do_hour;
55: static int do_day;
56:
57: static dbi_conn db_conn;
58:
59: static int create_tables(void)
60: {
61: dbi_result r;
62:
63: r = dbi_conn_query(db_conn,
64: "CREATE TABLE IF NOT EXISTS nodes (" \
65: " id INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE," \
66: " name TEXT NOT NULL," \
67: " source TEXT," \
68: " PRIMARY KEY (id)" \
69: ")");
70:
71: if (!r)
72: return 0;
73: else
74: dbi_result_free(r);
75:
76: r = dbi_conn_query(db_conn,
77: "CREATE TABLE IF NOT EXISTS items (" \
78: " id INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE," \
79: " name TEXT NOT NULL," \
80: " description TEXT," \
81: " node INT UNSIGNED NOT NULL," \
82: " handle INT UNSIGNED," \
83: " parent INT UNSIGNED," \
84: " indent INT UNSIGNED," \
85: " rx_usage SMALLINT NOT NULL," \
86: " tx_usage SMALLINT NOT NULL," \
87: " PRIMARY KEY (id)," \
88: " FOREIGN KEY (node) REFERENCES nodes(id)," \
89: " FOREIGN KEY (parent) REFERENCES items(id)" \
90: ")");
91:
92: if (!r)
93: return 0;
94: else
95: dbi_result_free(r);
96:
97: r = dbi_conn_query(db_conn,
98: "CREATE TABLE IF NOT EXISTS attrs (" \
99: " id INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE," \
100: " name CHAR(15) NOT NULL," \
101: " item INT UNSIGNED NOT NULL," \
102: " rx_rate INT UNSIGNED," \
103: " tx_rate INT UNSIGNED," \
104: " rx_counter BIGINT UNSIGNED NOT NULL," \
105: " tx_counter BIGINT UNSIGNED NOT NULL," \
106: " PRIMARY KEY (id)," \
107: " FOREIGN KEY (item) REFERENCES items(id)" \
108: ")");
109:
110: if (!r)
111: return 0;
112: else
113: dbi_result_free(r);
114:
115: r = dbi_conn_query(db_conn,
116: "CREATE TABLE IF NOT EXISTS attr_desc (" \
117: " id CHAR(15) NOT NULL UNIQUE," \
118: " is_num SMALLINT UNSIGNED NOT NULL," \
119: " txt TEXT NOT NULL," \
120: " PRIMARY KEY(id)" \
121: ")");
122:
123: if (!r)
124: return 0;
125: else
126: dbi_result_free(r);
127:
128: r = dbi_conn_query(db_conn,
129: "CREATE TABLE IF NOT EXISTS hist_r (" \
130: " attr INT UNSIGNED NOT NULL," \
131: " ts INT UNSIGNED NOT NULL," \
132: " offset INT UNSIGNED NOT NULL," \
133: " rx_rate INT UNSIGNED NOT NULL," \
134: " tx_rate INT UNSIGNED NOT NULL," \
135: " PRIMARY KEY (attr, ts, offset)," \
136: " FOREIGN KEY (attr) REFERENCES attrs(id)" \
137: ")");
138:
139: if (!r)
140: return 0;
141: else
142: dbi_result_free(r);
143:
144: r = dbi_conn_query(db_conn,
145: "CREATE TABLE IF NOT EXISTS hist_s (" \
146: " attr INT UNSIGNED NOT NULL," \
147: " ts INT UNSIGNED NOT NULL," \
148: " offset INT UNSIGNED NOT NULL," \
149: " rx_rate INT UNSIGNED NOT NULL," \
150: " tx_rate INT UNSIGNED NOT NULL," \
151: " PRIMARY KEY (attr, ts, offset)," \
152: " FOREIGN KEY (attr) REFERENCES attrs(id)" \
153: ")");
154:
155: if (!r)
156: return 0;
157: else
158: dbi_result_free(r);
159:
160: r = dbi_conn_query(db_conn,
161: "CREATE TABLE IF NOT EXISTS hist_m (" \
162: " attr INT UNSIGNED NOT NULL," \
163: " ts INT UNSIGNED NOT NULL," \
164: " offset INT UNSIGNED NOT NULL," \
165: " rx_rate INT UNSIGNED NOT NULL," \
166: " tx_rate INT UNSIGNED NOT NULL," \
167: " PRIMARY KEY (attr, ts, offset)," \
168: " FOREIGN KEY (attr) REFERENCES attrs(id)" \
169: ")");
170:
171: if (!r)
172: return 0;
173: else
174: dbi_result_free(r);
175:
176: r = dbi_conn_query(db_conn,
177: "CREATE TABLE IF NOT EXISTS hist_h (" \
178: " attr INT UNSIGNED NOT NULL," \
179: " ts INT UNSIGNED NOT NULL," \
180: " offset INT UNSIGNED NOT NULL," \
181: " rx_rate INT UNSIGNED NOT NULL," \
182: " tx_rate INT UNSIGNED NOT NULL," \
183: " PRIMARY KEY (attr, ts, offset)," \
184: " FOREIGN KEY (attr) REFERENCES attrs(id)" \
185: ")");
186:
187: if (!r)
188: return 0;
189: else
190: dbi_result_free(r);
191:
192: r = dbi_conn_query(db_conn,
193: "CREATE TABLE IF NOT EXISTS hist_d (" \
194: " attr INT UNSIGNED NOT NULL," \
195: " ts INT UNSIGNED NOT NULL," \
196: " offset INT UNSIGNED NOT NULL," \
197: " rx_rate INT UNSIGNED NOT NULL," \
198: " tx_rate INT UNSIGNED NOT NULL," \
199: " PRIMARY KEY (attr, ts, offset)," \
200: " FOREIGN KEY (attr) REFERENCES attrs(id)" \
201: ")");
202:
203: if (!r)
204: return 0;
205: else
206: dbi_result_free(r);
207:
208: return 1;
209: }
210:
211: static int insert_attr_desc(struct attr_type *a, void *arg)
212: {
213: dbi_result r = dbi_conn_queryf(db_conn,
214: "SELECT id FROM attr_desc WHERE id = '%s'", a->name);
215:
216: if (!r)
217: return -1;
218:
219: if (dbi_result_first_row(r) == 1) {
220: dbi_result_free(r);
221: return 0;
222: }
223:
224: dbi_result_free(r);
225:
226: r = dbi_conn_queryf(db_conn,
227: "INSERT INTO attr_desc (id, is_num, txt) VALUES " \
228: "('%s', %d, '%s')", a->name, a->unit == U_NUMBER,
229: a->desc);
230:
231: if (!r)
232: return -1;
233:
234: dbi_result_free(r);
235: return 0;
236: }
237:
238: static int insert_attr_descs(void)
239: {
240: return !foreach_attr_type(&insert_attr_desc, NULL);
241: }
242:
243: static void add_history(const char *table, int attr_id, timestamp_t *ts,
244: rate_cnt_t rx, rate_cnt_t tx)
245: {
246: dbi_result r = dbi_conn_queryf(db_conn,
247: "INSERT INTO %s (attr, ts, offset, rx_rate, tx_rate) " \
248: "values (%d, %u, %u, %u, %u)",
249: table, attr_id, (unsigned int) ts->tv_sec,
250: (unsigned int) ts->tv_usec, rx, tx);
251:
252: if (r)
253: dbi_result_free(r);
254: }
255:
256:
257: static inline int attr_exists(const char *name, int item_id)
258: {
259: int ret = -1;
260:
261: dbi_result r = dbi_conn_queryf(db_conn,
262: "SELECT id FROM attrs WHERE name = '%s' AND " \
263: "item = %d", name, item_id);
264:
265: if (r) {
266: if (dbi_result_first_row(r) == 1)
267: ret = (int) dbi_result_get_long(r, "id");
268: dbi_result_free(r);
269: }
270:
271: return ret;
272: }
273:
274: static inline int insert_attr(stat_attr_t *a, int item_id)
275: {
276: dbi_result r;
277:
278: r = dbi_conn_queryf(db_conn,
279: "INSERT INTO attrs (name, item, rx_rate, tx_rate, " \
280: "rx_counter, tx_counter) VALUES ('%s', %d, %d, %d, " \
281: "%lld, %lld)", type2name(a->a_type), item_id,
282: attr_get_rx_rate(a), attr_get_tx_rate(a),
283: attr_get_rx(a), attr_get_tx(a));
284:
285: if (!r)
286: return -1;
287:
288: dbi_result_free(r);
289: return attr_exists(type2name(a->a_type), item_id);
290:
291: }
292:
293: static inline int up_attr(stat_attr_t *a, int attr_id)
294: {
295: dbi_result r;
296:
297: r = dbi_conn_queryf(db_conn,
298: "UPDATE attrs SET rx_rate = %d, tx_rate = %d, " \
299: "rx_counter = %lld, tx_counter = %lld WHERE id = %d",
300: attr_get_rx_rate(a), attr_get_tx_rate(a),
301: attr_get_rx(a), attr_get_tx(a), attr_id);
302:
303: if (!r)
304: return -1;
305:
306: dbi_result_free(r);
307:
308: return 0;
309: }
310:
311: static inline int item_exists(item_t *it, node_t *node, int node_id)
312: {
313: int ret = -1;
314: dbi_result r;
315:
316: if (it->i_flags & ITEM_FLAG_IS_CHILD) {
317: int parent;
318: item_t *p = get_item(node, it->i_parent);
319: if (!p)
320: goto no_parent;
321:
322: parent = item_exists(p, node, node_id);
323: if (parent < 0)
324: goto no_parent;
325:
326: r = dbi_conn_queryf(db_conn,
327: "SELECT id FROM items WHERE node = %d AND " \
328: "name = '%s' AND handle = %d AND parent = %d",
329: node_id, it->i_name, it->i_handle, parent);
330: goto skip;
331: }
332:
333: no_parent:
334: r = dbi_conn_queryf(db_conn,
335: "SELECT id FROM items WHERE node = %d AND " \
336: "name = '%s' AND handle = %d", node_id, it->i_name, it->i_handle);
337:
338: skip:
339: if (r) {
340: if (dbi_result_first_row(r) == 1)
341: ret = (int) dbi_result_get_long(r, "id");
342: dbi_result_free(r);
343: }
344:
345: return ret;
346: }
347:
348: static inline int insert_item(item_t *it, node_t *node, int node_id)
349: {
350: dbi_result r;
351:
352: if (it->i_flags & ITEM_FLAG_IS_CHILD) {
353: int parent;
354: item_t *p = get_item(node, it->i_parent);
355: if (!p)
356: goto no_parent;
357:
358: parent = item_exists(p, node, node_id);
359: if (parent < 0)
360: goto no_parent;
361:
362: r = dbi_conn_queryf(db_conn,
363: "INSERT INTO items (name, node, handle, parent, " \
364: "indent, description) VALUES ('%s', %d, %d, %d, " \
365: "%d, %s%s%s)",
366: it->i_name, node_id, it->i_handle,
367: parent, it->i_level,
368: it->i_desc ? "'" : "",
369: it->i_desc ? it->i_desc : "NULL",
370: it->i_desc ? "'" : "");
371: goto skip;
372: }
373:
374: no_parent:
375: r = dbi_conn_queryf(db_conn,
376: "INSERT INTO items (name, node, handle) VALUES " \
377: "('%s', %d, %d)", it->i_name, node_id, it->i_handle);
378:
379: skip:
380: if (!r)
381: return -1;
382:
383: dbi_result_free(r);
384: return item_exists(it, node, node_id);
385: }
386:
387: static inline int up_item(item_t *it, int item_id)
388: {
389: dbi_result r;
390:
391: r = dbi_conn_queryf(db_conn,
392: "UPDATE attrs SET rx_usage = %d, tx_usage = %d " \
393: "WHERE id = %d",
394: it->i_rx_usage, it->i_tx_usage, item_id);
395:
396: if (!r)
397: return -1;
398:
399: dbi_result_free(r);
400:
401: return 0;
402: }
403:
404: static inline int node_exists(const char *name)
405: {
406: int ret = -1;
407: dbi_result r = dbi_conn_queryf(db_conn,
408: "SELECT id FROM nodes WHERE name = '%s'", name);
409:
410: if (r) {
411: if (dbi_result_first_row(r) == 1)
412: ret = (int) dbi_result_get_long(r, "id");
413: dbi_result_free(r);
414: }
415:
416: return ret;
417: }
418:
419: static inline int insert_node(const char *name, const char *source)
420: {
421: dbi_result r;
422:
423: if (source)
424: r = dbi_conn_queryf(db_conn,
425: "INSERT INTO nodes (name, source) VALUES ('%s', '%s')",
426: name, source);
427: else
428: r = dbi_conn_queryf(db_conn,
429: "INSERT INTO nodes (name) VALUES ('%s')", name);
430:
431: if (!r)
432: return -1;
433:
434: dbi_result_free(r);
435:
436: return node_exists(name);
437:
438: }
439:
440: static inline int cur_hist_index(int index)
441: {
442: return index == 0 ? (HISTORY_SIZE - 1) : (index - 1);
443: }
444:
445: struct xdata {
446: int node_id;
447: node_t *node;
448: };
449:
450: static void write_per_attr(stat_attr_t *a, void *arg)
451: {
452: int attr_id;
453: int item_id = (int) arg;
454:
455: attr_id = attr_exists(type2name(a->a_type), item_id);
456: if (attr_id < 0) {
457: attr_id = insert_attr(a, item_id);
458: if (attr_id < 0)
459: return;
460: } else
461: up_attr(a, attr_id);
462:
463: if (a->a_flags & ATTR_FLAG_HISTORY) {
464: history_t *h = &((stat_attr_hist_t *) a)->a_hist;
465:
466: if (do_read && get_read_interval() != 1.0f) {
467: int idx = cur_hist_index(h->h_read.he_index);
468: add_history("hist_r", attr_id,
469: &h->h_read.he_last_update,
470: h->h_read.he_rx.hd_data[idx],
471: h->h_read.he_tx.hd_data[idx]);
472: }
473:
474: if (do_sec && diff_now(&h->h_sec.he_last_update) < 0.5f) {
475: int idx = cur_hist_index(h->h_sec.he_index);
476: add_history("hist_s", attr_id,
477: &h->h_sec.he_last_update,
478: h->h_sec.he_rx.hd_data[idx],
479: h->h_sec.he_tx.hd_data[idx]);
480: }
481:
482: if (do_min && diff_now(&h->h_min.he_last_update) < 30.0f) {
483: int idx = cur_hist_index(h->h_min.he_index);
484: add_history("hist_m", attr_id,
485: &h->h_min.he_last_update,
486: h->h_min.he_rx.hd_data[idx],
487: h->h_min.he_tx.hd_data[idx]);
488: }
489:
490: if (do_hour && diff_now(&h->h_hour.he_last_update) < 1800.0f) {
491: int idx = cur_hist_index(h->h_hour.he_index);
492: add_history("hist_h", attr_id,
493: &h->h_hour.he_last_update,
494: h->h_hour.he_rx.hd_data[idx],
495: h->h_hour.he_tx.hd_data[idx]);
496: }
497:
498: if (do_day && diff_now(&h->h_day.he_last_update) < 43200.0f) {
499: int idx = cur_hist_index(h->h_day.he_index);
500: add_history("hist_d", attr_id,
501: &h->h_day.he_last_update,
502: h->h_day.he_rx.hd_data[idx],
503: h->h_day.he_tx.hd_data[idx]);
504: }
505: }
506: }
507:
508: static void write_per_item(item_t *item, void *arg)
509: {
510: int item_id;
511: struct xdata *x = (struct xdata *) arg;
512:
513: item_id = item_exists(item, x->node, x->node_id);
514: if (item_id < 0)
515: item_id = insert_item(item, x->node, x->node_id);
516:
517: if (item_id < 0)
518: return;
519:
520: up_item(item, item_id);
521:
522: foreach_attr(item, &write_per_attr, (void *) item_id);
523: }
524:
525: static void write_per_node(node_t *node, void *arg)
526: {
527: struct xdata x = { .node = node };
528:
529: x.node_id = node_exists(node->n_name);
530:
531: if (x.node_id < 0)
532: x.node_id = insert_node(node->n_name, node->n_from);
533:
534: if (x.node_id < 0)
535: return;
536:
537: foreach_item(node, write_per_item, &x);
538: }
539:
540: void db_draw(void)
541: {
542: static int rem = 1;
543:
544: if (--rem)
545: return;
546: else
547: rem = c_interval;
548:
549: foreach_node(write_per_node, NULL);
550: }
551:
552: static void print_module_help(void)
553: {
554: printf(
555: "DB - Database Output\n" \
556: "\n" \
557: " Writes current rate estimations into a database for\n" \
558: " other tools to pick up.\n" \
559: "\n" \
560: " Author: Thomas Graf <tgraf@suug.ch>\n" \
561: "\n" \
562: " Options:\n" \
563: " driverdir=DIR Directory containing libdi drivers\n" \
564: " driver=DRIVER DB driver (default: mysql)\n" \
565: " host=HOST Host the database is on (default: localhost)\n" \
566: " dbname=NAME Name of database (default: bmon)\n" \
567: " username=NAME Authentication username (default: bmon)\n" \
568: " password=TEXT Authentication password\n" \
569: " mask=MASK Write selection mask (default: mhd)\n" \
570: " interval=SEC Update interval in seconds (default: 3)\n" \
571: "\n" \
572: " Mask Attributes:\n" \
573: " r Read interval\n" \
574: " s Seconds\n" \
575: " m Minutes\n" \
576: " h Hours\n" \
577: " d Days\n" \
578: "\n" \
579: " Examples:\n" \
580: " -O 'db:password=bmon;mask=rmhd'");
581: }
582:
583: static void db_set_opts(tv_t *attrs)
584: {
585: while (attrs) {
586: if (!strcasecmp(attrs->type, "driverdir") && attrs->value)
587: c_driverdir = attrs->value;
588: else if (!strcasecmp(attrs->type, "driver") && attrs->value)
589: c_driver = attrs->value;
590: else if (!strcasecmp(attrs->type, "host") && attrs->value)
591: c_host = attrs->value;
592: else if (!strcasecmp(attrs->type, "username") && attrs->value)
593: c_username = attrs->value;
594: else if (!strcasecmp(attrs->type, "dbname") && attrs->value)
595: c_dbname = attrs->value;
596: else if (!strcasecmp(attrs->type, "password") && attrs->value)
597: c_password = attrs->value;
598: else if (!strcasecmp(attrs->type, "mask") && attrs->value)
599: c_mask = attrs->value;
600: else if (!strcasecmp(attrs->type, "interval") && attrs->value)
601: c_interval = strtol(attrs->value, NULL, 0);
602: else if (!strcasecmp(attrs->type, "help")) {
603: print_module_help();
604: exit(0);
605: }
606:
607: attrs = attrs->next;
608: }
609: }
610:
611: static int db_probe(void)
612: {
613: if (strchr(c_mask, 'r'))
614: do_read = 1;
615:
616: if (strchr(c_mask, 's'))
617: do_sec = 1;
618:
619: if (strchr(c_mask, 'm'))
620: do_min = 1;
621:
622: if (strchr(c_mask, 'h'))
623: do_hour = 1;
624:
625: if (strchr(c_mask, 'd'))
626: do_day = 1;
627:
628: if (c_password == NULL) {
629: fprintf(stderr, "You must specify the database password\n");
630: return 0;
631: }
632:
633: if (dbi_initialize(c_driverdir) < 0) {
634: fprintf(stderr, "Cannot initialize DBI layer\n");
635: return 0;
636: }
637:
638: db_conn = dbi_conn_new(c_driver);
639: if (db_conn == NULL) {
640: fprintf(stderr, "Cannot initialize connection \"%s\"\n",
641: c_driver);
642: return 0;
643: }
644:
645: dbi_conn_set_option(db_conn, "host", c_host);
646: dbi_conn_set_option(db_conn, "username", c_username);
647: dbi_conn_set_option(db_conn, "password", c_password);
648: dbi_conn_set_option(db_conn, "dbname", c_dbname);
649:
650: if (dbi_conn_connect(db_conn) < 0) {
651: fprintf(stderr, "Cannot open database \"%s\" connection on " \
652: "%s@%s\n", c_dbname, c_username, c_host);
653: return 0;
654: }
655:
656: if (!create_tables()) {
657: fprintf(stderr, "Could not create tables\n");
658: return 0;
659: }
660:
661: if (!insert_attr_descs()) {
662: fprintf(stderr, "Could not insert attribute descriptions\n");
663: return 0;
664: }
665:
666: return 1;
667: }
668:
669: static void db_shutdown(void)
670: {
671: if (db_conn)
672: dbi_conn_close(db_conn);
673:
674: dbi_shutdown();
675: }
676:
677: static struct output_module db_ops = {
678: .om_name = "db",
679: .om_draw = db_draw,
680: .om_set_opts = db_set_opts,
681: .om_probe = db_probe,
1.1.1.1.2.1! misho 682: .om_shutdown = db_shutdown,
1.1 misho 683: };
684:
685: static void __init db_init(void)
686: {
687: register_secondary_output_module(&db_ops);
688: }