2 * rrdtool-py3k, rrdtool bindings for Python 3.
3 * Based on the rrdtool Python bindings for Python 2 from
4 * Hye-Shik Chang <perky@fallin.lv>.
6 * Copyright 2012 Christian Jurk <commx@commx.ws>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
29 static PyObject
*rrdtool_OperationalError
;
30 static PyObject
*rrdtool_ProgrammingError
;
32 static char **rrdtool_argv
= NULL
;
33 static int rrdtool_argc
= 0;
38 PyMem_Del(rrdtool_argv
);
42 /* Helper function to convert Python objects into a representation that the
43 * rrdtool functions can work with.
46 convert_args(char *command
, PyObject
*args
)
49 int i
, j
, args_count
, argv_count
, element_count
;
51 args_count
= PyTuple_Size(args
);
53 for (i
= 0; i
< args_count
; i
++)
55 o
= PyTuple_GET_ITEM(args
, i
);
56 if (PyUnicode_Check(o
) || PyBytes_Check(o
))
58 else if (PyList_CheckExact(o
))
59 element_count
+= PyList_Size(o
);
61 PyErr_Format(rrdtool_ProgrammingError
,
62 "Argument %d must be string, bytes or list of " \
68 rrdtool_argv
= PyMem_New(char *, element_count
+ 1);
70 if (rrdtool_argv
== NULL
)
74 for (i
= 0; i
< args_count
; i
++) {
75 o
= PyTuple_GET_ITEM(args
, i
);
77 if (PyUnicode_Check(o
))
78 rrdtool_argv
[++argv_count
] = PyBytes_AsString(
79 PyUnicode_AsUTF8String(o
));
80 else if (PyBytes_Check(o
))
81 rrdtool_argv
[++argv_count
] = PyBytes_AS_STRING(o
);
82 else if (PyList_CheckExact(o
)) {
83 for (j
= 0; j
< PyList_Size(o
); j
++) {
84 lo
= PyList_GetItem(o
, j
);
85 if (PyUnicode_Check(lo
))
86 rrdtool_argv
[++argv_count
] = PyBytes_AS_STRING(
87 PyUnicode_AsUTF8String(lo
));
88 else if (PyBytes_Check(lo
))
89 rrdtool_argv
[++argv_count
] = PyBytes_AS_STRING(lo
);
91 PyMem_Del(rrdtool_argv
);
92 PyErr_Format(rrdtool_ProgrammingError
,
93 "Element %d in argument %d must be string", j
, i
);
98 PyMem_Del(rrdtool_argv
);
99 PyErr_Format(rrdtool_ProgrammingError
,
100 "Argument %d must be string or list of strings", i
);
105 rrdtool_argv
[0] = command
;
106 rrdtool_argc
= element_count
+ 1;
111 static char _rrdtool_create__doc__
[] = "Create a new Round Robin Database.\n\n\
112 Usage: create(args...)\n\
115 [--start|-b start time]\n\
117 [DS:ds-name:DST:heartbeat:min:max]\n\
118 [RRA:CF:xff:steps:rows]\n\n\
119 Full documentation can be found at:\n\
120 http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
123 _rrdtool_create(PyObject
*self
, PyObject
*args
)
127 if (convert_args("create", args
) == -1)
130 if (rrd_create(rrdtool_argc
, rrdtool_argv
) == -1) {
131 PyErr_SetString(rrdtool_OperationalError
, rrd_get_error());
143 static char _rrdtool_update__doc__
[] = "Store a new set of values into\
145 Usage: update(args..)\n\
148 [--template|-t ds-name[:ds-name]...]\n\
149 N|timestamp:value[:value...]\n\
150 [timestamp:value[:value...] ...]\n\n\
151 Full documentation can be found at:\n\
152 http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
155 _rrdtool_update(PyObject
*self
, PyObject
*args
)
159 if (convert_args("update", args
) == -1)
162 if (rrd_update(rrdtool_argc
, rrdtool_argv
) == -1) {
163 PyErr_SetString(rrdtool_OperationalError
, rrd_get_error());
175 static char _rrdtool_fetch__doc__
[] = "Fetch data from an RRD.\n\n\
176 Usage: fetch(args..)\n\
180 [--resolution|-r resolution]\n\
181 [--start|-s start]\n\
183 Full documentation can be found at:\n\
184 http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
187 _rrdtool_fetch(PyObject
*self
, PyObject
*args
)
189 PyObject
*ret
, *range_tup
, *dsnam_tup
, *data_list
, *t
;
190 rrd_value_t
*data
, *datai
, dv
;
191 unsigned long step
, ds_cnt
, i
, j
, row
;
195 if (convert_args("fetch", args
) == -1)
198 if (rrd_fetch(rrdtool_argc
, rrdtool_argv
, &start
, &end
, &step
, &ds_cnt
,
199 &ds_namv
, &data
) == -1) {
200 PyErr_SetString(rrdtool_OperationalError
, rrd_get_error());
204 row
= (end
- start
) / step
;
205 ret
= PyTuple_New(3);
206 range_tup
= PyTuple_New(3);
207 dsnam_tup
= PyTuple_New(ds_cnt
);
208 data_list
= PyList_New(row
);
210 PyTuple_SET_ITEM(ret
, 0, range_tup
);
211 PyTuple_SET_ITEM(ret
, 1, dsnam_tup
);
212 PyTuple_SET_ITEM(ret
, 2, data_list
);
216 PyTuple_SET_ITEM(range_tup
, 0, PyLong_FromLong((long)start
));
217 PyTuple_SET_ITEM(range_tup
, 1, PyLong_FromLong((long)end
));
218 PyTuple_SET_ITEM(range_tup
, 2, PyLong_FromLong((long)step
));
220 for (i
= 0; i
< ds_cnt
; i
++)
221 PyTuple_SET_ITEM(dsnam_tup
, i
, PyUnicode_FromString(ds_namv
[i
]));
223 for (i
= 0; i
< row
; i
++) {
224 t
= PyTuple_New(ds_cnt
);
225 PyList_SET_ITEM(data_list
, i
, t
);
227 for (j
= 0; j
< ds_cnt
; j
++) {
230 PyTuple_SET_ITEM(t
, j
, Py_None
);
233 PyTuple_SET_ITEM(t
, j
, PyFloat_FromDouble((double)dv
));
237 for (i
= 0; i
< ds_cnt
; i
++)
238 rrd_freemem(ds_namv
[i
]);
240 rrd_freemem(ds_namv
);
248 static char _rrdtool_graph__doc__
[] = "Create a graph based on one or more " \
250 Usage: graph(args..)\n\
253 [-s|--start start]\n\
256 [-t|--title string]\n\
257 [-v|--vertical-label string]\n\
258 [-w|--width pixels]\n\
259 [-h|--height pixels]\n\
261 [-D|--full-size-mode]\n\
262 [-u|--upper-limit value]\n\
263 [-l|--lower-limit value]\n\
265 [-A|--alt-autoscale]\n\
266 [-J|--alt-autoscale-min]\n\
267 [-M|--alt-autoscale-max]\n\
269 [-x|--x-grid (GTM:GST:MTM:MST:LTM:LST:LPR:LFM|none)]\n\
270 [-y|--y-grid (grid step:label factor|none)]\n\
272 [-o|--logarithmic]\n\
273 [-X|--units-exponent value]\n\
274 [-L|--units-length value]\n\
276 [--right-axis scale:shift]\n\
277 [--right-axis-label label]\n\
278 [--right-axis-format format-string]\n\
280 [-F|--force-rules-legend]\n\
281 [--legend-position=(north|south|west|east)]\n\
282 [--legend-direction=(topdown|bottomup)]\n\
284 [--daemon address]\n\
285 [-f|--imginfo printfstr]\n\
286 [-c|--color COLORTAG#rrggbb[aa]]\n\
287 [--grid-dash on:off]\n\
289 [--dynamic-labels]\n\
290 [-m|--zoom factor]\n\
291 [-n|--font FONTTAG:size:[font]]\n\
292 [-R|--font-render-mode {normal,light,mono}]\n\
293 [-B|--font-smoothing-threshold size]\n\
294 [-P|--pango-markup]\n\
295 [-G|--graph-render-mode {normal,mono}]\n\
297 [-a|--imgformat {PNG,SVG,EPS,PDF}]\n\
298 [-T|--tabwidth value]\n\
300 [-W|--watermark string]\n\
301 DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]\n\
302 CDEF:vname=RPN expression\n\
303 VDEF=vname:RPN expression\n\n\
304 Full documentation can be found at:\n\
305 http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
308 _rrdtool_graph(PyObject
*self
, PyObject
*args
, PyObject
*kwargs
)
311 int orig_stdout
, op
[2], xsize
, ysize
, i
;
316 if (convert_args("graph", args
) == -1)
319 if (rrdtool_argc
>= 2 && strcmp(rrdtool_argv
[1], "-") == 0)
323 orig_stdout
= dup(STDOUT_FILENO
);
325 PyErr_Format(rrdtool_OperationalError
,
326 "Cannot create pipe for stdout: %s", strerror(errno
));
329 dup2(op
[1], STDOUT_FILENO
);
332 // do something on stdout
333 // read(op[0], buffer, MAX_LEN);
337 if (rrd_graph(rrdtool_argc
, rrdtool_argv
, &calcpr
, &xsize
, &ysize
, NULL
,
338 &ymin
, &ymax
) == -1) {
339 PyErr_SetString(rrdtool_OperationalError
, rrd_get_error());
343 ret
= PyTuple_New(keep_in_mem
? 4 : 3);
345 PyTuple_SET_ITEM(ret
, 0, PyLong_FromLong((long)xsize
));
346 PyTuple_SET_ITEM(ret
, 1, PyLong_FromLong((long)ysize
));
352 PyTuple_SET_ITEM(ret
, 2, e
);
354 for (i
= 0; calcpr
[i
]; i
++) {
355 t
= PyUnicode_FromString(calcpr
[i
]);
358 rrd_freemem(calcpr
[i
]);
362 PyTuple_SET_ITEM(ret
, 2, Py_None
);
365 /* feed buffered contents into a PyBytes object */
371 pb
= PyBytes_FromStringAndSize("", 0);
373 if ((rs
= read(op
[0], buffer
, 4096)) <= 0)
376 PyBytes_Concat(&pb
, PyBytes_FromStringAndSize(buffer
, rs
));
379 PyTuple_SET_ITEM(ret
, 3, pb
);
384 dup2(orig_stdout
, STDOUT_FILENO
);
390 static PyMethodDef rrdtool_methods
[] = {
391 {"create", (PyCFunction
)_rrdtool_create
,
392 METH_VARARGS
, _rrdtool_create__doc__
},
393 {"update", (PyCFunction
)_rrdtool_update
,
394 METH_VARARGS
, _rrdtool_update__doc__
},
395 {"fetch", (PyCFunction
)_rrdtool_fetch
,
396 METH_VARARGS
, _rrdtool_fetch__doc__
},
397 {"graph", (PyCFunction
)_rrdtool_graph
,
398 METH_VARARGS
| METH_KEYWORDS
, _rrdtool_graph__doc__
},
399 {NULL
, NULL
, 0, NULL
}
402 static struct PyModuleDef rrdtoolmodule
= {
403 PyModuleDef_HEAD_INIT
,
405 "rrdtool bindings for Python 3",
415 m
= PyModule_Create(&rrdtoolmodule
);
419 rrdtool_ProgrammingError
= PyErr_NewException("rrdtool.ProgrammingError",
421 Py_INCREF(rrdtool_ProgrammingError
);
422 PyModule_AddObject(m
, "ProgrammingError", rrdtool_ProgrammingError
);
424 rrdtool_OperationalError
= PyErr_NewException("rrdtool.OperationalError",
426 Py_INCREF(rrdtool_OperationalError
);
427 PyModule_AddObject(m
, "OperationalError", rrdtool_OperationalError
);