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