]>
Commit | Line | Data |
---|---|---|
ce6564c2 | 1 | /* |
225c0910 | 2 | * python-rrdtool, rrdtool bindings for Python. |
618986c4 CJ |
3 | * Based on the rrdtool Python bindings for Python 2 from |
4 | * Hye-Shik Chang <perky@fallin.lv>. | |
ce6564c2 CJ |
5 | * |
6 | * Copyright 2012 Christian Jurk <commx@commx.ws> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
221dc864 CJ |
9 | * it under the terms of the GNU Lesser General Public License as |
10 | * published by the Free Software Foundation; either version 3 of the | |
275aa95d | 11 | * License, or (at your option) any later version. |
ce6564c2 CJ |
12 | * |
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. | |
17 | * | |
275aa95d | 18 | * You should have received a copy of the GNU Lesser General Public License |
ce6564c2 CJ |
19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
21 | * MA 02110-1301, USA. | |
22 | * | |
ce6564c2 CJ |
23 | */ |
24 | ||
25 | #include <Python.h> | |
ea7bad6d | 26 | #include <datetime.h> |
ce6564c2 CJ |
27 | #include <rrd.h> |
28 | ||
ea7bad6d CJ |
29 | /* Some macros to maintain compatibility between Python 2.x and 3.x */ |
30 | #if PY_MAJOR_VERSION >= 3 | |
31 | #define HAVE_PY3K | |
32 | #define PyRRD_String_Check(x) PyUnicode_Check(x) | |
33 | #define PyRRD_String_FromString(x) PyUnicode_FromString(x) | |
34 | #define PyRRD_String_AS_STRING(x) PyBytes_AsString(PyUnicode_AsUTF8String(o)) | |
35 | #define PyRRD_String_FromStringAndSize(x, y) PyBytes_FromStringAndSize(x, y) | |
36 | #define PyRRD_Int_FromLong(x) PyLong_FromLong(x) | |
221dc864 | 37 | #define PyRRD_Int_FromString(x, y, z) PyLong_FromString(x,y,z) |
ea7bad6d CJ |
38 | #else |
39 | #define PyRRD_String_Check(x) PyString_Check(x) | |
40 | #define PyRRD_String_FromString(x) PyString_FromString(x) | |
41 | #define PyRRD_String_AS_STRING(x) PyString_AS_STRING(x) | |
42 | #define PyRRD_String_FromStringAndSize(x, y) PyString_FromStringAndSize(x, y) | |
43 | #define PyRRD_Int_FromLong(x) PyInt_FromLong(x) | |
221dc864 | 44 | #define PyRRD_Int_FromString(x, y, z) PyInt_FromString(x,y,z) |
ea7bad6d CJ |
45 | #endif |
46 | ||
47 | /** Binding version. */ | |
48 | static const char *_version = "0.1.2"; | |
06fe4e5b | 49 | |
ea7bad6d | 50 | /** Exception types. */ |
ce6564c2 CJ |
51 | static PyObject *rrdtool_OperationalError; |
52 | static PyObject *rrdtool_ProgrammingError; | |
53 | ||
618986c4 CJ |
54 | static char **rrdtool_argv = NULL; |
55 | static int rrdtool_argc = 0; | |
56 | ||
ea7bad6d CJ |
57 | /* extern getopt state */ |
58 | extern int optind, opterr; | |
618986c4 | 59 | |
ea7bad6d CJ |
60 | |
61 | /** | |
62 | * Helper function to convert Python objects into a representation that the | |
618986c4 | 63 | * rrdtool functions can work with. |
ea7bad6d CJ |
64 | * |
65 | * @param command RRDtool command name | |
66 | * @param args Command arguments | |
67 | * @return Zero if the function succeeds, otherwise -1 | |
618986c4 CJ |
68 | */ |
69 | static int | |
70 | convert_args(char *command, PyObject *args) | |
71 | { | |
72 | PyObject *o, *lo; | |
221dc864 | 73 | int i, j, args_count, argv_count, element_count; |
618986c4 | 74 | |
221dc864 | 75 | argv_count = element_count = 0; |
618986c4 | 76 | args_count = PyTuple_Size(args); |
ea7bad6d | 77 | |
221dc864 | 78 | for (i = 0; i < args_count; i++) { |
618986c4 | 79 | o = PyTuple_GET_ITEM(args, i); |
ea7bad6d CJ |
80 | |
81 | if (PyRRD_String_Check(o)) | |
618986c4 CJ |
82 | element_count++; |
83 | else if (PyList_CheckExact(o)) | |
84 | element_count += PyList_Size(o); | |
85 | else { | |
221dc864 | 86 | PyErr_Format(PyExc_TypeError, |
ea7bad6d | 87 | "Argument %d must be str or a list of str", i); |
618986c4 CJ |
88 | return -1; |
89 | } | |
90 | } | |
91 | ||
92 | rrdtool_argv = PyMem_New(char *, element_count + 1); | |
93 | ||
94 | if (rrdtool_argv == NULL) | |
95 | return -1; | |
96 | ||
221dc864 | 97 | for (i = 0; i < args_count; i++) { |
618986c4 CJ |
98 | o = PyTuple_GET_ITEM(args, i); |
99 | ||
ea7bad6d CJ |
100 | if (PyRRD_String_Check(o)) |
101 | rrdtool_argv[++argv_count] = PyRRD_String_AS_STRING(o); | |
618986c4 CJ |
102 | else if (PyList_CheckExact(o)) { |
103 | for (j = 0; j < PyList_Size(o); j++) { | |
104 | lo = PyList_GetItem(o, j); | |
ea7bad6d | 105 | |
221dc864 CJ |
106 | if (PyRRD_String_Check(lo)) |
107 | rrdtool_argv[++argv_count] = PyRRD_String_AS_STRING(lo); | |
618986c4 CJ |
108 | else { |
109 | PyMem_Del(rrdtool_argv); | |
221dc864 | 110 | PyErr_Format(PyExc_TypeError, |
ea7bad6d | 111 | "Element %d in argument %d must be str", j, i); |
618986c4 CJ |
112 | return -1; |
113 | } | |
114 | } | |
115 | } else { | |
116 | PyMem_Del(rrdtool_argv); | |
117 | PyErr_Format(rrdtool_ProgrammingError, | |
ea7bad6d | 118 | "Argument %d must be str or list of str", i); |
618986c4 CJ |
119 | return -1; |
120 | } | |
121 | } | |
122 | ||
123 | rrdtool_argv[0] = command; | |
124 | rrdtool_argc = element_count + 1; | |
125 | ||
ea7bad6d CJ |
126 | /* reset getopt state */ |
127 | opterr = optind = 0; | |
128 | ||
618986c4 CJ |
129 | return 0; |
130 | } | |
131 | ||
ea7bad6d CJ |
132 | /** |
133 | * Destroy argument vector. | |
134 | */ | |
135 | static void | |
136 | destroy_args(void) | |
137 | { | |
138 | PyMem_Del(rrdtool_argv); | |
139 | rrdtool_argv = NULL; | |
140 | } | |
141 | ||
142 | /** | |
143 | * Convert RRDtool info to dict. | |
144 | * | |
145 | * @param data RRDtool info object | |
146 | * @return Python dict object | |
147 | */ | |
06fe4e5b CJ |
148 | static PyObject * |
149 | _rrdtool_util_info2dict(const rrd_info_t *data) | |
150 | { | |
b4570f2a | 151 | PyObject *dict, *val; |
06fe4e5b CJ |
152 | |
153 | dict = PyDict_New(); | |
154 | ||
155 | while (data) { | |
b4570f2a CJ |
156 | val = NULL; |
157 | ||
06fe4e5b CJ |
158 | switch (data->type) { |
159 | case RD_I_VAL: | |
160 | if (isnan(data->value.u_val)) { | |
161 | Py_INCREF(Py_None); | |
162 | val = Py_None; | |
163 | } else | |
164 | PyFloat_FromDouble(data->value.u_val); | |
165 | break; | |
166 | ||
167 | case RD_I_CNT: | |
168 | val = PyLong_FromUnsignedLong(data->value.u_cnt); | |
169 | break; | |
170 | ||
171 | case RD_I_INT: | |
172 | val = PyLong_FromLong(data->value.u_int); | |
173 | break; | |
174 | ||
175 | case RD_I_STR: | |
ea7bad6d | 176 | val = PyRRD_String_FromString(data->value.u_str); |
06fe4e5b CJ |
177 | break; |
178 | ||
179 | case RD_I_BLO: | |
ea7bad6d CJ |
180 | val = PyRRD_String_FromStringAndSize( |
181 | (char *)data->value.u_blo.ptr, | |
182 | data->value.u_blo.size); | |
06fe4e5b CJ |
183 | break; |
184 | default: | |
06fe4e5b CJ |
185 | break; |
186 | } | |
187 | ||
b4570f2a | 188 | if (val != NULL) { |
06fe4e5b CJ |
189 | PyDict_SetItemString(dict, data->key, val); |
190 | Py_DECREF(val); | |
191 | } | |
192 | ||
193 | data = data->next; | |
194 | } | |
195 | ||
196 | return dict; | |
197 | } | |
198 | ||
618986c4 CJ |
199 | static char _rrdtool_create__doc__[] = "Create a new Round Robin Database.\n\n\ |
200 | Usage: create(args...)\n\ | |
201 | Arguments:\n\n\ | |
202 | filename\n\ | |
ea7bad6d CJ |
203 | [-b|--start start time]\n\ |
204 | [-s|--step step]\n\ | |
205 | [-t|--template temolate-file]\n\ | |
206 | [-r|--source source-file]\n\ | |
207 | [-O|--no-overwrite]\n\ | |
208 | [-d|--daemon address]\n\ | |
209 | [DS:ds-name[=mapped-ds-name[source-index]]:DST:heartbeat:min:max]\n\ | |
618986c4 | 210 | [RRA:CF:xff:steps:rows]\n\n\ |
225c0910 CJ |
211 | Full documentation can be found at:\n\ |
212 | http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html"; | |
618986c4 | 213 | |
ce6564c2 CJ |
214 | static PyObject * |
215 | _rrdtool_create(PyObject *self, PyObject *args) | |
216 | { | |
618986c4 | 217 | PyObject *ret; |
ce6564c2 | 218 | |
618986c4 CJ |
219 | if (convert_args("create", args) == -1) |
220 | return NULL; | |
ce6564c2 | 221 | |
618986c4 | 222 | if (rrd_create(rrdtool_argc, rrdtool_argv) == -1) { |
ce6564c2 CJ |
223 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); |
224 | rrd_clear_error(); | |
618986c4 CJ |
225 | ret = NULL; |
226 | } else { | |
227 | Py_INCREF(Py_None); | |
228 | ret = Py_None; | |
229 | } | |
230 | ||
231 | destroy_args(); | |
232 | return ret; | |
233 | } | |
234 | ||
225c0910 CJ |
235 | static char _rrdtool_dump__doc__[] = "Dump an RRD to XML.\n\n\ |
236 | Usage: dump(args..)\n\ | |
237 | Arguments:\n\n\ | |
238 | [-h|--header {none,xsd,dtd}\n\ | |
ea7bad6d CJ |
239 | [-n|--no-header]\n\ |
240 | [-d|--daemon address]\n\ | |
225c0910 CJ |
241 | file.rrd\n\ |
242 | [file.xml]\n\n\ | |
243 | Full documentation can be found at:\n\ | |
244 | http://oss.oetiker.ch/rrdtool/doc/rrddump.en.html"; | |
245 | ||
246 | static PyObject * | |
247 | _rrdtool_dump(PyObject *self, PyObject *args) | |
248 | { | |
249 | PyObject *ret; | |
250 | ||
251 | if (convert_args("dump", args) == -1) | |
252 | return NULL; | |
253 | ||
254 | if (rrd_dump(rrdtool_argc, rrdtool_argv) != 0) { | |
255 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
256 | rrd_clear_error(); | |
257 | ret = NULL; | |
258 | } else { | |
259 | Py_INCREF(Py_None); | |
260 | ret = Py_None; | |
261 | } | |
262 | ||
263 | destroy_args(); | |
264 | return ret; | |
265 | } | |
266 | ||
618986c4 CJ |
267 | static char _rrdtool_update__doc__[] = "Store a new set of values into\ |
268 | the RRD.\n\n\ | |
269 | Usage: update(args..)\n\ | |
270 | Arguments:\n\n\ | |
271 | filename\n\ | |
272 | [--template|-t ds-name[:ds-name]...]\n\ | |
273 | N|timestamp:value[:value...]\n\ | |
274 | [timestamp:value[:value...] ...]\n\n\ | |
225c0910 CJ |
275 | Full documentation can be found at:\n\ |
276 | http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html"; | |
618986c4 CJ |
277 | |
278 | static PyObject * | |
279 | _rrdtool_update(PyObject *self, PyObject *args) | |
280 | { | |
281 | PyObject *ret; | |
282 | ||
283 | if (convert_args("update", args) == -1) | |
284 | return NULL; | |
285 | ||
286 | if (rrd_update(rrdtool_argc, rrdtool_argv) == -1) { | |
287 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
288 | rrd_clear_error(); | |
289 | ret = NULL; | |
290 | } else { | |
291 | Py_INCREF(Py_None); | |
292 | ret = Py_None; | |
293 | } | |
294 | ||
295 | destroy_args(); | |
296 | return ret; | |
297 | } | |
298 | ||
06fe4e5b CJ |
299 | static char _rrdtool_updatev__doc__[] = "Store a new set of values into "\ |
300 | "the Round Robin Database and return an info dictionary.\n\n\ | |
301 | This function works in the same manner as 'update', but will return an\n\ | |
302 | info dictionary instead of None."; | |
303 | ||
304 | static PyObject * | |
305 | _rrdtool_updatev(PyObject *self, PyObject *args) | |
306 | { | |
307 | PyObject *ret; | |
308 | rrd_info_t *data; | |
309 | ||
310 | if (convert_args("updatev", args) == -1) | |
311 | return NULL; | |
312 | ||
313 | if ((data = rrd_update_v(rrdtool_argc, rrdtool_argv)) == NULL) { | |
314 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
315 | rrd_clear_error(); | |
316 | ret = NULL; | |
317 | } else { | |
318 | ret = _rrdtool_util_info2dict(data); | |
319 | rrd_info_free(data); | |
320 | } | |
321 | ||
322 | destroy_args(); | |
323 | return ret; | |
324 | } | |
325 | ||
618986c4 CJ |
326 | static char _rrdtool_fetch__doc__[] = "Fetch data from an RRD.\n\n\ |
327 | Usage: fetch(args..)\n\ | |
328 | Arguments:\n\n\ | |
329 | filename\n\ | |
330 | CF\n\ | |
ea7bad6d CJ |
331 | [-r|--resolution resolution]\n\ |
332 | [-s|--start start]\n\ | |
333 | [-e|--end end]\n\ | |
334 | [-a|--align-start]\n\ | |
335 | [-d|--daemon address]\n\n\ | |
225c0910 CJ |
336 | Full documentation can be found at:\n\ |
337 | http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html"; | |
618986c4 CJ |
338 | |
339 | static PyObject * | |
340 | _rrdtool_fetch(PyObject *self, PyObject *args) | |
341 | { | |
342 | PyObject *ret, *range_tup, *dsnam_tup, *data_list, *t; | |
343 | rrd_value_t *data, *datai, dv; | |
344 | unsigned long step, ds_cnt, i, j, row; | |
345 | time_t start, end; | |
346 | char **ds_namv; | |
347 | ||
348 | if (convert_args("fetch", args) == -1) | |
349 | return NULL; | |
350 | ||
351 | if (rrd_fetch(rrdtool_argc, rrdtool_argv, &start, &end, &step, &ds_cnt, | |
352 | &ds_namv, &data) == -1) { | |
353 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
354 | rrd_clear_error(); | |
355 | ret = NULL; | |
356 | } else { | |
357 | row = (end - start) / step; | |
358 | ret = PyTuple_New(3); | |
359 | range_tup = PyTuple_New(3); | |
360 | dsnam_tup = PyTuple_New(ds_cnt); | |
361 | data_list = PyList_New(row); | |
362 | ||
363 | PyTuple_SET_ITEM(ret, 0, range_tup); | |
364 | PyTuple_SET_ITEM(ret, 1, dsnam_tup); | |
365 | PyTuple_SET_ITEM(ret, 2, data_list); | |
366 | ||
367 | datai = data; | |
368 | ||
ea7bad6d CJ |
369 | PyTuple_SET_ITEM(range_tup, 0, PyRRD_Int_FromLong((long) start)); |
370 | PyTuple_SET_ITEM(range_tup, 1, PyRRD_Int_FromLong((long) end)); | |
371 | PyTuple_SET_ITEM(range_tup, 2, PyRRD_Int_FromLong((long) step)); | |
618986c4 CJ |
372 | |
373 | for (i = 0; i < ds_cnt; i++) | |
ea7bad6d | 374 | PyTuple_SET_ITEM(dsnam_tup, i, PyRRD_String_FromString(ds_namv[i])); |
618986c4 CJ |
375 | |
376 | for (i = 0; i < row; i++) { | |
377 | t = PyTuple_New(ds_cnt); | |
378 | PyList_SET_ITEM(data_list, i, t); | |
379 | ||
380 | for (j = 0; j < ds_cnt; j++) { | |
381 | dv = *(datai++); | |
382 | if (isnan(dv)) { | |
383 | PyTuple_SET_ITEM(t, j, Py_None); | |
384 | Py_INCREF(Py_None); | |
385 | } else | |
ea7bad6d | 386 | PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv)); |
618986c4 CJ |
387 | } |
388 | } | |
389 | ||
390 | for (i = 0; i < ds_cnt; i++) | |
391 | rrd_freemem(ds_namv[i]); | |
392 | ||
393 | rrd_freemem(ds_namv); | |
394 | rrd_freemem(data); | |
395 | } | |
396 | ||
397 | destroy_args(); | |
398 | return ret; | |
399 | } | |
400 | ||
06fe4e5b CJ |
401 | static char _rrdtool_flushcached__doc__[] = "Flush RRD files from memory.\n\n\ |
402 | Usage: flushcached(args..)\n\ | |
403 | Arguments:\n\n\ | |
ea7bad6d | 404 | [-d|--daemon address]\n\ |
06fe4e5b CJ |
405 | filename\n\ |
406 | [filename ...]\n\n\ | |
225c0910 CJ |
407 | Full documentation can be found at:\n\ |
408 | http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html"; | |
06fe4e5b CJ |
409 | |
410 | static PyObject * | |
411 | _rrdtool_flushcached(PyObject *self, PyObject *args) | |
412 | { | |
413 | PyObject *ret; | |
414 | ||
415 | if (convert_args("flushcached", args) == -1) | |
416 | return NULL; | |
417 | ||
418 | if (rrd_flushcached(rrdtool_argc, rrdtool_argv) != 0) { | |
419 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
420 | rrd_clear_error(); | |
421 | ret = NULL; | |
422 | } else { | |
423 | Py_INCREF(Py_None); | |
424 | ret = Py_None; | |
425 | } | |
426 | ||
427 | destroy_args(); | |
428 | return ret; | |
429 | } | |
430 | ||
ea7bad6d | 431 | #ifdef HAVE_RRD_GRAPH |
618986c4 CJ |
432 | static char _rrdtool_graph__doc__[] = "Create a graph based on one or more " \ |
433 | "RRDs.\n\n\ | |
434 | Usage: graph(args..)\n\ | |
435 | Arguments:\n\n\ | |
436 | filename | -\n\ | |
437 | [-s|--start start]\n\ | |
438 | [-e|--end end]\n\ | |
439 | [-S|--step step]\n\ | |
440 | [-t|--title string]\n\ | |
441 | [-v|--vertical-label string]\n\ | |
442 | [-w|--width pixels]\n\ | |
443 | [-h|--height pixels]\n\ | |
444 | [-j|--only-graph]\n\ | |
445 | [-D|--full-size-mode]\n\ | |
446 | [-u|--upper-limit value]\n\ | |
447 | [-l|--lower-limit value]\n\ | |
448 | [-r|--rigid]\n\ | |
449 | [-A|--alt-autoscale]\n\ | |
450 | [-J|--alt-autoscale-min]\n\ | |
451 | [-M|--alt-autoscale-max]\n\ | |
452 | [-N|--no-gridfit]\n\ | |
453 | [-x|--x-grid (GTM:GST:MTM:MST:LTM:LST:LPR:LFM|none)]\n\ | |
454 | [-y|--y-grid (grid step:label factor|none)]\n\ | |
ea7bad6d CJ |
455 | [--week-fmt strftime format string]\n\ |
456 | [--left-axis-formatter formatter-name]\n\ | |
457 | [--left-axis-format format-string]\n\ | |
618986c4 CJ |
458 | [-Y|--alt-y-grid]\n\ |
459 | [-o|--logarithmic]\n\ | |
460 | [-X|--units-exponent value]\n\ | |
461 | [-L|--units-length value]\n\ | |
462 | [--units=si]\n\ | |
463 | [--right-axis scale:shift]\n\ | |
464 | [--right-axis-label label]\n\ | |
465 | [--right-axis-format format-string]\n\ | |
466 | [-g|--no-legend]\n\ | |
467 | [-F|--force-rules-legend]\n\ | |
468 | [--legend-position=(north|south|west|east)]\n\ | |
469 | [--legend-direction=(topdown|bottomup)]\n\ | |
470 | [-z|--lazy]\n\ | |
ea7bad6d | 471 | [-d|--daemon address]\n\ |
618986c4 CJ |
472 | [-f|--imginfo printfstr]\n\ |
473 | [-c|--color COLORTAG#rrggbb[aa]]\n\ | |
474 | [--grid-dash on:off]\n\ | |
475 | [--border width]\n\ | |
476 | [--dynamic-labels]\n\ | |
477 | [-m|--zoom factor]\n\ | |
478 | [-n|--font FONTTAG:size:[font]]\n\ | |
479 | [-R|--font-render-mode {normal,light,mono}]\n\ | |
480 | [-B|--font-smoothing-threshold size]\n\ | |
481 | [-P|--pango-markup]\n\ | |
482 | [-G|--graph-render-mode {normal,mono}]\n\ | |
483 | [-E|--slope-mode]\n\ | |
ea7bad6d CJ |
484 | [-a|--imgformat {PNG,SVG,EPS,PDF,XML,XMLENUM,JSON,JSONTIME,CSV,TSV,SSV}]\n\ |
485 | [-i|--interlaced]\n\ | |
618986c4 CJ |
486 | [-T|--tabwidth value]\n\ |
487 | [-b|--base value]\n\ | |
488 | [-W|--watermark string]\n\ | |
ea7bad6d | 489 | [-Z|--use-nan-for-all-missing-data]\n\ |
618986c4 CJ |
490 | DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]\n\ |
491 | CDEF:vname=RPN expression\n\ | |
492 | VDEF=vname:RPN expression\n\n\ | |
225c0910 CJ |
493 | Full documentation can be found at:\n\ |
494 | http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html"; | |
618986c4 CJ |
495 | |
496 | static PyObject * | |
497 | _rrdtool_graph(PyObject *self, PyObject *args, PyObject *kwargs) | |
498 | { | |
499 | PyObject *ret; | |
06fe4e5b | 500 | int xsize, ysize, i; |
618986c4 | 501 | double ymin, ymax; |
225c0910 | 502 | char **calcpr; |
618986c4 CJ |
503 | |
504 | if (convert_args("graph", args) == -1) | |
505 | return NULL; | |
506 | ||
618986c4 CJ |
507 | if (rrd_graph(rrdtool_argc, rrdtool_argv, &calcpr, &xsize, &ysize, NULL, |
508 | &ymin, &ymax) == -1) { | |
509 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
510 | rrd_clear_error(); | |
511 | ret = NULL; | |
512 | } else { | |
225c0910 | 513 | ret = PyTuple_New(3); |
618986c4 | 514 | |
ea7bad6d CJ |
515 | PyTuple_SET_ITEM(ret, 0, PyRRD_Int_FromLong((long) xsize)); |
516 | PyTuple_SET_ITEM(ret, 1, PyRRD_Int_FromLong((long) ysize)); | |
618986c4 CJ |
517 | |
518 | if (calcpr) { | |
519 | PyObject *e, *t; | |
520 | ||
521 | e = PyList_New(0); | |
522 | PyTuple_SET_ITEM(ret, 2, e); | |
523 | ||
524 | for (i = 0; calcpr[i]; i++) { | |
ea7bad6d | 525 | t = PyRRD_String_FromString(calcpr[i]); |
618986c4 CJ |
526 | PyList_Append(e, t); |
527 | Py_DECREF(t); | |
528 | rrd_freemem(calcpr[i]); | |
529 | } | |
530 | } else { | |
531 | Py_INCREF(Py_None); | |
532 | PyTuple_SET_ITEM(ret, 2, Py_None); | |
533 | } | |
06fe4e5b CJ |
534 | } |
535 | ||
536 | destroy_args(); | |
537 | return ret; | |
538 | } | |
539 | ||
ea7bad6d CJ |
540 | static char _rrdtool_graphv__doc__[] = "Create a graph based on one or more " \ |
541 | "RRDs and return data in RRDtool info format.\n\n\ | |
542 | This function works the same way as 'graph', but will return a info\n\ | |
543 | dictionary instead of None.\n\n\ | |
544 | Full documentation can be found at (graphv section):\n\ | |
545 | http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html"; | |
06fe4e5b CJ |
546 | |
547 | static PyObject * | |
548 | _rrdtool_graphv(PyObject *self, PyObject *args) | |
549 | { | |
550 | PyObject *ret; | |
551 | rrd_info_t *data; | |
552 | ||
553 | if (convert_args("graphv", args) == -1) | |
554 | return NULL; | |
555 | ||
556 | if ((data = rrd_graph_v(rrdtool_argc, rrdtool_argv)) == NULL) { | |
557 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
558 | rrd_clear_error(); | |
559 | ret = NULL; | |
560 | } else { | |
561 | ret = _rrdtool_util_info2dict(data); | |
562 | rrd_info_free(data); | |
563 | } | |
564 | ||
565 | destroy_args(); | |
566 | return ret; | |
567 | } | |
568 | ||
ea7bad6d CJ |
569 | static char _rrdtool_xport__doc__[] = "Dictionary representation of data " \ |
570 | "stored in RRDs.\n\n\ | |
571 | Usage: xport(args..)\n\ | |
572 | Arguments:\n\n\ | |
573 | [-s[--start seconds]\n\ | |
574 | [-e|--end seconds]\n\ | |
575 | [-m|--maxrows rows]\n\ | |
576 | [--step value]\n\ | |
577 | [--json]\n\ | |
578 | [--enumds]\n\ | |
579 | [--daemon address]\n\ | |
580 | [DEF:vname=rrd:ds-name:CF]\n\ | |
581 | [CDEF:vname=rpn-expression]\n\ | |
582 | [XPORT:vname[:legend]]\n\n\ | |
583 | Full documentation can be found at:\n\ | |
584 | http://oss.oetiker.ch/rrdtool/doc/rrdxport.en.html"; | |
585 | ||
586 | static PyObject * | |
587 | _rrdtool_xport(PyObject *self, PyObject *args) | |
588 | { | |
589 | PyObject *ret; | |
590 | int xsize; | |
591 | char **legend_v; | |
592 | time_t start, end; | |
593 | unsigned long step, col_cnt; | |
594 | rrd_value_t *data, *datai; | |
595 | ||
596 | if (convert_args("xport", args) == -1) | |
597 | return NULL; | |
598 | ||
599 | if (rrd_xport(rrdtool_argc, rrdtool_argv, &xsize, &start, &end, &step, | |
600 | &col_cnt, &legend_v, &data) == -1) { | |
601 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
602 | rrd_clear_error(); | |
603 | ret = NULL; | |
604 | } else { | |
605 | PyObject *meta_dict, *data_list, *legend_list, *t; | |
606 | rrd_value_t dv; | |
607 | unsigned long i, j, row_cnt = (end - start) / step; | |
608 | ||
609 | ret = PyDict_New(); | |
610 | meta_dict = PyDict_New(); | |
611 | legend_list = PyList_New(col_cnt); | |
612 | data_list = PyList_New(row_cnt); | |
613 | ||
614 | PyDict_SetItem(ret, PyRRD_String_FromString("meta"), meta_dict); | |
615 | PyDict_SetItem(ret, PyRRD_String_FromString("data"), data_list); | |
616 | ||
617 | datai = data; | |
618 | ||
619 | PyDict_SetItem(meta_dict, | |
620 | PyRRD_String_FromString("start"), | |
621 | PyRRD_Int_FromLong((long) start)); | |
622 | PyDict_SetItem(meta_dict, | |
623 | PyRRD_String_FromString("end"), | |
624 | PyRRD_Int_FromLong((long) end)); | |
625 | PyDict_SetItem(meta_dict, | |
626 | PyRRD_String_FromString("step"), | |
627 | PyRRD_Int_FromLong((long) step)); | |
628 | PyDict_SetItem(meta_dict, | |
629 | PyRRD_String_FromString("rows"), | |
630 | PyRRD_Int_FromLong((long) row_cnt)); | |
631 | PyDict_SetItem(meta_dict, | |
632 | PyRRD_String_FromString("columns"), | |
633 | PyRRD_Int_FromLong((long) col_cnt)); | |
634 | PyDict_SetItem(meta_dict, | |
635 | PyRRD_String_FromString("legend"), | |
636 | legend_list); | |
637 | ||
638 | for (i = 0; i < col_cnt; i++) | |
639 | PyList_SET_ITEM(legend_list, i, PyRRD_String_FromString(legend_v[i])); | |
640 | ||
641 | for (i = 0; i < row_cnt; i++) { | |
642 | t = PyTuple_New(col_cnt); | |
643 | PyList_SET_ITEM(data_list, i, t); | |
644 | ||
645 | for (j = 0; j < col_cnt; j++) { | |
646 | dv = *(datai++); | |
647 | ||
648 | if (isnan(dv)) { | |
649 | PyTuple_SET_ITEM(t, j, Py_None); | |
650 | Py_INCREF(Py_None); | |
651 | } else { | |
652 | PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv)); | |
653 | } | |
654 | } | |
655 | } | |
656 | ||
657 | for (i = 0; i < col_cnt; i++) | |
658 | rrd_freemem(legend_v[i]); | |
659 | ||
660 | rrd_freemem(legend_v); | |
661 | rrd_freemem(data); | |
662 | } | |
663 | ||
664 | destroy_args(); | |
665 | ||
666 | return ret; | |
667 | } | |
668 | #endif /* HAVE_RRD_GRAPH */ | |
669 | ||
06fe4e5b CJ |
670 | static char _rrdtool_tune__doc__[] = "Modify some basic properties of a " \ |
671 | "Round Robin Database.\n\n\ | |
672 | Usage: tune(args..)\n\ | |
673 | Arguments:\n\n\ | |
674 | filename\n\ | |
675 | [-h|--heartbeat ds-name:heartbeat]\n\ | |
676 | [-i|--minimum ds-name:min]\n\ | |
677 | [-a|--maximum ds-name:max]\n\ | |
678 | [-d|--data-source-type ds-name:DST]\n\ | |
679 | [-r|--data-source-rename old-name:new-name]\n\n\ | |
225c0910 CJ |
680 | Full documentation can be found at:\n\ |
681 | http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html"; | |
06fe4e5b CJ |
682 | |
683 | static PyObject * | |
684 | _rrdtool_tune(PyObject *self, PyObject *args) | |
685 | { | |
686 | PyObject *ret; | |
687 | ||
688 | if (convert_args("tune", args) == -1) | |
689 | return NULL; | |
690 | ||
691 | if (rrd_tune(rrdtool_argc, rrdtool_argv) == -1) { | |
692 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
693 | rrd_clear_error(); | |
694 | ret = NULL; | |
695 | } else { | |
696 | Py_INCREF(Py_None); | |
697 | ret = Py_None; | |
698 | } | |
699 | ||
700 | destroy_args(); | |
701 | return ret; | |
702 | } | |
703 | ||
704 | static char _rrdtool_first__doc__[] = "Get the first UNIX timestamp of the "\ | |
705 | "first data sample in an Round Robin Database.\n\n\ | |
706 | Usage: first(args..)\n\ | |
707 | Arguments:\n\n\ | |
708 | filename\n\ | |
ea7bad6d CJ |
709 | [--rraindex number]\n\ |
710 | [-d|--daemon address]\n\n\ | |
225c0910 CJ |
711 | Full documentation can be found at:\n\ |
712 | http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html"; | |
06fe4e5b CJ |
713 | |
714 | static PyObject * | |
715 | _rrdtool_first(PyObject *self, PyObject *args) | |
716 | { | |
717 | PyObject *ret; | |
718 | int ts; | |
719 | ||
720 | if (convert_args("first", args) == -1) | |
721 | return NULL; | |
722 | ||
723 | if ((ts = rrd_first(rrdtool_argc, rrdtool_argv)) == -1) { | |
724 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
725 | rrd_clear_error(); | |
726 | ret = NULL; | |
727 | } else | |
ea7bad6d | 728 | ret = PyRRD_Int_FromLong((long) ts); |
06fe4e5b CJ |
729 | |
730 | destroy_args(); | |
731 | return ret; | |
732 | } | |
733 | ||
734 | static char _rrdtool_last__doc__[] = "Get the UNIX timestamp of the most "\ | |
735 | "recent data sample in an Round Robin Database.\n\n\ | |
736 | Usage: last(args..)\n\ | |
737 | Arguments:\n\n\ | |
738 | filename\n\ | |
ea7bad6d | 739 | [-d|--daemon address]\n\n\ |
225c0910 CJ |
740 | Full documentation can be found at:\n\ |
741 | http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html"; | |
06fe4e5b CJ |
742 | |
743 | static PyObject * | |
744 | _rrdtool_last(PyObject *self, PyObject *args) | |
745 | { | |
746 | PyObject *ret; | |
747 | int ts; | |
748 | ||
749 | if (convert_args("last", args) == -1) | |
750 | return NULL; | |
751 | ||
752 | if ((ts = rrd_last(rrdtool_argc, rrdtool_argv)) == -1) { | |
753 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
754 | rrd_clear_error(); | |
755 | ret = NULL; | |
756 | } else | |
ea7bad6d | 757 | ret = PyRRD_Int_FromLong((long) ts); |
06fe4e5b CJ |
758 | |
759 | destroy_args(); | |
760 | return ret; | |
761 | } | |
762 | ||
763 | static char _rrdtool_resize__doc__[] = "Modify the number of rows in a "\ | |
764 | "Round Robin Database.\n\n\ | |
765 | Usage: resize(args..)\n\ | |
766 | Arguments:\n\n\ | |
767 | filename\n\ | |
768 | rra-num\n\ | |
769 | GROW|SHRINK\n\ | |
770 | rows\n\n\ | |
225c0910 CJ |
771 | Full documentation can be found at:\n\ |
772 | http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html"; | |
06fe4e5b CJ |
773 | |
774 | static PyObject * | |
775 | _rrdtool_resize(PyObject *self, PyObject *args) | |
776 | { | |
777 | PyObject *ret; | |
778 | int ts; | |
779 | ||
780 | if (convert_args("resize", args) == -1) | |
781 | return NULL; | |
782 | ||
783 | if ((ts = rrd_resize(rrdtool_argc, rrdtool_argv)) == -1) { | |
784 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
785 | rrd_clear_error(); | |
786 | ret = NULL; | |
787 | } else { | |
788 | Py_INCREF(Py_None); | |
789 | ret = Py_None; | |
790 | } | |
791 | ||
792 | destroy_args(); | |
793 | return ret; | |
794 | } | |
795 | ||
796 | static char _rrdtool_info__doc__[] = "Extract header information from an "\ | |
797 | "Round Robin Database.\n\n\ | |
ea7bad6d | 798 | Usage: info(filename, ...)\n\ |
06fe4e5b | 799 | Arguments:\n\n\ |
ea7bad6d CJ |
800 | filename\n\ |
801 | [-d|--daemon address]\n\ | |
802 | [-F|--noflush]\n\n\ | |
225c0910 CJ |
803 | Full documentation can be found at:\n\ |
804 | http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html"; | |
06fe4e5b CJ |
805 | |
806 | static PyObject * | |
807 | _rrdtool_info(PyObject *self, PyObject *args) | |
808 | { | |
809 | PyObject *ret; | |
810 | rrd_info_t *data; | |
811 | ||
812 | if (convert_args("info", args) == -1) | |
813 | return NULL; | |
814 | ||
815 | if ((data = rrd_info(rrdtool_argc, rrdtool_argv)) == NULL) { | |
816 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
817 | rrd_clear_error(); | |
818 | ret = NULL; | |
819 | } else { | |
820 | ret = _rrdtool_util_info2dict(data); | |
821 | rrd_info_free(data); | |
822 | } | |
ce6564c2 | 823 | |
618986c4 CJ |
824 | destroy_args(); |
825 | return ret; | |
ce6564c2 CJ |
826 | } |
827 | ||
ea7bad6d CJ |
828 | static char _rrdtool_lastupdate__doc__[] = "Returns datetime and value stored "\ |
829 | "for each datum in the most recent update of an RRD.\n\n\ | |
830 | Usage: lastupdate(filename, ...)\n\ | |
831 | Arguments:\n\n\ | |
832 | filename\n\ | |
833 | [-d|--daemon address]\n\n\ | |
834 | Full documentation can be found at:\n\ | |
835 | http://oss.oetiker.ch/rrdtool/doc/rrdlastupdate.en.html"; | |
836 | ||
837 | static PyObject * | |
838 | _rrdtool_lastupdate(PyObject *self, PyObject *args) | |
839 | { | |
840 | PyObject *ret, *ds_dict; | |
841 | rrd_info_t *data; | |
842 | int status; | |
843 | time_t last_update; | |
844 | char **ds_names, **last_ds; | |
845 | unsigned long ds_cnt, i; | |
846 | ||
847 | if (convert_args("lastupdate", args) == -1) | |
848 | return NULL; | |
849 | else if (rrdtool_argc < 2) { | |
221dc864 | 850 | PyErr_SetString(rrdtool_ProgrammingError, "Missing filename argument"); |
ea7bad6d CJ |
851 | return NULL; |
852 | } | |
853 | ||
ea7bad6d CJ |
854 | status = rrd_lastupdate_r(rrdtool_argv[1], |
855 | &last_update, | |
856 | &ds_cnt, | |
857 | &ds_names, | |
858 | &last_ds); | |
859 | ||
860 | if (status != 0) { | |
861 | PyErr_SetString(rrdtool_OperationalError, rrd_get_error()); | |
862 | rrd_clear_error(); | |
863 | ret = NULL; | |
864 | } else { | |
865 | /* convert last_update to Python datetime object */ | |
866 | struct tm *ts = localtime(&last_update); | |
867 | ret = PyDict_New(); | |
868 | ds_dict = PyDict_New(); | |
869 | ||
870 | PyDict_SetItemString(ret, | |
221dc864 | 871 | "date", |
ea7bad6d CJ |
872 | PyDateTime_FromDateAndTime( |
873 | ts->tm_year + 1900, | |
874 | ts->tm_mon + 1, | |
875 | ts->tm_mday, | |
876 | ts->tm_hour, | |
877 | ts->tm_min, | |
878 | ts->tm_sec, | |
879 | 0)); | |
221dc864 | 880 | PyDict_SetItemString(ret, "ds", ds_dict); |
ea7bad6d CJ |
881 | |
882 | for (i = 0; i < ds_cnt; i++) { | |
883 | PyDict_SetItemString(ds_dict, | |
221dc864 CJ |
884 | ds_names[i], |
885 | PyRRD_Int_FromString(last_ds[i], NULL, 10)); | |
ea7bad6d CJ |
886 | free(last_ds[i]); |
887 | free(ds_names[i]); | |
888 | } | |
889 | ||
890 | free(last_ds); | |
891 | free(ds_names); | |
892 | ||
893 | } | |
894 | ||
895 | destroy_args(); | |
896 | ||
897 | return ret; | |
898 | } | |
899 | ||
b4570f2a CJ |
900 | static char _rrdtool_lib_version__doc__[] = "Get the version this binding "\ |
901 | "was compiled against."; | |
902 | ||
ea7bad6d CJ |
903 | /** |
904 | * Returns a str object that contains the librrd version. | |
905 | * | |
906 | * @return librrd version (Python str object) | |
907 | */ | |
b4570f2a CJ |
908 | static PyObject * |
909 | _rrdtool_lib_version(PyObject *self, PyObject *args) | |
910 | { | |
ea7bad6d | 911 | return PyRRD_String_FromString(rrd_strversion()); |
b4570f2a CJ |
912 | } |
913 | ||
ea7bad6d | 914 | /** Method table. */ |
ce6564c2 | 915 | static PyMethodDef rrdtool_methods[] = { |
618986c4 CJ |
916 | {"create", (PyCFunction)_rrdtool_create, |
917 | METH_VARARGS, _rrdtool_create__doc__}, | |
225c0910 CJ |
918 | {"dump", (PyCFunction)_rrdtool_dump, |
919 | METH_VARARGS, _rrdtool_dump__doc__}, | |
618986c4 CJ |
920 | {"update", (PyCFunction)_rrdtool_update, |
921 | METH_VARARGS, _rrdtool_update__doc__}, | |
06fe4e5b CJ |
922 | {"updatev", (PyCFunction)_rrdtool_updatev, |
923 | METH_VARARGS, _rrdtool_updatev__doc__}, | |
618986c4 CJ |
924 | {"fetch", (PyCFunction)_rrdtool_fetch, |
925 | METH_VARARGS, _rrdtool_fetch__doc__}, | |
06fe4e5b CJ |
926 | {"flushcached", (PyCFunction)_rrdtool_flushcached, |
927 | METH_VARARGS, _rrdtool_flushcached__doc__}, | |
ea7bad6d | 928 | #ifdef HAVE_RRD_GRAPH |
618986c4 | 929 | {"graph", (PyCFunction)_rrdtool_graph, |
06fe4e5b CJ |
930 | METH_VARARGS, _rrdtool_graph__doc__}, |
931 | {"graphv", (PyCFunction)_rrdtool_graphv, | |
932 | METH_VARARGS, _rrdtool_graphv__doc__}, | |
ea7bad6d CJ |
933 | {"xport", (PyCFunction)_rrdtool_xport, |
934 | METH_VARARGS, _rrdtool_xport__doc__}, | |
935 | #endif | |
06fe4e5b CJ |
936 | {"tune", (PyCFunction)_rrdtool_tune, |
937 | METH_VARARGS, _rrdtool_tune__doc__}, | |
938 | {"first", (PyCFunction)_rrdtool_first, | |
939 | METH_VARARGS, _rrdtool_first__doc__}, | |
940 | {"last", (PyCFunction)_rrdtool_last, | |
941 | METH_VARARGS, _rrdtool_last__doc__}, | |
942 | {"resize", (PyCFunction)_rrdtool_resize, | |
943 | METH_VARARGS, _rrdtool_resize__doc__}, | |
944 | {"info", (PyCFunction)_rrdtool_info, | |
945 | METH_VARARGS, _rrdtool_info__doc__}, | |
ea7bad6d CJ |
946 | {"lastupdate", (PyCFunction)_rrdtool_lastupdate, |
947 | METH_VARARGS, _rrdtool_lastupdate__doc__}, | |
b4570f2a CJ |
948 | {"lib_version", (PyCFunction)_rrdtool_lib_version, |
949 | METH_VARARGS, _rrdtool_lib_version__doc__}, | |
ce6564c2 CJ |
950 | {NULL, NULL, 0, NULL} |
951 | }; | |
952 | ||
ea7bad6d CJ |
953 | /** Library init function. */ |
954 | #ifdef HAVE_PY3K | |
ce6564c2 CJ |
955 | static struct PyModuleDef rrdtoolmodule = { |
956 | PyModuleDef_HEAD_INIT, | |
957 | "rrdtool", | |
ea7bad6d | 958 | "rrdtool bindings for Python", |
ce6564c2 CJ |
959 | -1, |
960 | rrdtool_methods | |
961 | }; | |
962 | ||
225c0910 CJ |
963 | #endif |
964 | ||
ea7bad6d | 965 | #ifdef HAVE_PY3K |
ce6564c2 CJ |
966 | PyMODINIT_FUNC |
967 | PyInit_rrdtool(void) | |
225c0910 CJ |
968 | #else |
969 | void | |
970 | initrrdtool(void) | |
971 | #endif | |
ce6564c2 CJ |
972 | { |
973 | PyObject *m; | |
974 | ||
ea7bad6d CJ |
975 | PyDateTime_IMPORT; /* initialize PyDateTime_ functions */ |
976 | ||
977 | #ifdef HAVE_PY3K | |
ce6564c2 | 978 | m = PyModule_Create(&rrdtoolmodule); |
225c0910 CJ |
979 | #else |
980 | m = Py_InitModule3("rrdtool", | |
981 | rrdtool_methods, | |
982 | "rrdtool bindings for Python"); | |
983 | #endif | |
984 | ||
ce6564c2 | 985 | if (m == NULL) |
ea7bad6d | 986 | #ifdef HAVE_PY3K |
225c0910 CJ |
987 | return NULL; |
988 | #else | |
989 | return; | |
990 | #endif | |
ce6564c2 CJ |
991 | |
992 | rrdtool_ProgrammingError = PyErr_NewException("rrdtool.ProgrammingError", | |
993 | NULL, NULL); | |
994 | Py_INCREF(rrdtool_ProgrammingError); | |
995 | PyModule_AddObject(m, "ProgrammingError", rrdtool_ProgrammingError); | |
996 | ||
997 | rrdtool_OperationalError = PyErr_NewException("rrdtool.OperationalError", | |
998 | NULL, NULL); | |
999 | Py_INCREF(rrdtool_OperationalError); | |
1000 | PyModule_AddObject(m, "OperationalError", rrdtool_OperationalError); | |
ea7bad6d | 1001 | PyModule_AddObject(m, "__version__", PyRRD_String_FromString(_version)); |
ce6564c2 | 1002 | |
ea7bad6d CJ |
1003 | #ifdef HAVE_PY3K |
1004 | return m; | |
225c0910 | 1005 | #endif |
ce6564c2 | 1006 | } |