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