integrated fix for debian bug #529291 as supplied by Anders Hammarquist
[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, *lo;
62     int       args_count,
63               argv_count,
64               element_count,
65               i, j;
66
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     }
80    
81     *argv = PyMem_New(char *,
82                       element_count + 1);
83
84     if (*argv == NULL)
85         return -1;
86
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     }
111
112     (*argv)[0] = command;
113     *argc = element_count + 1;
114
115     /* reset getopt state */
116     opterr = optind = 0;
117
118     return 0;
119 }
120
121 static void destroy_args(
122     char ***argv)
123 {
124     PyMem_Del(*argv);
125     *argv = NULL;
126 }
127
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]";
133
134 static PyObject *PyRRD_create(
135     PyObject UNUSED(*self),
136     PyObject * args)
137 {
138     PyObject *r;
139     char    **argv;
140     int       argc;
141
142     if (create_args("create", args, &argc, &argv) < 0)
143         return NULL;
144
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     }
153
154     destroy_args(&argv);
155     return r;
156 }
157
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...] ...]";
162
163 static PyObject *PyRRD_update(
164     PyObject UNUSED(*self),
165     PyObject * args)
166 {
167     PyObject *r;
168     char    **argv;
169     int       argc;
170
171     if (create_args("update", args, &argc, &argv) < 0)
172         return NULL;
173
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     }
182
183     destroy_args(&argv);
184     return r;
185 }
186
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]";
191
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;
202
203     if (create_args("fetch", args, &argc, &argv) < 0)
204         return NULL;
205
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;
217
218         row = (end - start) / step;
219
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);
227
228         datai = data;
229
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));
233
234         for (i = 0; i < ds_cnt; i++)
235             PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
236
237         for (i = 0; i < row; i++) {
238             t = PyTuple_New(ds_cnt);
239             PyList_SET_ITEM(data_list, i, t);
240
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         }
251
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     }
257
258     destroy_args(&argv);
259     return r;
260 }
261
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]]]";
282
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;
291
292     if (create_args("graph", args, &argc, &argv) < 0)
293         return NULL;
294
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);
302
303         PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long) xsize));
304         PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long) ysize));
305
306         if (calcpr) {
307             PyObject *e, *t;
308
309             e = PyList_New(0);
310             PyTuple_SET_ITEM(r, 2, e);
311
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     }
324
325     destroy_args(&argv);
326     return r;
327 }
328
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]";
334
335 static PyObject *PyRRD_tune(
336     PyObject UNUSED(*self),
337     PyObject * args)
338 {
339     PyObject *r;
340     char    **argv;
341     int       argc;
342
343     if (create_args("tune", args, &argc, &argv) < 0)
344         return NULL;
345
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     }
354
355     destroy_args(&argv);
356     return r;
357 }
358
359 static char PyRRD_first__doc__[] =
360     "first(filename): Return the timestamp of the first data sample in an RRD";
361
362 static PyObject *PyRRD_first(
363     PyObject UNUSED(*self),
364     PyObject * args)
365 {
366     PyObject *r;
367     int       argc, ts;
368     char    **argv;
369
370     if (create_args("first", args, &argc, &argv) < 0)
371         return NULL;
372
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);
379
380     destroy_args(&argv);
381     return r;
382 }
383
384 static char PyRRD_last__doc__[] =
385     "last(filename): Return the timestamp of the last data sample in an RRD";
386
387 static PyObject *PyRRD_last(
388     PyObject UNUSED(*self),
389     PyObject * args)
390 {
391     PyObject *r;
392     int       argc, ts;
393     char    **argv;
394
395     if (create_args("last", args, &argc, &argv) < 0)
396         return NULL;
397
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);
404
405     destroy_args(&argv);
406     return r;
407 }
408
409 static char PyRRD_resize__doc__[] =
410     "resize(args...): alters the size of an RRA.\n"
411     "    resize filename rra-num GROW|SHRINK rows";
412
413 static PyObject *PyRRD_resize(
414     PyObject UNUSED(*self),
415     PyObject * args)
416 {
417     PyObject *r;
418     char    **argv;
419     int       argc, ts;
420
421     if (create_args("resize", args, &argc, &argv) < 0)
422         return NULL;
423
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     }
432
433     destroy_args(&argv);
434     return r;
435 }
436
437 static PyObject *PyDict_FromInfo(
438     rrd_info_t * data)
439 {
440     PyObject *r;
441
442     r = PyDict_New();
443     while (data) {
444         PyObject *val = NULL;
445
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 }
475
476 static char PyRRD_info__doc__[] =
477     "info(filename): extract header information from an rrd";
478
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;
487
488     if (create_args("info", args, &argc, &argv) < 0)
489         return NULL;
490
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     }
499
500     destroy_args(&argv);
501     return r;
502 }
503
504 static char PyRRD_graphv__doc__[] =
505     "graphv is called in the same manner as graph";
506
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;
515
516     if (create_args("graphv", args, &argc, &argv) < 0)
517         return NULL;
518
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     }
527
528     destroy_args(&argv);
529     return r;
530 }
531
532 static char PyRRD_updatev__doc__[] =
533     "updatev is called in the same manner as update";
534
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;
543
544     if (create_args("updatev", args, &argc, &argv) < 0)
545         return NULL;
546
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     }
555
556     destroy_args(&argv);
557     return r;
558 }
559
560 static char PyRRD_flush__doc__[] =
561   "flush(args..): flush RRD files from memory\n"
562   "   flush [--daemon address] file [file ...]";
563
564 static PyObject *PyRRD_flush(
565     PyObject UNUSED(*self),
566     PyObject * args)
567 {
568     PyObject *r;
569     int       argc;
570     char    **argv;
571
572     if (create_args("flush", args, &argc, &argv) < 0)
573         return NULL;
574
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     }
583
584     destroy_args(&argv);
585     return r;
586 }
587
588 /* List of methods defined in the module */
589 #define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
590
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 };
606
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);
615
616 /* Initialization function for the module */
617 void initrrdtool(
618     void)
619 {
620     PyObject *m, *d, *t;
621
622     /* Create the module and add the functions */
623     m = Py_InitModule("rrdtool", _rrdtool_methods);
624
625     /* Add some symbolic constants to the module */
626     d = PyModule_GetDict(m);
627
628     SET_STRCONSTANT(d, __version__);
629     ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
630     PyDict_SetItemString(d, "error", ErrorObject);
631
632     /* Check for errors */
633     if (PyErr_Occurred())
634         Py_FatalError("can't initialize the rrdtool module");
635 }
636
637 /*
638  * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
639  * ex: ts=8 sts=4 et
640  */