]> git.ipfire.org Git - people/ms/python-rrdtool.git/blob - rrdtoolmodule.c
Update README.md
[people/ms/python-rrdtool.git] / rrdtoolmodule.c
1 /*
2 * python-rrdtool, rrdtool bindings for Python.
3 * Based on the rrdtool Python bindings for Python 2 from
4 * Hye-Shik Chang <perky@fallin.lv>.
5 *
6 * Copyright 2012 Christian Jurk <commx@commx.ws>
7 *
8 * This program is free software; you can redistribute it and/or modify
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
11 * License, or (at your option) any later version.
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 *
18 * You should have received a copy of the GNU Lesser General Public License
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 *
23 */
24
25 #include <Python.h>
26 #include <datetime.h>
27 #include <rrd.h>
28
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)
37 #define PyRRD_Int_FromString(x, y, z) PyLong_FromString(x,y,z)
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)
44 #define PyRRD_Int_FromString(x, y, z) PyInt_FromString(x,y,z)
45 #endif
46
47 /** Binding version. */
48 static const char *_version = "0.1.2";
49
50 /** Exception types. */
51 static PyObject *rrdtool_OperationalError;
52 static PyObject *rrdtool_ProgrammingError;
53
54 static char **rrdtool_argv = NULL;
55 static int rrdtool_argc = 0;
56
57 /* extern getopt state */
58 extern int optind, opterr;
59
60
61 /**
62 * Helper function to convert Python objects into a representation that the
63 * rrdtool functions can work with.
64 *
65 * @param command RRDtool command name
66 * @param args Command arguments
67 * @return Zero if the function succeeds, otherwise -1
68 */
69 static int
70 convert_args(char *command, PyObject *args)
71 {
72 PyObject *o, *lo;
73 int i, j, args_count, argv_count, element_count;
74
75 argv_count = element_count = 0;
76 args_count = PyTuple_Size(args);
77
78 for (i = 0; i < args_count; i++) {
79 o = PyTuple_GET_ITEM(args, i);
80
81 if (PyRRD_String_Check(o))
82 element_count++;
83 else if (PyList_CheckExact(o))
84 element_count += PyList_Size(o);
85 else {
86 PyErr_Format(PyExc_TypeError,
87 "Argument %d must be str or a list of str", i);
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
97 for (i = 0; i < args_count; i++) {
98 o = PyTuple_GET_ITEM(args, i);
99
100 if (PyRRD_String_Check(o))
101 rrdtool_argv[++argv_count] = PyRRD_String_AS_STRING(o);
102 else if (PyList_CheckExact(o)) {
103 for (j = 0; j < PyList_Size(o); j++) {
104 lo = PyList_GetItem(o, j);
105
106 if (PyRRD_String_Check(lo))
107 rrdtool_argv[++argv_count] = PyRRD_String_AS_STRING(lo);
108 else {
109 PyMem_Del(rrdtool_argv);
110 PyErr_Format(PyExc_TypeError,
111 "Element %d in argument %d must be str", j, i);
112 return -1;
113 }
114 }
115 } else {
116 PyMem_Del(rrdtool_argv);
117 PyErr_Format(rrdtool_ProgrammingError,
118 "Argument %d must be str or list of str", i);
119 return -1;
120 }
121 }
122
123 rrdtool_argv[0] = command;
124 rrdtool_argc = element_count + 1;
125
126 /* reset getopt state */
127 opterr = optind = 0;
128
129 return 0;
130 }
131
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 */
148 static PyObject *
149 _rrdtool_util_info2dict(const rrd_info_t *data)
150 {
151 PyObject *dict, *val;
152
153 dict = PyDict_New();
154
155 while (data) {
156 val = NULL;
157
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:
176 val = PyRRD_String_FromString(data->value.u_str);
177 break;
178
179 case RD_I_BLO:
180 val = PyRRD_String_FromStringAndSize(
181 (char *)data->value.u_blo.ptr,
182 data->value.u_blo.size);
183 break;
184 default:
185 break;
186 }
187
188 if (val != NULL) {
189 PyDict_SetItemString(dict, data->key, val);
190 Py_DECREF(val);
191 }
192
193 data = data->next;
194 }
195
196 return dict;
197 }
198
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\
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\
210 [RRA:CF:xff:steps:rows]\n\n\
211 Full documentation can be found at:\n\
212 http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
213
214 static PyObject *
215 _rrdtool_create(PyObject *self, PyObject *args)
216 {
217 PyObject *ret;
218
219 if (convert_args("create", args) == -1)
220 return NULL;
221
222 if (rrd_create(rrdtool_argc, rrdtool_argv) == -1) {
223 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
224 rrd_clear_error();
225 ret = NULL;
226 } else {
227 Py_INCREF(Py_None);
228 ret = Py_None;
229 }
230
231 destroy_args();
232 return ret;
233 }
234
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\
239 [-n|--no-header]\n\
240 [-d|--daemon address]\n\
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
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\
275 Full documentation can be found at:\n\
276 http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
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
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
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\
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\
336 Full documentation can be found at:\n\
337 http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
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
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));
372
373 for (i = 0; i < ds_cnt; i++)
374 PyTuple_SET_ITEM(dsnam_tup, i, PyRRD_String_FromString(ds_namv[i]));
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
386 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
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
401 static char _rrdtool_flushcached__doc__[] = "Flush RRD files from memory.\n\n\
402 Usage: flushcached(args..)\n\
403 Arguments:\n\n\
404 [-d|--daemon address]\n\
405 filename\n\
406 [filename ...]\n\n\
407 Full documentation can be found at:\n\
408 http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html";
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
431 #ifdef HAVE_RRD_GRAPH
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\
455 [--week-fmt strftime format string]\n\
456 [--left-axis-formatter formatter-name]\n\
457 [--left-axis-format format-string]\n\
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\
471 [-d|--daemon address]\n\
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\
484 [-a|--imgformat {PNG,SVG,EPS,PDF,XML,XMLENUM,JSON,JSONTIME,CSV,TSV,SSV}]\n\
485 [-i|--interlaced]\n\
486 [-T|--tabwidth value]\n\
487 [-b|--base value]\n\
488 [-W|--watermark string]\n\
489 [-Z|--use-nan-for-all-missing-data]\n\
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\
493 Full documentation can be found at:\n\
494 http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
495
496 static PyObject *
497 _rrdtool_graph(PyObject *self, PyObject *args, PyObject *kwargs)
498 {
499 PyObject *ret;
500 int xsize, ysize, i;
501 double ymin, ymax;
502 char **calcpr;
503
504 if (convert_args("graph", args) == -1)
505 return NULL;
506
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 {
513 ret = PyTuple_New(3);
514
515 PyTuple_SET_ITEM(ret, 0, PyRRD_Int_FromLong((long) xsize));
516 PyTuple_SET_ITEM(ret, 1, PyRRD_Int_FromLong((long) ysize));
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++) {
525 t = PyRRD_String_FromString(calcpr[i]);
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 }
534 }
535
536 destroy_args();
537 return ret;
538 }
539
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";
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
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
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\
680 Full documentation can be found at:\n\
681 http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html";
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\
709 [--rraindex number]\n\
710 [-d|--daemon address]\n\n\
711 Full documentation can be found at:\n\
712 http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html";
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
728 ret = PyRRD_Int_FromLong((long) ts);
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\
739 [-d|--daemon address]\n\n\
740 Full documentation can be found at:\n\
741 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
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
757 ret = PyRRD_Int_FromLong((long) ts);
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\
771 Full documentation can be found at:\n\
772 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
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\
798 Usage: info(filename, ...)\n\
799 Arguments:\n\n\
800 filename\n\
801 [-d|--daemon address]\n\
802 [-F|--noflush]\n\n\
803 Full documentation can be found at:\n\
804 http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html";
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 }
823
824 destroy_args();
825 return ret;
826 }
827
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) {
850 PyErr_SetString(rrdtool_ProgrammingError, "Missing filename argument");
851 return NULL;
852 }
853
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,
871 "date",
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));
880 PyDict_SetItemString(ret, "ds", ds_dict);
881
882 for (i = 0; i < ds_cnt; i++) {
883 PyDict_SetItemString(ds_dict,
884 ds_names[i],
885 PyRRD_Int_FromString(last_ds[i], NULL, 10));
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
900 static char _rrdtool_lib_version__doc__[] = "Get the version this binding "\
901 "was compiled against.";
902
903 /**
904 * Returns a str object that contains the librrd version.
905 *
906 * @return librrd version (Python str object)
907 */
908 static PyObject *
909 _rrdtool_lib_version(PyObject *self, PyObject *args)
910 {
911 return PyRRD_String_FromString(rrd_strversion());
912 }
913
914 /** Method table. */
915 static PyMethodDef rrdtool_methods[] = {
916 {"create", (PyCFunction)_rrdtool_create,
917 METH_VARARGS, _rrdtool_create__doc__},
918 {"dump", (PyCFunction)_rrdtool_dump,
919 METH_VARARGS, _rrdtool_dump__doc__},
920 {"update", (PyCFunction)_rrdtool_update,
921 METH_VARARGS, _rrdtool_update__doc__},
922 {"updatev", (PyCFunction)_rrdtool_updatev,
923 METH_VARARGS, _rrdtool_updatev__doc__},
924 {"fetch", (PyCFunction)_rrdtool_fetch,
925 METH_VARARGS, _rrdtool_fetch__doc__},
926 {"flushcached", (PyCFunction)_rrdtool_flushcached,
927 METH_VARARGS, _rrdtool_flushcached__doc__},
928 #ifdef HAVE_RRD_GRAPH
929 {"graph", (PyCFunction)_rrdtool_graph,
930 METH_VARARGS, _rrdtool_graph__doc__},
931 {"graphv", (PyCFunction)_rrdtool_graphv,
932 METH_VARARGS, _rrdtool_graphv__doc__},
933 {"xport", (PyCFunction)_rrdtool_xport,
934 METH_VARARGS, _rrdtool_xport__doc__},
935 #endif
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__},
946 {"lastupdate", (PyCFunction)_rrdtool_lastupdate,
947 METH_VARARGS, _rrdtool_lastupdate__doc__},
948 {"lib_version", (PyCFunction)_rrdtool_lib_version,
949 METH_VARARGS, _rrdtool_lib_version__doc__},
950 {NULL, NULL, 0, NULL}
951 };
952
953 /** Library init function. */
954 #ifdef HAVE_PY3K
955 static struct PyModuleDef rrdtoolmodule = {
956 PyModuleDef_HEAD_INIT,
957 "rrdtool",
958 "rrdtool bindings for Python",
959 -1,
960 rrdtool_methods
961 };
962
963 #endif
964
965 #ifdef HAVE_PY3K
966 PyMODINIT_FUNC
967 PyInit_rrdtool(void)
968 #else
969 void
970 initrrdtool(void)
971 #endif
972 {
973 PyObject *m;
974
975 PyDateTime_IMPORT; /* initialize PyDateTime_ functions */
976
977 #ifdef HAVE_PY3K
978 m = PyModule_Create(&rrdtoolmodule);
979 #else
980 m = Py_InitModule3("rrdtool",
981 rrdtool_methods,
982 "rrdtool bindings for Python");
983 #endif
984
985 if (m == NULL)
986 #ifdef HAVE_PY3K
987 return NULL;
988 #else
989 return;
990 #endif
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);
1001 PyModule_AddObject(m, "__version__", PyRRD_String_FromString(_version));
1002
1003 #ifdef HAVE_PY3K
1004 return m;
1005 #endif
1006 }