1 /*
2 * rrdtoolmodule.c
3 *
4 * RRDTool Python binding
5 *
6 * Author : Hye-Shik Chang <perky@fallin.lv>
7 * Date : $Date: 2003/02/22 07:41:19 $
8 * Created : 23 May 2002
9 *
10 * $Revision: 1.14 $
11 *
12 * ==========================================================================
13 * This file is part of py-rrdtool.
14 *
15 * py-rrdtool is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License as published
17 * by the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * py-rrdtool is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with Foobar; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 */
31 #ifdef UNUSED
32 #elif defined(__GNUC__)
33 # define UNUSED(x) x __attribute__((unused))
34 #elif defined(__LCLINT__)
35 # define UNUSED(x) /*@unused@*/ x
36 #else
37 # define UNUSED(x) x
38 #endif
41 #include "../../rrd_config.h"
42 static const char *__version__ = PACKAGE_VERSION;
44 #include "Python.h"
45 #include "../../src/rrd_tool.h"
46 //#include "rrd.h"
47 //#include "rrd_extra.h"
49 static PyObject *ErrorObject;
50 extern int optind;
51 extern int opterr;
53 /* forward declaration to keep compiler happy */
54 void initrrdtool(
55 void);
57 static int create_args(
58 char *command,
59 PyObject * args,
60 int *argc,
61 char ***argv)
62 {
63 PyObject *o, *lo;
64 int args_count,
65 argv_count,
66 element_count,
67 i, j;
69 args_count = PyTuple_Size(args);
70 element_count = 0;
71 for (i = 0; i < args_count; i++) {
72 o = PyTuple_GET_ITEM(args, i);
73 if (PyString_Check(o))
74 element_count++;
75 else if (PyList_CheckExact(o))
76 element_count += PyList_Size(o);
77 else {
78 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
79 return -1;
80 }
81 }
83 *argv = PyMem_New(char *,
84 element_count + 1);
86 if (*argv == NULL)
87 return -1;
89 argv_count = 0;
90 for (i = 0; i < args_count; i++) {
91 o = PyTuple_GET_ITEM(args, i);
92 if (PyString_Check(o)) {
93 argv_count++;
94 (*argv)[argv_count] = PyString_AS_STRING(o);
95 } else if (PyList_CheckExact(o))
96 for (j = 0; j < PyList_Size(o); j++) {
97 lo = PyList_GetItem(o, j);
98 if (PyString_Check(lo)) {
99 argv_count++;
100 (*argv)[argv_count] = PyString_AS_STRING(lo);
101 } else {
102 PyMem_Del(*argv);
103 PyErr_Format(PyExc_TypeError, "element %d in argument %d must be string", j, i);
104 return -1;
105 }
106 }
107 else {
108 PyMem_Del(*argv);
109 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
110 return -1;
111 }
112 }
114 (*argv)[0] = command;
115 *argc = element_count + 1;
117 /* reset getopt state */
118 opterr = optind = 0;
120 return 0;
121 }
123 static void destroy_args(
124 char ***argv)
125 {
126 PyMem_Del(*argv);
127 *argv = NULL;
128 }
130 static char PyRRD_create__doc__[] =
131 "create(args..): Set up a new Round Robin Database\n\
132 create filename [--start|-b start time] \
133 [--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \
134 [RRA:CF:xff:steps:rows]";
136 static PyObject *PyRRD_create(
137 PyObject UNUSED(*self),
138 PyObject * args)
139 {
140 PyObject *r;
141 char **argv;
142 int argc;
144 if (create_args("create", args, &argc, &argv) < 0)
145 return NULL;
147 if (rrd_create(argc, argv) == -1) {
148 PyErr_SetString(ErrorObject, rrd_get_error());
149 rrd_clear_error();
150 r = NULL;
151 } else {
152 Py_INCREF(Py_None);
153 r = Py_None;
154 }
156 destroy_args(&argv);
157 return r;
158 }
160 static char PyRRD_update__doc__[] =
161 "update(args..): Store a new set of values into the rrd\n"
162 " update filename [--template|-t ds-name[:ds-name]...] "
163 "N|timestamp:value[:value...] [timestamp:value[:value...] ...]";
165 static PyObject *PyRRD_update(
166 PyObject UNUSED(*self),
167 PyObject * args)
168 {
169 PyObject *r;
170 char **argv;
171 int argc;
173 if (create_args("update", args, &argc, &argv) < 0)
174 return NULL;
176 if (rrd_update(argc, argv) == -1) {
177 PyErr_SetString(ErrorObject, rrd_get_error());
178 rrd_clear_error();
179 r = NULL;
180 } else {
181 Py_INCREF(Py_None);
182 r = Py_None;
183 }
185 destroy_args(&argv);
186 return r;
187 }
189 static char PyRRD_fetch__doc__[] =
190 "fetch(args..): fetch data from an rrd.\n"
191 " fetch filename CF [--resolution|-r resolution] "
192 "[--start|-s start] [--end|-e end]";
194 static PyObject *PyRRD_fetch(
195 PyObject UNUSED(*self),
196 PyObject * args)
197 {
198 PyObject *r;
199 rrd_value_t *data, *datai;
200 unsigned long step, ds_cnt;
201 time_t start, end;
202 int argc;
203 char **argv, **ds_namv;
205 if (create_args("fetch", args, &argc, &argv) < 0)
206 return NULL;
208 if (rrd_fetch(argc, argv, &start, &end, &step,
209 &ds_cnt, &ds_namv, &data) == -1) {
210 PyErr_SetString(ErrorObject, rrd_get_error());
211 rrd_clear_error();
212 r = NULL;
213 } else {
214 /* Return :
215 ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */
216 PyObject *range_tup, *dsnam_tup, *data_list, *t;
217 unsigned long i, j, row;
218 rrd_value_t dv;
220 row = (end - start) / step;
222 r = PyTuple_New(3);
223 range_tup = PyTuple_New(3);
224 dsnam_tup = PyTuple_New(ds_cnt);
225 data_list = PyList_New(row);
226 PyTuple_SET_ITEM(r, 0, range_tup);
227 PyTuple_SET_ITEM(r, 1, dsnam_tup);
228 PyTuple_SET_ITEM(r, 2, data_list);
230 datai = data;
232 PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long) start));
233 PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long) end));
234 PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long) step));
236 for (i = 0; i < ds_cnt; i++)
237 PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
239 for (i = 0; i < row; i++) {
240 t = PyTuple_New(ds_cnt);
241 PyList_SET_ITEM(data_list, i, t);
243 for (j = 0; j < ds_cnt; j++) {
244 dv = *(datai++);
245 if (isnan(dv)) {
246 PyTuple_SET_ITEM(t, j, Py_None);
247 Py_INCREF(Py_None);
248 } else {
249 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
250 }
251 }
252 }
254 for (i = 0; i < ds_cnt; i++)
255 rrd_freemem(ds_namv[i]);
256 rrd_freemem(ds_namv); /* rrdtool don't use PyMem_Malloc :) */
257 rrd_freemem(data);
258 }
260 destroy_args(&argv);
261 return r;
262 }
264 static char PyRRD_graph__doc__[] =
265 "graph(args..): Create a graph based on data from one or several RRD\n"
266 " graph filename [-s|--start seconds] "
267 "[-e|--end seconds] [-x|--x-grid x-axis grid and label] "
268 "[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] "
269 "[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value "
270 "[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] "
271 "[-i|--interlaced] "
272 "[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] "
273 "[-B|--background value] [-O|--overlay value] "
274 "[-U|--unit value] [-z|--lazy] [-o|--logarithmic] "
275 "[-u|--upper-limit value] [-l|--lower-limit value] "
276 "[-g|--no-legend] [-r|--rigid] [--step value] "
277 "[-b|--base value] [-c|--color COLORTAG#rrggbb] "
278 "[-t|--title title] [DEF:vname=rrd:ds-name:CF] "
279 "[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] "
280 "[GPRINT:vname:CF:format] [COMMENT:text] "
281 "[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] "
282 "[LINE{1|2|3}:vname[#rrggbb[:legend]]] "
283 "[AREA:vname[#rrggbb[:legend]]] " "[STACK:vname[#rrggbb[:legend]]]";
285 static PyObject *PyRRD_graph(
286 PyObject UNUSED(*self),
287 PyObject * args)
288 {
289 PyObject *r;
290 char **argv, **calcpr;
291 int argc, xsize, ysize, i;
292 double ymin, ymax;
294 if (create_args("graph", args, &argc, &argv) < 0)
295 return NULL;
297 if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) ==
298 -1) {
299 PyErr_SetString(ErrorObject, rrd_get_error());
300 rrd_clear_error();
301 r = NULL;
302 } else {
303 r = PyTuple_New(3);
305 PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) xsize));
306 PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long) ysize));
308 if (calcpr) {
309 PyObject *e, *t;
311 e = PyList_New(0);
312 PyTuple_SET_ITEM(r, 2, e);
314 for (i = 0; calcpr[i]; i++) {
315 t = PyString_FromString(calcpr[i]);
316 PyList_Append(e, t);
317 Py_DECREF(t);
318 rrd_freemem(calcpr[i]);
319 }
320 rrd_freemem(calcpr);
321 } else {
322 Py_INCREF(Py_None);
323 PyTuple_SET_ITEM(r, 2, Py_None);
324 }
325 }
327 destroy_args(&argv);
328 return r;
329 }
331 static char PyRRD_tune__doc__[] =
332 "tune(args...): Modify some basic properties of a Round Robin Database\n"
333 " tune filename [--heartbeat|-h ds-name:heartbeat] "
334 "[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] "
335 "[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]";
337 static PyObject *PyRRD_tune(
338 PyObject UNUSED(*self),
339 PyObject * args)
340 {
341 PyObject *r;
342 char **argv;
343 int argc;
345 if (create_args("tune", args, &argc, &argv) < 0)
346 return NULL;
348 if (rrd_tune(argc, argv) == -1) {
349 PyErr_SetString(ErrorObject, rrd_get_error());
350 rrd_clear_error();
351 r = NULL;
352 } else {
353 Py_INCREF(Py_None);
354 r = Py_None;
355 }
357 destroy_args(&argv);
358 return r;
359 }
361 static char PyRRD_first__doc__[] =
362 "first(filename): Return the timestamp of the first data sample in an RRD";
364 static PyObject *PyRRD_first(
365 PyObject UNUSED(*self),
366 PyObject * args)
367 {
368 PyObject *r;
369 int argc, ts;
370 char **argv;
372 if (create_args("first", args, &argc, &argv) < 0)
373 return NULL;
375 if ((ts = rrd_first(argc, argv)) == -1) {
376 PyErr_SetString(ErrorObject, rrd_get_error());
377 rrd_clear_error();
378 r = NULL;
379 } else
380 r = PyInt_FromLong((long) ts);
382 destroy_args(&argv);
383 return r;
384 }
386 static char PyRRD_last__doc__[] =
387 "last(filename): Return the timestamp of the last data sample in an RRD";
389 static PyObject *PyRRD_last(
390 PyObject UNUSED(*self),
391 PyObject * args)
392 {
393 PyObject *r;
394 int argc, ts;
395 char **argv;
397 if (create_args("last", args, &argc, &argv) < 0)
398 return NULL;
400 if ((ts = rrd_last(argc, argv)) == -1) {
401 PyErr_SetString(ErrorObject, rrd_get_error());
402 rrd_clear_error();
403 r = NULL;
404 } else
405 r = PyInt_FromLong((long) ts);
407 destroy_args(&argv);
408 return r;
409 }
411 static char PyRRD_resize__doc__[] =
412 "resize(args...): alters the size of an RRA.\n"
413 " resize filename rra-num GROW|SHRINK rows";
415 static PyObject *PyRRD_resize(
416 PyObject UNUSED(*self),
417 PyObject * args)
418 {
419 PyObject *r;
420 char **argv;
421 int argc, ts;
423 if (create_args("resize", args, &argc, &argv) < 0)
424 return NULL;
426 if ((ts = rrd_resize(argc, argv)) == -1) {
427 PyErr_SetString(ErrorObject, rrd_get_error());
428 rrd_clear_error();
429 r = NULL;
430 } else {
431 Py_INCREF(Py_None);
432 r = Py_None;
433 }
435 destroy_args(&argv);
436 return r;
437 }
439 static PyObject *PyDict_FromInfo(
440 rrd_info_t * data)
441 {
442 PyObject *r;
444 r = PyDict_New();
445 while (data) {
446 PyObject *val = NULL;
448 switch (data->type) {
449 case RD_I_VAL:
450 val = isnan(data->value.u_val)
451 ? (Py_INCREF(Py_None), Py_None)
452 : PyFloat_FromDouble(data->value.u_val);
453 break;
454 case RD_I_CNT:
455 val = PyLong_FromUnsignedLong(data->value.u_cnt);
456 break;
457 case RD_I_INT:
458 val = PyLong_FromLong(data->value.u_int);
459 break;
460 case RD_I_STR:
461 val = PyString_FromString(data->value.u_str);
462 break;
463 case RD_I_BLO:
464 val =
465 PyString_FromStringAndSize((char *) data->value.u_blo.ptr,
466 data->value.u_blo.size);
467 break;
468 }
469 if (val) {
470 PyDict_SetItemString(r, data->key, val);
471 Py_DECREF(val);
472 }
473 data = data->next;
474 }
475 return r;
476 }
478 static char PyRRD_info__doc__[] =
479 "info(filename): extract header information from an rrd";
481 static PyObject *PyRRD_info(
482 PyObject UNUSED(*self),
483 PyObject * args)
484 {
485 PyObject *r;
486 int argc;
487 char **argv;
488 rrd_info_t *data;
490 if (create_args("info", args, &argc, &argv) < 0)
491 return NULL;
493 if ((data = rrd_info(argc, argv)) == NULL) {
494 PyErr_SetString(ErrorObject, rrd_get_error());
495 rrd_clear_error();
496 r = NULL;
497 } else {
498 r = PyDict_FromInfo(data);
499 rrd_info_free(data);
500 }
502 destroy_args(&argv);
503 return r;
504 }
506 static char PyRRD_graphv__doc__[] =
507 "graphv is called in the same manner as graph";
509 static PyObject *PyRRD_graphv(
510 PyObject UNUSED(*self),
511 PyObject * args)
512 {
513 PyObject *r;
514 int argc;
515 char **argv;
516 rrd_info_t *data;
518 if (create_args("graphv", args, &argc, &argv) < 0)
519 return NULL;
521 if ((data = rrd_graph_v(argc, argv)) == NULL) {
522 PyErr_SetString(ErrorObject, rrd_get_error());
523 rrd_clear_error();
524 r = NULL;
525 } else {
526 r = PyDict_FromInfo(data);
527 rrd_info_free(data);
528 }
530 destroy_args(&argv);
531 return r;
532 }
534 static char PyRRD_updatev__doc__[] =
535 "updatev is called in the same manner as update";
537 static PyObject *PyRRD_updatev(
538 PyObject UNUSED(*self),
539 PyObject * args)
540 {
541 PyObject *r;
542 int argc;
543 char **argv;
544 rrd_info_t *data;
546 if (create_args("updatev", args, &argc, &argv) < 0)
547 return NULL;
549 if ((data = rrd_update_v(argc, argv)) == NULL) {
550 PyErr_SetString(ErrorObject, rrd_get_error());
551 rrd_clear_error();
552 r = NULL;
553 } else {
554 r = PyDict_FromInfo(data);
555 rrd_info_free(data);
556 }
558 destroy_args(&argv);
559 return r;
560 }
562 static char PyRRD_flushcached__doc__[] =
563 "flush(args..): flush RRD files from memory\n"
564 " flush [--daemon address] file [file ...]";
566 static PyObject *PyRRD_flushcached(
567 PyObject UNUSED(*self),
568 PyObject * args)
569 {
570 PyObject *r;
571 int argc;
572 char **argv;
574 if (create_args("flushcached", args, &argc, &argv) < 0)
575 return NULL;
577 if (rrd_flushcached(argc, argv) != 0) {
578 PyErr_SetString(ErrorObject, rrd_get_error());
579 rrd_clear_error();
580 r = NULL;
581 } else {
582 Py_INCREF(Py_None);
583 r = Py_None;
584 }
586 destroy_args(&argv);
587 return r;
588 }
590 static char PyRRD_xport__doc__[] =
591 "xport(args..): dictionary representation of data stored in RRDs\n"
592 " [-s|--start seconds] [-e|--end seconds] [-m|--maxrows rows]"
593 "[--step value] [--daemon address] [DEF:vname=rrd:ds-name:CF]"
594 "[CDEF:vname=rpn-expression] [XPORT:vname[:legend]]";
597 static PyObject *PyRRD_xport(
598 PyObject UNUSED(*self),
599 PyObject * args)
600 {
601 PyObject *r;
602 int argc, xsize;
603 char **argv, **legend_v;
604 time_t start, end;
605 unsigned long step, col_cnt;
606 rrd_value_t *data, *datai;
608 if (create_args("xport", args, &argc, &argv) < 0)
609 return NULL;
611 if (rrd_xport(argc, argv, &xsize, &start, &end,
612 &step, &col_cnt, &legend_v, &data) == -1) {
613 PyErr_SetString(ErrorObject, rrd_get_error());
614 rrd_clear_error();
615 r = NULL;
616 } else {
617 PyObject *meta_dict, *data_list, *legend_list, *t;
618 unsigned long i, j;
619 rrd_value_t dv;
621 unsigned long row_cnt = ((end - start) / step) + 1;
623 r = PyDict_New();
624 meta_dict = PyDict_New();
625 legend_list = PyList_New(col_cnt);
626 data_list = PyList_New(row_cnt);
627 PyDict_SetItem(r, PyString_FromString("meta"), meta_dict);
628 PyDict_SetItem(r, PyString_FromString("data"), data_list);
630 datai = data;
632 PyDict_SetItem(meta_dict, PyString_FromString("start"), PyInt_FromLong((long) start));
633 PyDict_SetItem(meta_dict, PyString_FromString("end"), PyInt_FromLong((long) end));
634 PyDict_SetItem(meta_dict, PyString_FromString("step"), PyInt_FromLong((long) step));
635 PyDict_SetItem(meta_dict, PyString_FromString("rows"), PyInt_FromLong((long) row_cnt));
636 PyDict_SetItem(meta_dict, PyString_FromString("columns"), PyInt_FromLong((long) col_cnt));
637 PyDict_SetItem(meta_dict, PyString_FromString("legend"), legend_list);
639 for (i = 0; i < col_cnt; i++) {
640 PyList_SET_ITEM(legend_list, i, PyString_FromString(legend_v[i]));
641 }
643 for (i = 0; i < row_cnt; i++) {
644 t = PyTuple_New(col_cnt);
645 PyList_SET_ITEM(data_list, i, t);
647 for (j = 0; j < col_cnt; j++) {
648 dv = *(datai++);
649 if (isnan(dv)) {
650 PyTuple_SET_ITEM(t, j, Py_None);
651 Py_INCREF(Py_None);
652 } else {
653 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
654 }
655 }
656 }
658 for (i = 0; i < col_cnt; i++) {
659 rrd_freemem(legend_v[i]);
660 }
661 rrd_freemem(legend_v);
662 rrd_freemem(data);
663 }
664 destroy_args(&argv);
665 return r;
666 }
668 /* List of methods defined in the module */
669 #define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
671 static PyMethodDef _rrdtool_methods[] = {
672 meth("create", PyRRD_create, PyRRD_create__doc__),
673 meth("update", PyRRD_update, PyRRD_update__doc__),
674 meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__),
675 meth("graph", PyRRD_graph, PyRRD_graph__doc__),
676 meth("tune", PyRRD_tune, PyRRD_tune__doc__),
677 meth("first", PyRRD_first, PyRRD_first__doc__),
678 meth("last", PyRRD_last, PyRRD_last__doc__),
679 meth("resize", PyRRD_resize, PyRRD_resize__doc__),
680 meth("info", PyRRD_info, PyRRD_info__doc__),
681 meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__),
682 meth("updatev", PyRRD_updatev, PyRRD_updatev__doc__),
683 meth("flushcached", PyRRD_flushcached, PyRRD_flushcached__doc__),
684 meth("xport", PyRRD_xport, PyRRD_xport__doc__),
685 {NULL, NULL, 0, NULL}
686 };
688 #define SET_INTCONSTANT(dict, value) \
689 t = PyInt_FromLong((long)value); \
690 PyDict_SetItemString(dict, #value, t); \
691 Py_DECREF(t);
692 #define SET_STRCONSTANT(dict, value) \
693 t = PyString_FromString(value); \
694 PyDict_SetItemString(dict, #value, t); \
695 Py_DECREF(t);
697 /* Initialization function for the module */
698 void initrrdtool(
699 void)
700 {
701 PyObject *m, *d, *t;
703 /* Create the module and add the functions */
704 m = Py_InitModule("rrdtool", _rrdtool_methods);
706 /* Add some symbolic constants to the module */
707 d = PyModule_GetDict(m);
709 SET_STRCONSTANT(d, __version__);
710 ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
711 PyDict_SetItemString(d, "error", ErrorObject);
713 /* Check for errors */
714 if (PyErr_Occurred())
715 Py_FatalError("can't initialize the rrdtool module");
716 }
718 /*
719 * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
720 * ex: ts=8 sts=4 et
721 */