Split the Values class into PluginData and Values to use PluginData as a baseclass...
[collectd.git] / src / pyvalues.c
1 #include <Python.h>
2 #include <structmember.h>
3
4 #include "collectd.h"
5 #include "common.h"
6
7 #include "cpython.h"
8
9 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
10                 "For dispatching values this can be set to 0 which means \"now\".\n"
11                 "This means the time the value is actually dispatched, not the time\n"
12                 "it was set to 0.";
13
14 static char host_doc[] = "The hostname of the host this value was read from.\n"
15                 "For dispatching this can be set to an empty string which means\n"
16                 "the local hostname as defined in the collectd.conf.";
17
18 static char type_doc[] = "The type of this value. This type has to be defined\n"
19                 "in your types.db. Attempting to set it to any other value will\n"
20                 "raise a TypeError exception.\n"
21                 "Assigning a type is mandetory, calling dispatch without doing\n"
22                 "so will raise a RuntimeError exception.";
23
24 static char type_instance_doc[] = "";
25
26 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
27                 "member to an empty string will insert \"python\" upon dispatching.";
28
29 static char plugin_instance_doc[] = "";
30
31 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
32                 "and Notification. It is pretty useless by itself and was therefore not\n"
33                 "not exported to the collectd module.";
34
35 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
36         PluginData *self;
37         
38         self = (PluginData *) type->tp_alloc(type, 0);
39         if (self == NULL)
40                 return NULL;
41         
42         self->time = 0;
43         self->host[0] = 0;
44         self->plugin[0] = 0;
45         self->plugin_instance[0] = 0;
46         self->type[0] = 0;
47         self->type_instance[0] = 0;
48         return (PyObject *) self;
49 }
50
51 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
52         PluginData *self = (PluginData *) s;
53         double time = 0;
54         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
55         static char *kwlist[] = {"type", "plugin_instance", "type_instance",
56                         "plugin", "host", "time", NULL};
57         
58         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
59                         &plugin_instance, &type_instance, &plugin, &host, &time))
60                 return -1;
61         
62         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
63                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
64                 return -1;
65         }
66
67         sstrncpy(self->host, host, sizeof(self->host));
68         sstrncpy(self->plugin, plugin, sizeof(self->plugin));
69         sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
70         sstrncpy(self->type, type, sizeof(self->type));
71         sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
72         
73         self->time = time;
74         return 0;
75 }
76
77 static PyObject *PluginData_repr(PyObject *s) {
78         PluginData *self = (PluginData *) s;
79         
80         return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
81                         *self->type_instance ? "',type_instance='" : "", self->type_instance,
82                         *self->plugin ? "',plugin='" : "", self->plugin,
83                         *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
84                         *self->host ? "',host='" : "", self->host,
85                         (long unsigned) self->time);
86 }
87
88 static PyMemberDef PluginData_members[] = {
89         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
90         {NULL}
91 };
92
93 static PyObject *PluginData_getstring(PyObject *self, void *data) {
94         const char *value = ((char *) self) + (int) data;
95         
96         return PyString_FromString(value);
97 }
98
99 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
100         char *old;
101         const char *new;
102         
103         if (value == NULL) {
104                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
105                 return -1;
106         }
107         new = PyString_AsString(value);
108         if (new == NULL) return -1;
109         old = ((char *) self) + (int) data;
110         sstrncpy(old, new, DATA_MAX_NAME_LEN);
111         return 0;
112 }
113
114 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
115         char *old;
116         const char *new;
117         
118         if (value == NULL) {
119                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
120                 return -1;
121         }
122         new = PyString_AsString(value);
123         if (new == NULL) return -1;
124
125         if (plugin_get_ds(new) == NULL) {
126                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
127                 return -1;
128         }
129
130         old = ((char *) self) + (int) data;
131         sstrncpy(old, new, DATA_MAX_NAME_LEN);
132         return 0;
133 }
134
135 static PyGetSetDef PluginData_getseters[] = {
136         {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
137         {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
138         {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
139         {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
140         {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
141         {NULL}
142 };
143
144 PyTypeObject PluginDataType = {
145         PyObject_HEAD_INIT(NULL)
146         0,                         /* Always 0 */
147         "collectd.PluginData",     /* tp_name */
148         sizeof(PluginData),        /* tp_basicsize */
149         0,                         /* Will be filled in later */
150         0,                         /* tp_dealloc */
151         0,                         /* tp_print */
152         0,                         /* tp_getattr */
153         0,                         /* tp_setattr */
154         0,                         /* tp_compare */
155         PluginData_repr,           /* tp_repr */
156         0,                         /* tp_as_number */
157         0,                         /* tp_as_sequence */
158         0,                         /* tp_as_mapping */
159         0,                         /* tp_hash */
160         0,                         /* tp_call */
161         0,                         /* tp_str */
162         0,                         /* tp_getattro */
163         0,                         /* tp_setattro */
164         0,                         /* tp_as_buffer */
165         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
166         PluginData_doc        ,    /* tp_doc */
167         0,                         /* tp_traverse */
168         0,                         /* tp_clear */
169         0,                         /* tp_richcompare */
170         0,                         /* tp_weaklistoffset */
171         0,                         /* tp_iter */
172         0,                         /* tp_iternext */
173         0,                         /* tp_methods */
174         PluginData_members,        /* tp_members */
175         PluginData_getseters,      /* tp_getset */
176         0,                         /* tp_base */
177         0,                         /* tp_dict */
178         0,                         /* tp_descr_get */
179         0,                         /* tp_descr_set */
180         0,                         /* tp_dictoffset */
181         PluginData_init,           /* tp_init */
182         0,                         /* tp_alloc */
183         PluginData_new             /* tp_new */
184 };
185
186 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
187                 "the same data source. This value has to be a positive integer, so you can't\n"
188                 "submit more than one value per second. If this member is set to a\n"
189                 "non-positive value, the default value as specified in the config file will\n"
190                 "be used (default: 10).\n"
191                 "\n"
192                 "If you submit values more often than the specified interval, the average\n"
193                 "will be used. If you submit less values, your graphes will have gaps.";
194
195 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
196                 "It has to be a sequence (a tuple or list) of numbers.\n"
197                 "The size of the sequence and the type of its content depend on the type\n"
198                 "member your types.db file. For more information on this read the types.db\n"
199                 "man page.\n"
200                 "\n"
201                 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
202                 "exception will be raised. If the content of the sequence is not a number,\n"
203                 "a TypeError exception will be raised.";
204
205 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
206                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
207                 "\n"
208                 "Dispatch this instance to the collectd process. A values object a members\n"
209                 "for each of the possible arguments for this method. For a detailed explanation\n"
210                 "of these parameters see the member of the same same.\n"
211                 "\n"
212                 "If you do not submit a parameter the value saved in its member will be submitted.\n"
213                 "If you do provide a parameter it will be used instead, without altering the member.";
214
215 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
216
217 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
218         Values *self;
219         
220         self = (Values *) PluginData_new(type, args, kwds);
221         if (self == NULL)
222                 return NULL;
223         
224         self->values = PyList_New(0);
225         self->interval = 0;
226         return (PyObject *) self;
227 }
228
229 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
230         Values *self = (Values *) s;
231         int interval = 0, ret;
232         double time = 0;
233         PyObject *values = NULL, *tmp;
234         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
235         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
236                         "plugin", "host", "time", "interval", NULL};
237         
238         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
239                         &type, &values, &plugin_instance, &type_instance,
240                         &plugin, &host, &time, &interval))
241                 return -1;
242         
243         tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
244         if (tmp == NULL)
245                 return -1;
246         ret = PluginDataType.tp_init(s, tmp, NULL);
247         Py_DECREF(tmp);
248         if (ret != 0)
249                 return -1;
250         
251         if (values == NULL) {
252                 values = PyList_New(0);
253                 PyErr_Clear();
254         } else {
255                 Py_INCREF(values);
256         }
257         
258         tmp = self->values;
259         self->values = values;
260         Py_XDECREF(tmp);
261         
262         self->interval = interval;
263         return 0;
264 }
265
266 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
267         int i, ret;
268         const data_set_t *ds;
269         Py_ssize_t size;
270         value_t *value;
271         value_list_t value_list = VALUE_LIST_INIT;
272         PyObject *values = self->values;
273         double time = self->data.time;
274         int interval = self->interval;
275         const char *host = self->data.host;
276         const char *plugin = self->data.plugin;
277         const char *plugin_instance = self->data.plugin_instance;
278         const char *type = self->data.type;
279         const char *type_instance = self->data.type_instance;
280         
281         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
282                         "plugin", "host", "time", "interval", NULL};
283         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
284                         &type, &values, &plugin_instance, &type_instance,
285                         &plugin, &host, &time, &interval))
286                 return NULL;
287
288         if (type[0] == 0) {
289                 PyErr_SetString(PyExc_RuntimeError, "type not set");
290                 return NULL;
291         }
292         ds = plugin_get_ds(type);
293         if (ds == NULL) {
294                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
295                 return NULL;
296         }
297         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
298                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
299                 return NULL;
300         }
301         size = PySequence_Length(values);
302         if (size != ds->ds_num) {
303                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %zd", type, ds->ds_num, size);
304                 return NULL;
305         }
306         value = malloc(size * sizeof(*value));
307         for (i = 0; i < size; ++i) {
308                 PyObject *item, *num;
309                 item = PySequence_GetItem(values, i);
310                 if (ds->ds->type == DS_TYPE_COUNTER) {
311                         num = PyNumber_Long(item);
312                         if (num != NULL)
313                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
314                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
315                         num = PyNumber_Float(item);
316                         if (num != NULL)
317                                 value[i].gauge = PyFloat_AsDouble(num);
318                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
319                         /* This might overflow without raising an exception.
320                          * Not much we can do about it */
321                         num = PyNumber_Long(item);
322                         if (num != NULL)
323                                 value[i].derive = PyLong_AsLongLong(num);
324                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
325                         /* This might overflow without raising an exception.
326                          * Not much we can do about it */
327                         num = PyNumber_Long(item);
328                         if (num != NULL)
329                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
330                 } else {
331                         free(value);
332                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
333                         return NULL;
334                 }
335                 if (PyErr_Occurred() != NULL) {
336                         free(value);
337                         return NULL;
338                 }
339         }
340         value_list.values = value;
341         value_list.values_len = size;
342         value_list.time = time;
343         value_list.interval = interval;
344         sstrncpy(value_list.host, host, sizeof(value_list.host));
345         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
346         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
347         sstrncpy(value_list.type, type, sizeof(value_list.type));
348         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
349         value_list.meta = NULL;
350         if (value_list.host[0] == 0)
351                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
352         if (value_list.plugin[0] == 0)
353                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
354         Py_BEGIN_ALLOW_THREADS;
355         ret = plugin_dispatch_values(&value_list);
356         Py_END_ALLOW_THREADS;
357         if (ret != 0) {
358                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
359                 return NULL;
360         }
361         free(value);
362         Py_RETURN_NONE;
363 }
364
365 static PyObject *Values_repr(PyObject *s) {
366         PyObject *ret, *valuestring = NULL;
367         Values *self = (Values *) s;
368         
369         if (self->values != NULL)
370                 valuestring = PyObject_Repr(self->values);
371         if (valuestring == NULL)
372                 return NULL;
373         
374         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
375                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
376                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
377                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
378                         *self->data.host ? "',host='" : "", self->data.host,
379                         (long unsigned) self->data.time, self->interval,
380                         valuestring ? PyString_AsString(valuestring) : "[]");
381         Py_XDECREF(valuestring);
382         return ret;
383 }
384
385 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
386         Values *v = (Values *) self;
387         Py_VISIT(v->values);
388         return 0;
389 }
390
391 static int Values_clear(PyObject *self) {
392         Values *v = (Values *) self;
393         Py_CLEAR(v->values);
394         return 0;
395 }
396
397 static void Values_dealloc(PyObject *self) {
398         Values_clear(self);
399         self->ob_type->tp_free(self);
400 }
401
402 static PyMemberDef Values_members[] = {
403         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
404         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
405         {NULL}
406 };
407
408 static PyMethodDef Values_methods[] = {
409         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
410         {NULL}
411 };
412
413 PyTypeObject ValuesType = {
414         PyObject_HEAD_INIT(NULL)
415         0,                         /* Always 0 */
416         "collectd.Values",         /* tp_name */
417         sizeof(Values),            /* tp_basicsize */
418         0,                         /* Will be filled in later */
419         Values_dealloc,            /* tp_dealloc */
420         0,                         /* tp_print */
421         0,                         /* tp_getattr */
422         0,                         /* tp_setattr */
423         0,                         /* tp_compare */
424         Values_repr,               /* tp_repr */
425         0,                         /* tp_as_number */
426         0,                         /* tp_as_sequence */
427         0,                         /* tp_as_mapping */
428         0,                         /* tp_hash */
429         0,                         /* tp_call */
430         0,                         /* tp_str */
431         0,                         /* tp_getattro */
432         0,                         /* tp_setattro */
433         0,                         /* tp_as_buffer */
434         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
435         Values_doc,                /* tp_doc */
436         Values_traverse,           /* tp_traverse */
437         Values_clear,              /* tp_clear */
438         0,                         /* tp_richcompare */
439         0,                         /* tp_weaklistoffset */
440         0,                         /* tp_iter */
441         0,                         /* tp_iternext */
442         Values_methods,            /* tp_methods */
443         Values_members,            /* tp_members */
444         0,                         /* tp_getset */
445         0,                         /* tp_base */
446         0,                         /* tp_dict */
447         0,                         /* tp_descr_get */
448         0,                         /* tp_descr_set */
449         0,                         /* tp_dictoffset */
450         Values_init,               /* tp_init */
451         0,                         /* tp_alloc */
452         Values_new                 /* tp_new */
453 };