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