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 */
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
40 static const char *__version__ = "$Revision: 1.14 $";
42 #include "Python.h"
43 #include "rrd.h"
44 #include "rrd_extra.h"
45 #include "rrd_rpncalc.h"
47 static PyObject *ErrorObject;
48 extern int optind;
49 extern int opterr;
51 /* forward declaration to keep compiler happy */
52 void initrrdtool(void);
54 static int
55 create_args(char *command, PyObject *args, int *argc, char ***argv)
56 {
57 PyObject *o;
58 int size, i;
60 size = PyTuple_Size(args);
61 *argv = PyMem_New(char *, size + 1);
62 if (*argv == NULL)
63 return -1;
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;
78 /* reset getopt state */
79 opterr = optind = 0;
81 return 0;
82 }
84 static void
85 destroy_args(char ***argv)
86 {
87 PyMem_Del(*argv);
88 *argv = NULL;
89 }
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]";
97 static PyObject *
98 PyRRD_create(PyObject UNUSED(*self), PyObject *args)
99 {
100 PyObject *r;
101 char **argv;
102 int argc;
104 if (create_args("create", args, &argc, &argv) < 0)
105 return NULL;
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 }
116 destroy_args(&argv);
117 return r;
118 }
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...] ...]";
125 static PyObject *
126 PyRRD_update(PyObject UNUSED(*self), PyObject *args)
127 {
128 PyObject *r;
129 char **argv;
130 int argc;
132 if (create_args("update", args, &argc, &argv) < 0)
133 return NULL;
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 }
144 destroy_args(&argv);
145 return r;
146 }
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]";
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;
163 if (create_args("fetch", args, &argc, &argv) < 0)
164 return NULL;
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;
178 row = (end - start) / step;
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);
188 datai = data;
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));
194 for (i = 0; i < ds_cnt; i++)
195 PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
197 for (i = 0; i < row; i ++) {
198 t = PyTuple_New(ds_cnt);
199 PyList_SET_ITEM(data_list, i, t);
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 }
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 }
218 destroy_args(&argv);
219 return r;
220 }
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]]]";
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;
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);
261 PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long)xsize));
262 PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long)ysize));
264 if (calcpr) {
265 PyObject *e, *t;
267 e = PyList_New(0);
268 PyTuple_SET_ITEM(r, 2, e);
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 }
283 destroy_args(&argv);
284 return r;
285 }
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]";
293 static PyObject *
294 PyRRD_tune(PyObject UNUSED(*self), PyObject *args)
295 {
296 PyObject *r;
297 char **argv;
298 int argc;
300 if (create_args("tune", args, &argc, &argv) < 0)
301 return NULL;
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 }
312 destroy_args(&argv);
313 return r;
314 }
316 static char PyRRD_first__doc__[] =
317 "first(filename): Return the timestamp of the first data sample in an RRD";
319 static PyObject *
320 PyRRD_first(PyObject UNUSED(*self), PyObject *args)
321 {
322 PyObject *r;
323 int argc, ts;
324 char **argv;
326 if (create_args("first", args, &argc, &argv) < 0)
327 return NULL;
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);
336 destroy_args(&argv);
337 return r;
338 }
340 static char PyRRD_last__doc__[] =
341 "last(filename): Return the timestamp of the last data sample in an RRD";
343 static PyObject *
344 PyRRD_last(PyObject UNUSED(*self), PyObject *args)
345 {
346 PyObject *r;
347 int argc, ts;
348 char **argv;
350 if (create_args("last", args, &argc, &argv) < 0)
351 return NULL;
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);
360 destroy_args(&argv);
361 return r;
362 }
364 static char PyRRD_resize__doc__[] =
365 "resize(args...): alters the size of an RRA.\n"
366 " resize filename rra-num GROW|SHRINK rows";
368 static PyObject *
369 PyRRD_resize(PyObject UNUSED(*self), PyObject *args)
370 {
371 PyObject *r;
372 char **argv;
373 int argc, ts;
375 if (create_args("resize", args, &argc, &argv) < 0)
376 return NULL;
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 }
387 destroy_args(&argv);
388 return r;
389 }
391 static char PyRRD_info__doc__[] =
392 "info(filename): extract header information from an rrd";
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;
403 if (! PyArg_ParseTuple(args, "s:info", &filename))
404 return NULL;
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);
413 #define DICTSET_STR(dict, name, value) \
414 t = PyString_FromString(value); \
415 PyDict_SetItemString(dict, name, t); \
416 Py_DECREF(t);
418 #define DICTSET_CNT(dict, name, value) \
419 t = PyInt_FromLong((long)value); \
420 PyDict_SetItemString(dict, name, t); \
421 Py_DECREF(t);
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);
429 r = PyDict_New();
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);
436 ds = PyDict_New();
437 PyDict_SetItemString(r, "ds", ds);
438 Py_DECREF(ds);
440 for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
441 PyObject *d;
443 d = PyDict_New();
444 PyDict_SetItemString(ds, rrd.ds_def[i].ds_nam, d);
445 Py_DECREF(d);
447 DICTSET_STR(d, "ds_name", rrd.ds_def[i].ds_nam);
448 DICTSET_STR(d, "type", rrd.ds_def[i].dst);
450 switch(dst_conv(rrd.ds_def[i].dst)) {
451 case DST_CDEF:
452 {
453 char *buffer = NULL;
454 rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
455 rrd.ds_def, &buffer);
456 DICTSET_STR(d, "cdef", buffer);
457 }
458 break;
459 default:
460 DICTSET_CNT(d, "minimal_heartbeat", rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
461 DICTSET_VAL(d, "min", rrd.ds_def[i].par[DS_min_val].u_val);
462 DICTSET_VAL(d, "max", rrd.ds_def[i].par[DS_max_val].u_val);
463 break;
464 }
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", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
468 }
470 ds = PyList_New(rrd.stat_head->rra_cnt);
471 PyDict_SetItemString(r, "rra", ds);
472 Py_DECREF(ds);
474 for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
475 PyObject *d, *cdp;
477 d = PyDict_New();
478 PyList_SET_ITEM(ds, i, d);
480 DICTSET_STR(d, "cf", rrd.rra_def[i].cf_nam);
481 DICTSET_CNT(d, "rows", rrd.rra_def[i].row_cnt);
482 DICTSET_CNT(d, "pdp_per_row", rrd.rra_def[i].pdp_cnt);
483 DICTSET_VAL(d, "xff", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
485 cdp = PyList_New(rrd.stat_head->ds_cnt);
486 PyDict_SetItemString(d, "cdp_prep", cdp);
487 Py_DECREF(cdp);
489 for (j = 0; j < rrd.stat_head->ds_cnt; j++) {
490 PyObject *cdd;
492 cdd = PyDict_New();
493 PyList_SET_ITEM(cdp, j, cdd);
495 DICTSET_VAL(cdd, "value",
496 rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_val].u_val);
497 DICTSET_CNT(cdd, "unknown_datapoints",
498 rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_unkn_pdp_cnt].u_cnt);
499 }
500 }
502 rrd_free(&rrd);
504 return r;
505 }
507 /* List of methods defined in the module */
508 #define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
510 static PyMethodDef _rrdtool_methods[] = {
511 meth("create", PyRRD_create, PyRRD_create__doc__),
512 meth("update", PyRRD_update, PyRRD_update__doc__),
513 meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__),
514 meth("graph", PyRRD_graph, PyRRD_graph__doc__),
515 meth("tune", PyRRD_tune, PyRRD_tune__doc__),
516 meth("first", PyRRD_first, PyRRD_first__doc__),
517 meth("last", PyRRD_last, PyRRD_last__doc__),
518 meth("resize", PyRRD_resize, PyRRD_resize__doc__),
519 meth("info", PyRRD_info, PyRRD_info__doc__),
520 {NULL, NULL,0,NULL}
521 };
523 #define SET_INTCONSTANT(dict, value) \
524 t = PyInt_FromLong((long)value); \
525 PyDict_SetItemString(dict, #value, t); \
526 Py_DECREF(t);
527 #define SET_STRCONSTANT(dict, value) \
528 t = PyString_FromString(value); \
529 PyDict_SetItemString(dict, #value, t); \
530 Py_DECREF(t);
532 /* Initialization function for the module */
533 void
534 initrrdtool(void)
535 {
536 PyObject *m, *d, *t;
538 /* Create the module and add the functions */
539 m = Py_InitModule("rrdtool", _rrdtool_methods);
541 /* Add some symbolic constants to the module */
542 d = PyModule_GetDict(m);
544 SET_STRCONSTANT(d, __version__);
545 ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
546 PyDict_SetItemString(d, "error", ErrorObject);
548 /* Check for errors */
549 if (PyErr_Occurred())
550 Py_FatalError("can't initialize the rrdtool module");
551 }
553 /*
554 * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
555 * ex: ts=8 sts=4 et
556 */