fix off by 1 error
[rrdtool.git] / bindings / python / rrdtoolmodule.c
index f829734..aea40f1 100644 (file)
 # define UNUSED(x) x
 #endif
 
-static const char *__version__ = "$Revision: 1.14 $";
+
+#include "../../rrd_config.h"
+static const char *__version__ = PACKAGE_VERSION;
 
 #include "Python.h"
-#include "rrd.h"
-#include "rrd_extra.h"
+#include "../../src/rrd_tool.h"
+//#include "rrd.h"
+//#include "rrd_extra.h"
 
 static PyObject *ErrorObject;
 extern int optind;
@@ -57,28 +60,59 @@ static int create_args(
     int *argc,
     char ***argv)
 {
-    PyObject *o;
-    int       size, i;
-
-    size = PyTuple_Size(args);
+    PyObject *o, *lo;
+    int       args_count,
+              argv_count,
+              element_count,
+              i, j;
+
+    args_count = PyTuple_Size(args);
+    element_count = 0;
+    for (i = 0; i < args_count; i++) {
+        o = PyTuple_GET_ITEM(args, i);
+        if (PyString_Check(o))
+            element_count++;
+        else if (PyList_CheckExact(o))
+                element_count += PyList_Size(o);
+             else {
+                 PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
+                 return -1;
+             }
+    }
+   
     *argv = PyMem_New(char *,
-                      size + 1);
+                      element_count + 1);
 
     if (*argv == NULL)
         return -1;
 
