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
40 static const char *__version__ = "$Revision: 1.14 $";
42 #include "Python.h"
43 #include "../../src/rrd_tool.h"
44 //#include "rrd.h"
45 //#include "rrd_extra.h"
47 static PyObject *ErrorObject;
48 extern int optind;
49 extern int opterr;
51 /* forward declaration to keep compiler happy */
52 void initrrdtool(
53 void);
55 static int create_args(
56 char *command,
57 PyObject * args,
58 int *argc,
59 char ***argv)
60 {
61 PyObject *o, *lo;
62 int args_count,
63 argv_count,
64 element_count,
65 i, j;
67 args_count = PyTuple_Size(args);
68 element_count = 0;
69 for (i = 0; i < args_count; i++) {
70 o = PyTuple_GET_ITEM(args, i);
71 if (PyString_Check(o))
72 element_count++;
73 else if (PyList_CheckExact(o))
74 element_count += PyList_Size(o);
75 else {
76 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
77 return -1;
78 }
79 }
81 *argv = PyMem_New(char *,
82 element_count + 1);
84 if (*argv == NULL)
85 return -1;
87 argv_count = 0;
88 for (i = 0; i < args_count; i++) {
89 o = PyTuple_GET_ITEM(args, i);
90 if (PyString_Check(o)) {
91 argv_count++;
92 (*argv)[argv_count] = PyString_AS_STRING(o);
93 } else if (PyList_CheckExact(o))
94 for (j = 0; j < PyList_Size(o); j++) {
95 lo = PyList_GetItem(o, j);
96 if (PyString_Check(lo)) {
97 argv_count++;
98 (*argv)[argv_count] = PyString_AS_STRING(lo);
99 } else {
100 PyMem_Del(*argv);
101 PyErr_Format(PyExc_TypeError, "element %d in argument %d must be string", j, i);
102 return -1;
103 }
104 }
105 else {
106 PyMem_Del(*argv);
107 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
108 return -1;
109 }
110 }
112 (*argv)[0] = command;
113 *argc = element_count + 1;
115 /* reset getopt state */
116 opterr = optind = 0;
118 return 0;
119 }
121 static void destroy_args(
122 char ***argv)
123 {
124 PyMem_Del(*argv);
125 *argv = NULL;
126 }
128 static char PyRRD_create__doc__[] =
129 "create(args..): Set up a new Round Robin Database\n\
130 create filename [--start|-b start time] \
131 [--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \
132 [RRA:CF:xff:steps:rows]";
134 static PyObject *PyRRD_create(
135 PyObject UNUSED(*self),
136 PyObject * args)
137 {
138 PyObject *r;
139 char **argv;
140 int argc;
142 if (create_args("create", args, &argc, &argv) < 0)
143 return NULL;
145 if (rrd_create(argc, argv) == -1) {
146 PyErr_SetString(ErrorObject, rrd_get_error());
147 rrd_clear_error();
148 r = NULL;
149 } else {
150 Py_INCREF(Py_None);
151 r = Py_None;
152 }
154 destroy_args(&argv);
155 return r;
156 }
158 static char PyRRD_update__doc__[] =
159 "update(args..): Store a new set of values into the rrd\n"
160 " update filename [--template|-t ds-name[:ds-name]...] "
161 "N|timestamp:value[:value...] [timestamp:value[:value...] ...]";
163 static PyObject *PyRRD_update(
164 PyObject UNUSED(*self),
165 PyObject * args)
166 {
167 PyObject *r;
168 char **argv;
169 int argc;
171 if (create_args("update", args, &argc, &argv) < 0)
172 return NULL;
174 if (rrd_update(argc, argv) == -1) {
175 PyErr_SetString(ErrorObject, rrd_get_error());
176 rrd_clear_error();
177 r = NULL;
178 } else {
179 Py_INCREF(Py_None);
180 r = Py_None;
181 }
183 destroy_args(&argv);
184 return r;
185 }
187 static char PyRRD_fetch__doc__[] =
188 "fetch(args..): fetch data from an rrd.\n"
189 " fetch filename CF [--resolution|-r resolution] "
190 "[--start|-s start] [--end|-e end]";
192 static PyObject *PyRRD_fetch(
193 PyObject UNUSED(*self),
194 PyObject * args)
195 {
196 PyObject *r;
197 rrd_value_t *data, *datai;
198 unsigned long step, ds_cnt;
199 time_t start, end;
200 int argc;
201 char **argv, **ds_namv;
203 if (create_args("fetch", args, &argc, &argv) < 0)
204 return NULL;
206 if (rrd_fetch(argc, argv, &start, &end, &step,
207 &ds_cnt, &ds_namv, &data) == -1) {
208 PyErr_SetString(ErrorObject, rrd_get_error());
209 rrd_clear_error();
210 r = NULL;
211 } else {
212 /* Return :
213 ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */
214 PyObject *range_tup, *dsnam_tup, *data_list, *t;
215 unsigned long i, j, row;
216 rrd_value_t dv;
218 row = (end - start) / step;
220 r = PyTuple_New(3);
221 range_tup = PyTuple_New(3);
222 dsnam_tup = PyTuple_New(ds_cnt);
223 data_list = PyList_New(row);
224 PyTuple_SET_ITEM(r, 0, range_tup);
225 PyTuple_SET_ITEM(r, 1, dsnam_tup);
226 PyTuple_SET_ITEM(r, 2, data_list);
228 datai = data;
230 PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long) start));
231 PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long) end));
232 PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long) step));
234 for (i = 0; i < ds_cnt; i++)
235 PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
237 for (i = 0; i < row; i++) {
238 t = PyTuple_New(ds_cnt);
239 PyList_SET_ITEM(data_list, i, t);
241 for (j = 0; j < ds_cnt; j++) {
242 dv = *(datai++);
243 if (isnan(dv)) {
244 PyTuple_SET_ITEM(t, j, Py_None);
245 Py_INCREF(Py_None);
246 } else {
247 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
248 }
249 }
250 }
252 for (i = 0; i < ds_cnt; i++)
253 rrd_freemem(ds_namv[i]);
254 rrd_freemem(ds_namv); /* rrdtool don't use PyMem_Malloc :) */
255 rrd_freemem(data);
256 }
258 destroy_args(&argv);
259 return r;
260 }
262 static char PyRRD_graph__doc__[] =
263 "graph(args..): Create a graph based on data from one or several RRD\n"
264 " graph filename [-s|--start seconds] "
265 "[-e|--end seconds] [-x|--x-grid x-axis grid and label] "
266 "[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] "
267 "[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value "
268 "[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] "
269 "[-i|--interlaced] "
270 "[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] "
271 "[-B|--background value] [-O|--overlay value] "
272 "[-U|--unit value] [-z|--lazy] [-o|--logarithmic] "
273 "[-u|--upper-limit value] [-l|--lower-limit value] "
274 "[-g|--no-legend] [-r|--rigid] [--step value] "
275 "[-b|--base value] [-c|--color COLORTAG#rrggbb] "
276 "[-t|--title title] [DEF:vname=rrd:ds-name:CF] "
277 "[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] "
278 "[GPRINT:vname:CF:format] [COMMENT:text] "
279 "[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] "
280 "[LINE{1|2|3}:vname[#rrggbb[:legend]]] "
281 "[AREA:vname[#rrggbb[:legend]]] " "[STACK:vname[#rrggbb[:legend]]]";
283 static PyObject *PyRRD_graph(
284 PyObject UNUSED(*self),
285 PyObject * args)
286 {
287 PyObject *r;
288 char **argv, **calcpr;
289 int argc, xsize, ysize, i;
290 double ymin, ymax;
292 if (create_args("graph", args, &argc, &argv) < 0)
293 return NULL;
295 if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) ==
296 -1) {
297 PyErr_SetString(ErrorObject, rrd_get_error());
298 rrd_clear_error();
299 r = NULL;
300 } else {
301 r = PyTuple_New(3);
303 PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) xsize));
304 PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long) ysize));
306 if (calcpr) {
307 PyObject *e, *t;
309 e = PyList_New(0);
310 PyTuple_SET_ITEM(r, 2, e);
312 for (i = 0; calcpr[i]; i++) {
313 t = PyString_FromString(calcpr[i]);
314 PyList_Append(e, t);
315 Py_DECREF(t);
316 rrd_freemem(calcpr[i]);
317 }
318 rrd_freemem(calcpr);
319 } else {
320 Py_INCREF(Py_None);
321 PyTuple_SET_ITEM(r, 2, Py_None);
322 }
323 }
325 destroy_args(&argv);
326 return r;
327 }
329 static char PyRRD_tune__doc__[] =
330 "tune(args...): Modify some basic properties of a Round Robin Database\n"
331 " tune filename [--heartbeat|-h ds-name:heartbeat] "
332 "[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] "
333 "[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]";
335 static PyObject *PyRRD_tune(
336 PyObject UNUSED(*self),
337 PyObject * args)
338 {
339 PyObject *r;
340 char **argv;
341 int argc;
343 if (create_args("tune", args, &argc, &argv) < 0)
344 return NULL;
346 if (rrd_tune(argc, argv) == -1) {
347 PyErr_SetString(ErrorObject, rrd_get_error());
348 rrd_clear_error();
349 r = NULL;
350 } else {
351 Py_INCREF(Py_None);
352 r = Py_None;
353 }
355 destroy_args(&argv);
356 return r;
357 }
359 static char PyRRD_first__doc__[] =
360 "first(filename): Return the timestamp of the first data sample in an RRD";
362 static PyObject *PyRRD_first(
363 PyObject UNUSED(*self),
364 PyObject * args)
365 {
366 PyObject *r;
367 int argc, ts;
368 char **argv;
370 if (create_args("first", args, &argc, &argv) < 0)
371 return NULL;
373 if ((ts = rrd_first(argc, argv)) == -1) {
374 PyErr_SetString(ErrorObject, rrd_get_error());
375 rrd_clear_error();
376 r = NULL;
377 } else
378 r = PyInt_FromLong((long) ts);
380 destroy_args(&argv);
381 return r;
382 }
384 static char PyRRD_last__doc__[] =
385 "last(filename): Return the timestamp of the last data sample in an RRD";
387 static PyObject *PyRRD_last(
388 PyObject UNUSED(*self),
389 PyObject * args)
390 {
391 PyObject *r;
392 int argc, ts;
393 char **argv;
395 if (create_args("last", args, &argc, &argv) < 0)
396 return NULL;
398 if ((ts = rrd_last(argc, argv)) == -1) {
399 PyErr_SetString(ErrorObject, rrd_get_error());
400 rrd_clear_error();
401 r = NULL;
402 } else
403 r = PyInt_FromLong((long) ts);
405 destroy_args(&argv);
406 return r;
407 }
409 static char PyRRD_resize__doc__[] =
410 "resize(args...): alters the size of an RRA.\n"
411 " resize filename rra-num GROW|SHRINK rows";
413 static PyObject *PyRRD_resize(
414 PyObject UNUSED(*self),
415 PyObject * args)
416 {
417 PyObject *r;
418 char **argv;
419 int argc, ts;
421 if (create_args("resize", args, &argc, &argv) < 0)
422 return NULL;
424 if ((ts = rrd_resize(argc, argv)) == -1) {
425 PyErr_SetString(ErrorObject, rrd_get_error());
426 rrd_clear_error();
427 r = NULL;
428 } else {
429 Py_INCREF(Py_None);
430 r = Py_None;
431 }
433 destroy_args(&argv);
434 return r;
435 }
437 static PyObject *PyDict_FromInfo(
438 rrd_info_t * data)
439 {
440 PyObject *r;
442 r = PyDict_New();
443 while (data) {
444 PyObject *val = NULL;
446 switch (data->type) {
447 case RD_I_VAL:
448 val = isnan(data->value.u_val)
449 ? (Py_INCREF(Py_None), Py_None)
450 : PyFloat_FromDouble(data->value.u_val);
451 break;
452 case RD_I_CNT:
453 val = PyLong_FromUnsignedLong(data->value.u_cnt);
454 break;
455 case RD_I_INT:
456 val = PyLong_FromLong(data->value.u_int);
457 break;
458 case RD_I_STR:
459 val = PyString_FromString(data->value.u_str);
460 break;
461 case RD_I_BLO:
462 val =
463 PyString_FromStringAndSize((char *) data->value.u_blo.ptr,
464 data->value.u_blo.size);
465 break;
466 }
467 if (val) {
468 PyDict_SetItemString(r, data->key, val);
469 Py_DECREF(val);
470 }
471 data = data->next;
472 }
473 return r;
474 }
476 static char PyRRD_info__doc__[] =
477 "info(filename): extract header information from an rrd";
479 static PyObject *PyRRD_info(
480 PyObject UNUSED(*self),
481 PyObject * args)
482 {
483 PyObject *r;
484 int argc;
485 char **argv;
486 rrd_info_t *data;
488 if (create_args("info", args, &argc, &argv) < 0)
489 return NULL;
491 if ((data = rrd_info(argc, argv)) == NULL) {
492 PyErr_SetString(ErrorObject, rrd_get_error());
493 rrd_clear_error();
494 r = NULL;
495 } else {
496 r = PyDict_FromInfo(data);
497 rrd_info_free(data);
498 }
500 destroy_args(&argv);
501 return r;
502 }
504 static char PyRRD_graphv__doc__[] =
505 "graphv is called in the same manner as graph";
507 static PyObject *PyRRD_graphv(
508 PyObject UNUSED(*self),
509 PyObject * args)
510 {
511 PyObject *r;
512 int argc;
513 char **argv;
514 rrd_info_t *data;
516 if (create_args("graphv", args, &argc, &argv) < 0)
517 return NULL;
519 if ((data = rrd_graph_v(argc, argv)) == NULL) {
520 PyErr_SetString(ErrorObject, rrd_get_error());
521 rrd_clear_error();
522 r = NULL;
523 } else {
524 r = PyDict_FromInfo(data);
525 rrd_info_free(data);
526 }
528 destroy_args(&argv);
529 return r;
530 }
532 static char PyRRD_updatev__doc__[] =
533 "updatev is called in the same manner as update";
535 static PyObject *PyRRD_updatev(
536 PyObject UNUSED(*self),
537 PyObject * args)
538 {
539 PyObject *r;
540 int argc;
541 char **argv;
542 rrd_info_t *data;
544 if (create_args("updatev", args, &argc, &argv) < 0)
545 return NULL;
547 if ((data = rrd_update_v(argc, argv)) == NULL) {
548 PyErr_SetString(ErrorObject, rrd_get_error());
549 rrd_clear_error();
550 r = NULL;
551 } else {
552 r = PyDict_FromInfo(data);
553 rrd_info_free(data);
554 }
556 destroy_args(&argv);
557 return r;
558 }
560 static char PyRRD_flush__doc__[] =
561 "flush(args..): flush RRD files from memory\n"
562 " flush [--daemon address] file [file ...]";
564 static PyObject *PyRRD_flush(
565 PyObject UNUSED(*self),
566 PyObject * args)
567 {
568 PyObject *r;
569 int argc;
570 char **argv;
572 if (create_args("flush", args, &argc, &argv) < 0)
573 return NULL;
575 if (rrd_cmd_flush(argc, argv) != 0) {
576 PyErr_SetString(ErrorObject, rrd_get_error());
577 rrd_clear_error();
578 r = NULL;
579 } else {
580 Py_INCREF(Py_None);
581 r = Py_None;
582 }
584 destroy_args(&argv);
585 return r;
586 }
588 /* List of methods defined in the module */
589 #define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
591 static PyMethodDef _rrdtool_methods[] = {
592 meth("create", PyRRD_create, PyRRD_create__doc__),
593 meth("update", PyRRD_update, PyRRD_update__doc__),
594 meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__),
595 meth("graph", PyRRD_graph, PyRRD_graph__doc__),
596 meth("tune", PyRRD_tune, PyRRD_tune__doc__),
597 meth("first", PyRRD_first, PyRRD_first__doc__),
598 meth("last", PyRRD_last, PyRRD_last__doc__),
599 meth("resize", PyRRD_resize, PyRRD_resize__doc__),
600 meth("info", PyRRD_info, PyRRD_info__doc__),
601 meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__),
602 meth("updatev", PyRRD_updatev, PyRRD_updatev__doc__),
603 meth("flush", PyRRD_flush, PyRRD_flush__doc__),
604 {NULL, NULL, 0, NULL}
605 };
607 #define SET_INTCONSTANT(dict, value) \
608 t = PyInt_FromLong((long)value); \
609 PyDict_SetItemString(dict, #value, t); \
610 Py_DECREF(t);
611 #define SET_STRCONSTANT(dict, value) \
612 t = PyString_FromString(value); \
613 PyDict_SetItemString(dict, #value, t); \
614 Py_DECREF(t);
616 /* Initialization function for the module */
617 void initrrdtool(
618 void)
619 {
620 PyObject *m, *d, *t;
622 /* Create the module and add the functions */
623 m = Py_InitModule("rrdtool", _rrdtool_methods);
625 /* Add some symbolic constants to the module */
626 d = PyModule_GetDict(m);
628 SET_STRCONSTANT(d, __version__);
629 ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
630 PyDict_SetItemString(d, "error", ErrorObject);
632 /* Check for errors */
633 if (PyErr_Occurred())
634 Py_FatalError("can't initialize the rrdtool module");
635 }
637 /*
638 * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
639 * ex: ts=8 sts=4 et
640 */