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