]> git.ipfire.org Git - people/ms/python-rrdtool.git/blame - rrdtoolmodule.c
Update README.md
[people/ms/python-rrdtool.git] / rrdtoolmodule.c
CommitLineData
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. */
48static const char *_version = "0.1.2";
06fe4e5b 49
ea7bad6d 50/** Exception types. */
ce6564c2
CJ
51static PyObject *rrdtool_OperationalError;
52static PyObject *rrdtool_ProgrammingError;
53
618986c4
CJ
54static char **rrdtool_argv = NULL;
55static int rrdtool_argc = 0;
56
ea7bad6d
CJ
57/* extern getopt state */
58extern 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 */
69static int
70convert_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 */
135static void
136destroy_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
148static 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
199static 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
214static 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
235static 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
246static 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
267static 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
278static 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
299static 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
304static 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
326static 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
339static 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
401static 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
410static 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
432static 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
496static 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
540static 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
547static 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
569static 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
586static 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
670static 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
683static 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
704static 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
714static 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
734static 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
743static 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
763static 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
774static 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
796static 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
806static 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
828static 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
837static 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
900static 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
908static 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 915static 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
955static 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
966PyMODINIT_FUNC
967PyInit_rrdtool(void)
225c0910
CJ
968#else
969void
970initrrdtool(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}