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