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