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