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