python: repr works for Values again.
[collectd.git] / src / pyvalues.c
1 /**
2  * collectd - src/pyvalues.c
3  * Copyright (C) 2009  Sven Trenkel
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Sven Trenkel <collectd at semidefinite.de>  
25  **/
26
27 #include <Python.h>
28 #include <structmember.h>
29
30 #include "collectd.h"
31 #include "common.h"
32
33 #include "cpython.h"
34
35 static PyObject *cpy_common_repr(PyObject *s) {
36         PyObject *ret, *tmp;
37         static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
38         static PyObject *l_host = NULL, *l_time = NULL;
39         PluginData *self = (PluginData *) s;
40         
41         if (l_type == NULL)
42                 l_type = cpy_string_to_unicode_or_bytes("(type=");
43         if (l_type_instance == NULL)
44                 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
45         if (l_plugin == NULL)
46                 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
47         if (l_plugin_instance == NULL)
48                 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
49         if (l_host == NULL)
50                 l_host = cpy_string_to_unicode_or_bytes(",host=");
51         if (l_time == NULL)
52                 l_time = cpy_string_to_unicode_or_bytes(",time=");
53         
54         if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
55                 return NULL;
56         
57         ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
58
59         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type);
60         tmp = cpy_string_to_unicode_or_bytes(self->type);
61         CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
62         if (tmp)
63                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
64         Py_XDECREF(tmp);
65
66         if (self->type_instance[0] != 0) {
67                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type_instance);
68                 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
69                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
70                 if (tmp)
71                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
72                 Py_XDECREF(tmp);
73         }
74
75         if (self->plugin[0] != 0) {
76                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin);
77                 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
78                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
79                 if (tmp)
80                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
81                 Py_XDECREF(tmp);
82         }
83
84         if (self->plugin_instance[0] != 0) {
85                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin_instance);
86                 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
87                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
88                 if (tmp)
89                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
90                 Py_XDECREF(tmp);
91         }
92
93         if (self->host[0] != 0) {
94                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_host);
95                 tmp = cpy_string_to_unicode_or_bytes(self->host);
96                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
97                 if (tmp)
98                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
99                 Py_XDECREF(tmp);
100         }
101
102         if (self->time != 0) {
103                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_time);
104                 tmp = PyInt_FromLong(self->time);
105                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
106                 if (tmp)
107                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
108                 Py_XDECREF(tmp);
109         }
110         return ret;
111 }
112
113 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
114                 "For dispatching values this can be set to 0 which means \"now\".\n"
115                 "This means the time the value is actually dispatched, not the time\n"
116                 "it was set to 0.";
117
118 static char host_doc[] = "The hostname of the host this value was read from.\n"
119                 "For dispatching this can be set to an empty string which means\n"
120                 "the local hostname as defined in the collectd.conf.";
121
122 static char type_doc[] = "The type of this value. This type has to be defined\n"
123                 "in your types.db. Attempting to set it to any other value will\n"
124                 "raise a TypeError exception.\n"
125                 "Assigning a type is mandetory, calling dispatch without doing\n"
126                 "so will raise a RuntimeError exception.";
127
128 static char type_instance_doc[] = "";
129
130 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
131                 "member to an empty string will insert \"python\" upon dispatching.";
132
133 static char plugin_instance_doc[] = "";
134
135 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
136                 "and Notification. It is pretty useless by itself and was therefore not\n"
137                 "exported to the collectd module.";
138
139 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
140         PluginData *self;
141         
142         self = (PluginData *) type->tp_alloc(type, 0);
143         if (self == NULL)
144                 return NULL;
145         
146         self->time = 0;
147         self->host[0] = 0;
148         self->plugin[0] = 0;
149         self->plugin_instance[0] = 0;
150         self->type[0] = 0;
151         self->type_instance[0] = 0;
152         return (PyObject *) self;
153 }
154
155 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
156         PluginData *self = (PluginData *) s;
157         double time = 0;
158         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
159         static char *kwlist[] = {"type", "plugin_instance", "type_instance",
160                         "plugin", "host", "time", NULL};
161         
162         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
163                         NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
164                 return -1;
165         
166         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
167                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
168                 return -1;
169         }
170
171         sstrncpy(self->host, host, sizeof(self->host));
172         sstrncpy(self->plugin, plugin, sizeof(self->plugin));
173         sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
174         sstrncpy(self->type, type, sizeof(self->type));
175         sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
176         
177         self->time = time;
178         return 0;
179 }
180
181 static PyObject *PluginData_repr(PyObject *s) {
182         PyObject *ret;
183         static PyObject *l_closing = NULL;
184         
185         if (l_closing == NULL)
186                 l_closing = cpy_string_to_unicode_or_bytes(")");
187         
188         if (l_closing == NULL)
189                 return NULL;
190         
191         ret = cpy_common_repr(s);
192         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
193         return ret;
194 }
195
196 static PyMemberDef PluginData_members[] = {
197         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
198         {NULL}
199 };
200
201 static PyObject *PluginData_getstring(PyObject *self, void *data) {
202         const char *value = ((char *) self) + (intptr_t) data;
203         
204         return cpy_string_to_unicode_or_bytes(value);
205 }
206
207 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
208         char *old;
209         const char *new;
210         
211         if (value == NULL) {
212                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
213                 return -1;
214         }
215         Py_INCREF(value);
216         new = cpy_unicode_or_bytes_to_string(&value);
217         if (new == NULL) {
218                 Py_DECREF(value);
219                 return -1;
220         }
221         old = ((char *) self) + (intptr_t) data;
222         sstrncpy(old, new, DATA_MAX_NAME_LEN);
223         Py_DECREF(value);
224         return 0;
225 }
226
227 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
228         char *old;
229         const char *new;
230         
231         if (value == NULL) {
232                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
233                 return -1;
234         }
235         Py_INCREF(value);
236         new = cpy_unicode_or_bytes_to_string(&value);
237         if (new == NULL) {
238                 Py_DECREF(value);
239                 return -1;
240         }
241
242         if (plugin_get_ds(new) == NULL) {
243                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
244                 Py_DECREF(value);
245                 return -1;
246         }
247
248         old = ((char *) self) + (intptr_t) data;
249         sstrncpy(old, new, DATA_MAX_NAME_LEN);
250         Py_DECREF(value);
251         return 0;
252 }
253
254 static PyGetSetDef PluginData_getseters[] = {
255         {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
256         {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
257         {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
258         {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
259         {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
260         {NULL}
261 };
262
263 PyTypeObject PluginDataType = {
264         CPY_INIT_TYPE
265         "collectd.PluginData",     /* tp_name */
266         sizeof(PluginData),        /* tp_basicsize */
267         0,                         /* Will be filled in later */
268         0,                         /* tp_dealloc */
269         0,                         /* tp_print */
270         0,                         /* tp_getattr */
271         0,                         /* tp_setattr */
272         0,                         /* tp_compare */
273         PluginData_repr,           /* tp_repr */
274         0,                         /* tp_as_number */
275         0,                         /* tp_as_sequence */
276         0,                         /* tp_as_mapping */
277         0,                         /* tp_hash */
278         0,                         /* tp_call */
279         0,                         /* tp_str */
280         0,                         /* tp_getattro */
281         0,                         /* tp_setattro */
282         0,                         /* tp_as_buffer */
283         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
284         PluginData_doc,            /* tp_doc */
285         0,                         /* tp_traverse */
286         0,                         /* tp_clear */
287         0,                         /* tp_richcompare */
288         0,                         /* tp_weaklistoffset */
289         0,                         /* tp_iter */
290         0,                         /* tp_iternext */
291         0,                         /* tp_methods */
292         PluginData_members,        /* tp_members */
293         PluginData_getseters,      /* tp_getset */
294         0,                         /* tp_base */
295         0,                         /* tp_dict */
296         0,                         /* tp_descr_get */
297         0,                         /* tp_descr_set */
298         0,                         /* tp_dictoffset */
299         PluginData_init,           /* tp_init */
300         0,                         /* tp_alloc */
301         PluginData_new             /* tp_new */
302 };
303
304 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
305                 "the same data source. This value has to be a positive integer, so you can't\n"
306                 "submit more than one value per second. If this member is set to a\n"
307                 "non-positive value, the default value as specified in the config file will\n"
308                 "be used (default: 10).\n"
309                 "\n"
310                 "If you submit values more often than the specified interval, the average\n"
311                 "will be used. If you submit less values, your graphs will have gaps.";
312
313 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
314                 "It has to be a sequence (a tuple or list) of numbers.\n"
315                 "The size of the sequence and the type of its content depend on the type\n"
316                 "member your types.db file. For more information on this read the types.db\n"
317                 "man page.\n"
318                 "\n"
319                 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
320                 "exception will be raised. If the content of the sequence is not a number,\n"
321                 "a TypeError exception will be raised.";
322
323 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
324                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
325                 "\n"
326                 "Dispatch this instance to the collectd process. The object has members\n"
327                 "for each of the possible arguments for this method. For a detailed explanation\n"
328                 "of these parameters see the member of the same same.\n"
329                 "\n"
330                 "If you do not submit a parameter the value saved in its member will be submitted.\n"
331                 "If you do provide a parameter it will be used instead, without altering the member.";
332
333 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
334                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
335                 "\n"
336                 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
337                 "This will bypass the main collectd process and all filtering and caching.\n"
338                 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
339                 "used instead of 'write'.\n";
340
341 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
342
343 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
344         Values *self;
345         
346         self = (Values *) PluginData_new(type, args, kwds);
347         if (self == NULL)
348                 return NULL;
349         
350         self->values = PyList_New(0);
351         self->interval = 0;
352         return (PyObject *) self;
353 }
354
355 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
356         Values *self = (Values *) s;
357         int interval = 0;
358         double time = 0;
359         PyObject *values = NULL, *tmp;
360         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
361         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
362                         "plugin", "host", "time", "interval", NULL};
363         
364         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
365                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
366                         NULL, &plugin, NULL, &host, &time, &interval))
367                 return -1;
368         
369         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
370                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
371                 return -1;
372         }
373
374         sstrncpy(self->data.host, host, sizeof(self->data.host));
375         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
376         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
377         sstrncpy(self->data.type, type, sizeof(self->data.type));
378         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
379         self->data.time = time;
380
381         if (values == NULL) {
382                 values = PyList_New(0);
383                 PyErr_Clear();
384         } else {
385                 Py_INCREF(values);
386         }
387         
388         tmp = self->values;
389         self->values = values;
390         Py_XDECREF(tmp);
391         
392         self->interval = interval;
393         return 0;
394 }
395
396 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
397         int i, ret;
398         const data_set_t *ds;
399         int size;
400         value_t *value;
401         value_list_t value_list = VALUE_LIST_INIT;
402         PyObject *values = self->values;
403         double time = self->data.time;
404         int interval = self->interval;
405         const char *host = self->data.host;
406         const char *plugin = self->data.plugin;
407         const char *plugin_instance = self->data.plugin_instance;
408         const char *type = self->data.type;
409         const char *type_instance = self->data.type_instance;
410         
411         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
412                         "plugin", "host", "time", "interval", NULL};
413         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
414                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
415                         NULL, &plugin, NULL, &host, &time, &interval))
416                 return NULL;
417
418         if (type[0] == 0) {
419                 PyErr_SetString(PyExc_RuntimeError, "type not set");
420                 return NULL;
421         }
422         ds = plugin_get_ds(type);
423         if (ds == NULL) {
424                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
425                 return NULL;
426         }
427         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
428                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
429                 return NULL;
430         }
431         size = (int) PySequence_Length(values);
432         if (size != ds->ds_num) {
433                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
434                 return NULL;
435         }
436         value = malloc(size * sizeof(*value));
437         for (i = 0; i < size; ++i) {
438                 PyObject *item, *num;
439                 item = PySequence_GetItem(values, i);
440                 if (ds->ds->type == DS_TYPE_COUNTER) {
441                         num = PyNumber_Long(item);
442                         if (num != NULL)
443                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
444                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
445                         num = PyNumber_Float(item);
446                         if (num != NULL)
447                                 value[i].gauge = PyFloat_AsDouble(num);
448                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
449                         /* This might overflow without raising an exception.
450                          * Not much we can do about it */
451                         num = PyNumber_Long(item);
452                         if (num != NULL)
453                                 value[i].derive = PyLong_AsLongLong(num);
454                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
455                         /* This might overflow without raising an exception.
456                          * Not much we can do about it */
457                         num = PyNumber_Long(item);
458                         if (num != NULL)
459                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
460                 } else {
461                         free(value);
462                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
463                         return NULL;
464                 }
465                 if (PyErr_Occurred() != NULL) {
466                         free(value);
467                         return NULL;
468                 }
469         }
470         value_list.values = value;
471         value_list.values_len = size;
472         value_list.time = time;
473         value_list.interval = interval;
474         sstrncpy(value_list.host, host, sizeof(value_list.host));
475         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
476         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
477         sstrncpy(value_list.type, type, sizeof(value_list.type));
478         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
479         value_list.meta = NULL;
480         if (value_list.host[0] == 0)
481                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
482         if (value_list.plugin[0] == 0)
483                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
484         Py_BEGIN_ALLOW_THREADS;
485         ret = plugin_dispatch_values(&value_list);
486         Py_END_ALLOW_THREADS;
487         if (ret != 0) {
488                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
489                 return NULL;
490         }
491         free(value);
492         Py_RETURN_NONE;
493 }
494
495 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
496         int i, ret;
497         const data_set_t *ds;
498         int size;
499         value_t *value;
500         value_list_t value_list = VALUE_LIST_INIT;
501         PyObject *values = self->values;
502         double time = self->data.time;
503         int interval = self->interval;
504         const char *host = self->data.host;
505         const char *plugin = self->data.plugin;
506         const char *plugin_instance = self->data.plugin_instance;
507         const char *type = self->data.type;
508         const char *type_instance = self->data.type_instance;
509         const char *dest = NULL;
510         
511         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
512                         "plugin", "host", "time", "interval", NULL};
513         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
514                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
515                         NULL, &plugin, NULL, &host, &time, &interval))
516                 return NULL;
517
518         if (type[0] == 0) {
519                 PyErr_SetString(PyExc_RuntimeError, "type not set");
520                 return NULL;
521         }
522         ds = plugin_get_ds(type);
523         if (ds == NULL) {
524                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
525                 return NULL;
526         }
527         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
528                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
529                 return NULL;
530         }
531         size = (int) PySequence_Length(values);
532         if (size != ds->ds_num) {
533                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
534                 return NULL;
535         }
536         value = malloc(size * sizeof(*value));
537         for (i = 0; i < size; ++i) {
538                 PyObject *item, *num;
539                 item = PySequence_GetItem(values, i);
540                 if (ds->ds->type == DS_TYPE_COUNTER) {
541                         num = PyNumber_Long(item);
542                         if (num != NULL)
543                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
544                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
545                         num = PyNumber_Float(item);
546                         if (num != NULL)
547                                 value[i].gauge = PyFloat_AsDouble(num);
548                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
549                         /* This might overflow without raising an exception.
550                          * Not much we can do about it */
551                         num = PyNumber_Long(item);
552                         if (num != NULL)
553                                 value[i].derive = PyLong_AsLongLong(num);
554                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
555                         /* This might overflow without raising an exception.
556                          * Not much we can do about it */
557                         num = PyNumber_Long(item);
558                         if (num != NULL)
559                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
560                 } else {
561                         free(value);
562                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
563                         return NULL;
564                 }
565                 if (PyErr_Occurred() != NULL) {
566                         free(value);
567                         return NULL;
568                 }
569         }
570         value_list.values = value;
571         value_list.values_len = size;
572         value_list.time = time;
573         value_list.interval = interval;
574         sstrncpy(value_list.host, host, sizeof(value_list.host));
575         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
576         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
577         sstrncpy(value_list.type, type, sizeof(value_list.type));
578         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
579         value_list.meta = NULL;
580         if (value_list.host[0] == 0)
581                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
582         if (value_list.plugin[0] == 0)
583                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
584         Py_BEGIN_ALLOW_THREADS;
585         ret = plugin_write(dest, NULL, &value_list);
586         Py_END_ALLOW_THREADS;
587         if (ret != 0) {
588                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
589                 return NULL;
590         }
591         free(value);
592         Py_RETURN_NONE;
593 }
594
595 static PyObject *Values_repr(PyObject *s) {
596         PyObject *ret, *tmp;
597         static PyObject *l_interval = NULL, *l_values = NULL, *l_closing = NULL;
598         Values *self = (Values *) s;
599         
600         if (l_interval == NULL)
601                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
602         if (l_values == NULL)
603                 l_values = cpy_string_to_unicode_or_bytes(",values=");
604         if (l_closing == NULL)
605                 l_closing = cpy_string_to_unicode_or_bytes(")");
606         
607         if (l_interval == NULL || l_values == NULL || l_closing == NULL)
608                 return NULL;
609         
610         ret = cpy_common_repr(s);
611         if (self->interval != 0) {
612                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_interval);
613                 tmp = PyInt_FromLong(self->interval);
614                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
615                 if (tmp)
616                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
617                 Py_XDECREF(tmp);
618         }
619         if (self->values != NULL && PySequence_Length(self->values) > 0) {
620                 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_values);
621                 tmp = PyObject_Repr(self->values);
622                 if (tmp)
623                         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
624                 Py_XDECREF(tmp);
625         }
626         CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
627         return ret;
628 }
629
630 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
631         Values *v = (Values *) self;
632         Py_VISIT(v->values);
633         return 0;
634 }
635
636 static int Values_clear(PyObject *self) {
637         Values *v = (Values *) self;
638         Py_CLEAR(v->values);
639         return 0;
640 }
641
642 static void Values_dealloc(PyObject *self) {
643         Values_clear(self);
644         self->ob_type->tp_free(self);
645 }
646
647 static PyMemberDef Values_members[] = {
648         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
649         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
650         {NULL}
651 };
652
653 static PyMethodDef Values_methods[] = {
654         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
655         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
656         {NULL}
657 };
658
659 PyTypeObject ValuesType = {
660         CPY_INIT_TYPE
661         "collectd.Values",         /* tp_name */
662         sizeof(Values),            /* tp_basicsize */
663         0,                         /* Will be filled in later */
664         Values_dealloc,            /* tp_dealloc */
665         0,                         /* tp_print */
666         0,                         /* tp_getattr */
667         0,                         /* tp_setattr */
668         0,                         /* tp_compare */
669         Values_repr,               /* tp_repr */
670         0,                         /* tp_as_number */
671         0,                         /* tp_as_sequence */
672         0,                         /* tp_as_mapping */
673         0,                         /* tp_hash */
674         0,                         /* tp_call */
675         0,                         /* tp_str */
676         0,                         /* tp_getattro */
677         0,                         /* tp_setattro */
678         0,                         /* tp_as_buffer */
679         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
680         Values_doc,                /* tp_doc */
681         Values_traverse,           /* tp_traverse */
682         Values_clear,              /* tp_clear */
683         0,                         /* tp_richcompare */
684         0,                         /* tp_weaklistoffset */
685         0,                         /* tp_iter */
686         0,                         /* tp_iternext */
687         Values_methods,            /* tp_methods */
688         Values_members,            /* tp_members */
689         0,                         /* tp_getset */
690         0,                         /* tp_base */
691         0,                         /* tp_dict */
692         0,                         /* tp_descr_get */
693         0,                         /* tp_descr_set */
694         0,                         /* tp_dictoffset */
695         Values_init,               /* tp_init */
696         0,                         /* tp_alloc */
697         Values_new                 /* tp_new */
698 };
699
700 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
701                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
702
703 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
704
705 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
706                 "It can be used to notify other plugins about bad stuff happening. It works\n"
707                 "similar to Values but has a severity and a message instead of interval\n"
708                 "and time.\n"
709                 "Notifications can be dispatched at any time and can be received with register_notification.";
710
711 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
712         Notification *self = (Notification *) s;
713         int severity = 0;
714         double time = 0;
715         const char *message = "";
716         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
717         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
718                         "plugin", "host", "time", "severity", NULL};
719         
720         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
721                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
722                         NULL, &plugin, NULL, &host, &time, &severity))
723                 return -1;
724         
725         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
726                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
727                 return -1;
728         }
729
730         sstrncpy(self->data.host, host, sizeof(self->data.host));
731         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
732         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
733         sstrncpy(self->data.type, type, sizeof(self->data.type));
734         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
735         self->data.time = time;
736
737         sstrncpy(self->message, message, sizeof(self->message));
738         self->severity = severity;
739         return 0;
740 }
741
742 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
743         int ret;
744         const data_set_t *ds;
745         notification_t notification;
746         double t = self->data.time;
747         int severity = self->severity;
748         const char *host = self->data.host;
749         const char *plugin = self->data.plugin;
750         const char *plugin_instance = self->data.plugin_instance;
751         const char *type = self->data.type;
752         const char *type_instance = self->data.type_instance;
753         const char *message = self->message;
754         
755         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
756                         "plugin", "host", "time", "severity", NULL};
757         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
758                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
759                         NULL, &plugin, NULL, &host, &t, &severity))
760                 return NULL;
761
762         if (type[0] == 0) {
763                 PyErr_SetString(PyExc_RuntimeError, "type not set");
764                 return NULL;
765         }
766         ds = plugin_get_ds(type);
767         if (ds == NULL) {
768                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
769                 return NULL;
770         }
771
772         notification.time = t;
773         notification.severity = severity;
774         sstrncpy(notification.message, message, sizeof(notification.message));
775         sstrncpy(notification.host, host, sizeof(notification.host));
776         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
777         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
778         sstrncpy(notification.type, type, sizeof(notification.type));
779         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
780         notification.meta = NULL;
781         if (notification.time < 1)
782                 notification.time = time(0);
783         if (notification.host[0] == 0)
784                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
785         if (notification.plugin[0] == 0)
786                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
787         Py_BEGIN_ALLOW_THREADS;
788         ret = plugin_dispatch_notification(&notification);
789         Py_END_ALLOW_THREADS;
790         if (ret != 0) {
791                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
792                 return NULL;
793         }
794         Py_RETURN_NONE;
795 }
796
797 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
798         Notification *self;
799         
800         self = (Notification *) PluginData_new(type, args, kwds);
801         if (self == NULL)
802                 return NULL;
803         
804         self->message[0] = 0;
805         self->severity = 0;
806         return (PyObject *) self;
807 }
808
809 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
810         char *old;
811         const char *new;
812         
813         if (value == NULL) {
814                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
815                 return -1;
816         }
817         Py_INCREF(value);
818         new = cpy_unicode_or_bytes_to_string(&value);
819         if (new == NULL) {
820                 Py_DECREF(value);
821                 return -1;
822         }
823         old = ((char *) self) + (intptr_t) data;
824         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
825         Py_DECREF(value);
826         return 0;
827 }
828
829 /*static PyObject *Notification_repr(PyObject *s) {
830         PyObject *ret;
831         Notification *self = (Notification *) s;
832         
833         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
834                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
835                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
836                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
837                         *self->data.host ? "',host='" : "", self->data.host,
838                         *self->message ? "',message='" : "", self->message,
839                         (long unsigned) self->data.time, self->severity);
840         return ret;
841 }*/
842
843 static PyMethodDef Notification_methods[] = {
844         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
845         {NULL}
846 };
847
848 static PyMemberDef Notification_members[] = {
849         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
850         {NULL}
851 };
852
853 static PyGetSetDef Notification_getseters[] = {
854         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
855         {NULL}
856 };
857
858 PyTypeObject NotificationType = {
859         CPY_INIT_TYPE
860         "collectd.Notification",   /* tp_name */
861         sizeof(Notification),      /* tp_basicsize */
862         0,                         /* Will be filled in later */
863         0,                         /* tp_dealloc */
864         0,                         /* tp_print */
865         0,                         /* tp_getattr */
866         0,                         /* tp_setattr */
867         0,                         /* tp_compare */
868         0/*Notification_repr*/,         /* tp_repr */
869         0,                         /* tp_as_number */
870         0,                         /* tp_as_sequence */
871         0,                         /* tp_as_mapping */
872         0,                         /* tp_hash */
873         0,                         /* tp_call */
874         0,                         /* tp_str */
875         0,                         /* tp_getattro */
876         0,                         /* tp_setattro */
877         0,                         /* tp_as_buffer */
878         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
879         Notification_doc,          /* tp_doc */
880         0,                         /* tp_traverse */
881         0,                         /* tp_clear */
882         0,                         /* tp_richcompare */
883         0,                         /* tp_weaklistoffset */
884         0,                         /* tp_iter */
885         0,                         /* tp_iternext */
886         Notification_methods,      /* tp_methods */
887         Notification_members,      /* tp_members */
888         Notification_getseters,    /* tp_getset */
889         0,                         /* tp_base */
890         0,                         /* tp_dict */
891         0,                         /* tp_descr_get */
892         0,                         /* tp_descr_set */
893         0,                         /* tp_dictoffset */
894         Notification_init,         /* tp_init */
895         0,                         /* tp_alloc */
896         Notification_new           /* tp_new */
897 };