-    for (i = 0; i < size; i++) {
+    argv_count = 0;
+    for (i = 0; i < args_count; i++) {
         o = PyTuple_GET_ITEM(args, i);
-        if (PyString_Check(o))
-            (*argv)[i + 1] = PyString_AS_STRING(o);
-        else {
-            PyMem_Del(*argv);
-            PyErr_Format(PyExc_TypeError, "argument %d must be string", i);
-            return -1;
-        }
+        if (PyString_Check(o)) {
+            argv_count++;
+            (*argv)[argv_count] = PyString_AS_STRING(o);
+        } else if (PyList_CheckExact(o))
+                   for (j = 0; j < PyList_Size(o); j++) {
+                       lo = PyList_GetItem(o, j);
+                       if (PyString_Check(lo)) {
+                           argv_count++;
+                           (*argv)[argv_count] = PyString_AS_STRING(lo);
+                       } else {
+                             PyMem_Del(*argv);
+                             PyErr_Format(PyExc_TypeError, "element %d in argument %d must be string", j, i);
+                             return -1;
+                       }
+                   }
+               else {
+                   PyMem_Del(*argv);
+                   PyErr_Format(PyExc_TypeError, "argument %d must be string or list of strings", i);
+                   return -1;
+               }
     }
+
     (*argv)[0] = command;
-    *argc = size + 1;
+    *argc = element_count + 1;
 
     /* reset getopt state */
     opterr = optind = 0;
@@ -183,7 +217,7 @@ static PyObject *PyRRD_fetch(
         unsigned long i, j, row;
         rrd_value_t dv;
 
-        row = ((end - start) / step + 1);
+        row = (end - start) / step;
 
         r = PyTuple_New(3);
         range_tup = PyTuple_New(3);
@@ -218,9 +252,9 @@ static PyObject *PyRRD_fetch(
         }
 
         for (i = 0; i < ds_cnt; i++)
-            free(ds_namv[i]);
-        free(ds_namv);  /* rrdtool don't use PyMem_Malloc :) */
-        free(data);
+            rrd_freemem(ds_namv[i]);
+        rrd_freemem(ds_namv);   /* rrdtool don't use PyMem_Malloc :) */
+        rrd_freemem(data);
     }
 
     destroy_args(&argv);
@@ -281,9 +315,9 @@ static PyObject *PyRRD_graph(
                 t = PyString_FromString(calcpr[i]);
                 PyList_Append(e, t);
                 Py_DECREF(t);
-                free(calcpr[i]);
+                rrd_freemem(calcpr[i]);
             }
-            free(calcpr);
+            rrd_freemem(calcpr);
         } else {
             Py_INCREF(Py_None);
             PyTuple_SET_ITEM(r, 2, Py_None);
@@ -402,6 +436,45 @@ static PyObject *PyRRD_resize(
     return r;
 }
 
+static PyObject *PyDict_FromInfo(
+    rrd_info_t * data)
+{
+    PyObject *r;
+
+    r = PyDict_New();
+    while (data) {
+        PyObject *val = NULL;
+
+        switch (data->type) {
+        case RD_I_VAL:
+            val = isnan(data->value.u_val)
+                ? (Py_INCREF(Py_None), Py_None)
+                : PyFloat_FromDouble(data->value.u_val);
+            break;
+        case RD_I_CNT:
+            val = PyLong_FromUnsignedLong(data->value.u_cnt);
+            break;
+        case RD_I_INT:
+            val = PyLong_FromLong(data->value.u_int);
+            break;
+        case RD_I_STR:
+            val = PyString_FromString(data->value.u_str);
+            break;
+        case RD_I_BLO:
+            val =
+                PyString_FromStringAndSize((char *) data->value.u_blo.ptr,
+                                           data->value.u_blo.size);
+            break;
+        }
+        if (val) {
+            PyDict_SetItemString(r, data->key, val);
+            Py_DECREF(val);
+        }
+        data = data->next;
+    }
+    return r;
+}
+
 static char PyRRD_info__doc__[] =
     "info(filename): extract header information from an rrd";
 
@@ -409,101 +482,186 @@ static PyObject *PyRRD_info(
     PyObject UNUSED(*self),
     PyObject * args)
 {
-    PyObject *r, *t, *ds;
-    rrd_t     rrd;
-    char     *filename;
-    unsigned long i, j;
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
 
-    if (!PyArg_ParseTuple(args, "s:info", &filename))
+    if (create_args("info", args, &argc, &argv) < 0)
         return NULL;
 
-    if (!rrd_open(filename, &rrd, RRD_READONLY) == -1) {
+    if ((data = rrd_info(argc, argv)) == NULL) {
         PyErr_SetString(ErrorObject, rrd_get_error());
         rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_graphv__doc__[] =
+    "graphv is called in the same manner as graph";
+
+static PyObject *PyRRD_graphv(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
+
+    if (create_args("graphv", args, &argc, &argv) < 0)
         return NULL;
+
+    if ((data = rrd_graph_v(argc, argv)) == NULL) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
     }
-#define DICTSET_STR(dict, name, value) \
-    t = PyString_FromString(value); \
-    PyDict_SetItemString(dict, name, t); \
-    Py_DECREF(t);
-
-#define DICTSET_CNT(dict, name, value) \
-    t = PyInt_FromLong((long)value); \
-    PyDict_SetItemString(dict, name, t); \
-    Py_DECREF(t);
-
-#define DICTSET_VAL(dict, name, value) \
-    t = isnan(value) ? (Py_INCREF(Py_None), Py_None) :  \
-        PyFloat_FromDouble((double)value); \
-    PyDict_SetItemString(dict, name, t); \
-    Py_DECREF(t);
 
-    r = PyDict_New();
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_updatev__doc__[] =
+    "updatev is called in the same manner as update";
+
+static PyObject *PyRRD_updatev(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+    rrd_info_t *data;
+
+    if (create_args("updatev", args, &argc, &argv) < 0)
+        return NULL;
+
+    if ((data = rrd_update_v(argc, argv)) == NULL) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        r = PyDict_FromInfo(data);
+        rrd_info_free(data);
+    }
+
+    destroy_args(&argv);
+    return r;
+}
 
-    DICTSET_STR(r, "filename", filename);
-    DICTSET_STR(r, "rrd_version", rrd.stat_head->version);
-    DICTSET_CNT(r, "step", rrd.stat_head->pdp_step);
-    DICTSET_CNT(r, "last_update", rrd.live_head->last_up);
-
-    ds = PyDict_New();
-    PyDict_SetItemString(r, "ds", ds);
-    Py_DECREF(ds);
-
-    for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
-        PyObject *d;
-
-        d = PyDict_New();
-        PyDict_SetItemString(ds, rrd.ds_def[i].ds_nam, d);
-        Py_DECREF(d);
-
-        DICTSET_STR(d, "ds_name", rrd.ds_def[i].ds_nam);
-        DICTSET_STR(d, "type", rrd.ds_def[i].dst);
-        DICTSET_CNT(d, "minimal_heartbeat",
-                    rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
-        DICTSET_VAL(d, "min", rrd.ds_def[i].par[DS_min_val].u_val);
-        DICTSET_VAL(d, "max", rrd.ds_def[i].par[DS_max_val].u_val);
-        DICTSET_STR(d, "last_ds", rrd.pdp_prep[i].last_ds);
-        DICTSET_VAL(d, "value", rrd.pdp_prep[i].scratch[PDP_val].u_val);
-        DICTSET_CNT(d, "unknown_sec",
-                    rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+static char PyRRD_flushcached__doc__[] =
+  "flush(args..): flush RRD files from memory\n"
+  "   flush [--daemon address] file [file ...]";
+
+static PyObject *PyRRD_flushcached(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc;
+    char    **argv;
+
+    if (create_args("flushcached", args, &argc, &argv) < 0)
+        return NULL;
+
+    if (rrd_flushcached(argc, argv) != 0) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        Py_INCREF(Py_None);
+        r = Py_None;
     }
 
-    ds = PyList_New(rrd.stat_head->rra_cnt);
-    PyDict_SetItemString(r, "rra", ds);
-    Py_DECREF(ds);
+    destroy_args(&argv);
+    return r;
+}
+
+static char PyRRD_xport__doc__[] =
+    "xport(args..): dictionary representation of data stored in RRDs\n"
+    "    [-s|--start seconds] [-e|--end seconds] [-m|--maxrows rows]"
+    "[--step value] [--daemon address] [DEF:vname=rrd:ds-name:CF]"
+    "[CDEF:vname=rpn-expression] [XPORT:vname[:legend]]";
+
 
-    for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
-        PyObject *d, *cdp;
+static PyObject *PyRRD_xport(
+    PyObject UNUSED(*self),
+    PyObject * args)
+{
+    PyObject *r;
+    int       argc, xsize;
+    char    **argv, **legend_v;
+    time_t    start, end;
+    unsigned long step, col_cnt;
+    rrd_value_t *data, *datai;
 
-        d = PyDict_New();
-        PyList_SET_ITEM(ds, i, d);
+    if (create_args("xport", args, &argc, &argv) < 0)
+        return NULL;
 
-        DICTSET_STR(d, "cf", rrd.rra_def[i].cf_nam);
-        DICTSET_CNT(d, "rows", rrd.rra_def[i].row_cnt);
-        DICTSET_CNT(d, "pdp_per_row", rrd.rra_def[i].pdp_cnt);
-        DICTSET_VAL(d, "xff", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+    if (rrd_xport(argc, argv, &xsize, &start, &end,
+                  &step, &col_cnt, &legend_v, &data) == -1) {
+        PyErr_SetString(ErrorObject, rrd_get_error());
+        rrd_clear_error();
+        r = NULL;
+    } else {
+        PyObject *meta_dict, *data_list, *legend_list, *t;
+        unsigned long i, j;
+        rrd_value_t dv;
 
-        cdp = PyList_New(rrd.stat_head->ds_cnt);
-        PyDict_SetItemString(d, "cdp_prep", cdp);
-        Py_DECREF(cdp);
+        unsigned long row_cnt = ((end - start) / step) + 1;
 
-        for (j = 0; j < rrd.stat_head->ds_cnt; j++) {
-            PyObject *cdd;
+        r = PyDict_New();
+        meta_dict = PyDict_New();
+        legend_list = PyList_New(col_cnt);
+        data_list = PyList_New(row_cnt);
+        PyDict_SetItem(r, PyString_FromString("meta"), meta_dict);
+        PyDict_SetItem(r, PyString_FromString("data"), data_list);
 
-            cdd = PyDict_New();
-            PyList_SET_ITEM(cdp, j, cdd);
+        datai = data;
+
+        PyDict_SetItem(meta_dict, PyString_FromString("start"), PyInt_FromLong((long) start));
+        PyDict_SetItem(meta_dict, PyString_FromString("end"), PyInt_FromLong((long) end));
+        PyDict_SetItem(meta_dict, PyString_FromString("step"), PyInt_FromLong((long) step));
+        PyDict_SetItem(meta_dict, PyString_FromString("rows"), PyInt_FromLong((long) row_cnt));
+        PyDict_SetItem(meta_dict, PyString_FromString("columns"), PyInt_FromLong((long) col_cnt));
+        PyDict_SetItem(meta_dict, PyString_FromString("legend"), legend_list);
 
-            DICTSET_VAL(cdd, "value",
-                        rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
-                                     j].scratch[CDP_val].u_val);
-            DICTSET_CNT(cdd, "unknown_datapoints",
-                        rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
-                                     j].scratch[CDP_unkn_pdp_cnt].u_cnt);
+        for (i = 0; i < col_cnt; i++) {
+            PyList_SET_ITEM(legend_list, i, PyString_FromString(legend_v[i]));
         }
-    }
 
-    rrd_free(&rrd);
+        for (i = 0; i < row_cnt; i++) {
+            t = PyTuple_New(col_cnt);
+            PyList_SET_ITEM(data_list, i, t);
+
+            for (j = 0; j < col_cnt; j++) {
+                dv = *(datai++);
+                if (isnan(dv)) {
+                    PyTuple_SET_ITEM(t, j, Py_None);
+                    Py_INCREF(Py_None);
+                } else {
+                    PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double) dv));
+                }
+            }
+        }
 
+        for (i = 0; i < col_cnt; i++) {
+            rrd_freemem(legend_v[i]);
+        }
+        rrd_freemem(legend_v);
+        rrd_freemem(data);
+    }
+    destroy_args(&argv);
     return r;
 }
 
@@ -520,6 +678,10 @@ static PyMethodDef _rrdtool_methods[] = {
     meth("last", PyRRD_last, PyRRD_last__doc__),
     meth("resize", PyRRD_resize, PyRRD_resize__doc__),
     meth("info", PyRRD_info, PyRRD_info__doc__),
+    meth("graphv", PyRRD_graphv, PyRRD_graphv__doc__),
+    meth("updatev", PyRRD_updatev, PyRRD_updatev__doc__),
+    meth("flushcached", PyRRD_flushcached, PyRRD_flushcached__doc__),
+    meth("xport", PyRRD_xport, PyRRD_xport__doc__),
     {NULL, NULL, 0, NULL}
 };