Code

python: Made the plugin unicode save.
[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  **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include "collectd.h"
31 #include "common.h"
33 #include "cpython.h"
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.";
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.";
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.";
50 static char type_instance_doc[] = "";
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.";
55 static char plugin_instance_doc[] = "";
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.";
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 }
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, "|etetetetetd", kwlist, NULL, &type,
85                         NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &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         }
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;
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);
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);
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         }
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         }
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         }
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         }
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;
184 static PyMemberDef PluginData_members[] = {
185         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
186         {NULL}
187 };
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);
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;
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         }
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         }
236         old = ((char *) self) + (intptr_t) data;
237         sstrncpy(old, new, DATA_MAX_NAME_LEN);
238         Py_DECREF(value);
239         return 0;
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 };
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 };
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.";
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.";
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.";
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";
329 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
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;
343 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
344         Values *self = (Values *) s;
345         int interval = 0;
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, "|etOetetetetdi", kwlist,
353                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
354                         NULL, &plugin, NULL, &host, &time, &interval))
355                 return -1;
356         
357         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
358                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
359                 return -1;
360         }
362         sstrncpy(self->data.host, host, sizeof(self->data.host));
363         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
364         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
365         sstrncpy(self->data.type, type, sizeof(self->data.type));
366         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
367         self->data.time = time;
369         if (values == NULL) {
370                 values = PyList_New(0);
371                 PyErr_Clear();
372         } else {
373                 Py_INCREF(values);
374         }
375         
376         tmp = self->values;
377         self->values = values;
378         Py_XDECREF(tmp);
379         
380         self->interval = interval;
381         return 0;
384 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
385         int i, ret;
386         const data_set_t *ds;
387         int size;
388         value_t *value;
389         value_list_t value_list = VALUE_LIST_INIT;
390         PyObject *values = self->values;
391         double time = self->data.time;
392         int interval = self->interval;
393         const char *host = self->data.host;
394         const char *plugin = self->data.plugin;
395         const char *plugin_instance = self->data.plugin_instance;
396         const char *type = self->data.type;
397         const char *type_instance = self->data.type_instance;
398         
399         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
400                         "plugin", "host", "time", "interval", NULL};
401         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
402                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
403                         NULL, &plugin, NULL, &host, &time, &interval))
404                 return NULL;
406         if (type[0] == 0) {
407                 PyErr_SetString(PyExc_RuntimeError, "type not set");
408                 return NULL;
409         }
410         ds = plugin_get_ds(type);
411         if (ds == NULL) {
412                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
413                 return NULL;
414         }
415         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
416                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
417                 return NULL;
418         }
419         size = (int) PySequence_Length(values);
420         if (size != ds->ds_num) {
421                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
422                 return NULL;
423         }
424         value = malloc(size * sizeof(*value));
425         for (i = 0; i < size; ++i) {
426                 PyObject *item, *num;
427                 item = PySequence_GetItem(values, i);
428                 if (ds->ds->type == DS_TYPE_COUNTER) {
429                         num = PyNumber_Long(item);
430                         if (num != NULL)
431                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
432                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
433                         num = PyNumber_Float(item);
434                         if (num != NULL)
435                                 value[i].gauge = PyFloat_AsDouble(num);
436                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
437                         /* This might overflow without raising an exception.
438                          * Not much we can do about it */
439                         num = PyNumber_Long(item);
440                         if (num != NULL)
441                                 value[i].derive = PyLong_AsLongLong(num);
442                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
443                         /* This might overflow without raising an exception.
444                          * Not much we can do about it */
445                         num = PyNumber_Long(item);
446                         if (num != NULL)
447                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
448                 } else {
449                         free(value);
450                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
451                         return NULL;
452                 }
453                 if (PyErr_Occurred() != NULL) {
454                         free(value);
455                         return NULL;
456                 }
457         }
458         value_list.values = value;
459         value_list.values_len = size;
460         value_list.time = time;
461         value_list.interval = interval;
462         sstrncpy(value_list.host, host, sizeof(value_list.host));
463         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
464         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
465         sstrncpy(value_list.type, type, sizeof(value_list.type));
466         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
467         value_list.meta = NULL;
468         if (value_list.host[0] == 0)
469                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
470         if (value_list.plugin[0] == 0)
471                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
472         Py_BEGIN_ALLOW_THREADS;
473         ret = plugin_dispatch_values(&value_list);
474         Py_END_ALLOW_THREADS;
475         if (ret != 0) {
476                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
477                 return NULL;
478         }
479         free(value);
480         Py_RETURN_NONE;
483 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
484         int i, ret;
485         const data_set_t *ds;
486         int size;
487         value_t *value;
488         value_list_t value_list = VALUE_LIST_INIT;
489         PyObject *values = self->values;
490         double time = self->data.time;
491         int interval = self->interval;
492         const char *host = self->data.host;
493         const char *plugin = self->data.plugin;
494         const char *plugin_instance = self->data.plugin_instance;
495         const char *type = self->data.type;
496         const char *type_instance = self->data.type_instance;
497         const char *dest = NULL;
498         
499         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
500                         "plugin", "host", "time", "interval", NULL};
501         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
502                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
503                         NULL, &plugin, NULL, &host, &time, &interval))
504                 return NULL;
506         if (type[0] == 0) {
507                 PyErr_SetString(PyExc_RuntimeError, "type not set");
508                 return NULL;
509         }
510         ds = plugin_get_ds(type);
511         if (ds == NULL) {
512                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
513                 return NULL;
514         }
515         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
516                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
517                 return NULL;
518         }
519         size = (int) PySequence_Length(values);
520         if (size != ds->ds_num) {
521                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
522                 return NULL;
523         }
524         value = malloc(size * sizeof(*value));
525         for (i = 0; i < size; ++i) {
526                 PyObject *item, *num;
527                 item = PySequence_GetItem(values, i);
528                 if (ds->ds->type == DS_TYPE_COUNTER) {
529                         num = PyNumber_Long(item);
530                         if (num != NULL)
531                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
532                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
533                         num = PyNumber_Float(item);
534                         if (num != NULL)
535                                 value[i].gauge = PyFloat_AsDouble(num);
536                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
537                         /* This might overflow without raising an exception.
538                          * Not much we can do about it */
539                         num = PyNumber_Long(item);
540                         if (num != NULL)
541                                 value[i].derive = PyLong_AsLongLong(num);
542                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
543                         /* This might overflow without raising an exception.
544                          * Not much we can do about it */
545                         num = PyNumber_Long(item);
546                         if (num != NULL)
547                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
548                 } else {
549                         free(value);
550                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
551                         return NULL;
552                 }
553                 if (PyErr_Occurred() != NULL) {
554                         free(value);
555                         return NULL;
556                 }
557         }
558         value_list.values = value;
559         value_list.values_len = size;
560         value_list.time = time;
561         value_list.interval = interval;
562         sstrncpy(value_list.host, host, sizeof(value_list.host));
563         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
564         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
565         sstrncpy(value_list.type, type, sizeof(value_list.type));
566         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
567         value_list.meta = NULL;
568         if (value_list.host[0] == 0)
569                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
570         if (value_list.plugin[0] == 0)
571                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
572         Py_BEGIN_ALLOW_THREADS;
573         ret = plugin_write(dest, NULL, &value_list);
574         Py_END_ALLOW_THREADS;
575         if (ret != 0) {
576                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
577                 return NULL;
578         }
579         free(value);
580         Py_RETURN_NONE;
583 /*static PyObject *Values_repr(PyObject *s) {
584         PyObject *ret, *valuestring = NULL;
585         Values *self = (Values *) s;
586         
587         if (self->values != NULL)
588                 valuestring = PyObject_Repr(self->values);
589         if (valuestring == NULL)
590                 return NULL;
591         
592         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
593                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
594                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
595                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
596                         *self->data.host ? "',host='" : "", self->data.host,
597                         (long unsigned) self->data.time, self->interval,
598                         valuestring ? cpy_unicode_or_bytes_to_string(valuestring) : "[]");
599         Py_XDECREF(valuestring);
600         return ret;
601 }*/
603 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
604         Values *v = (Values *) self;
605         Py_VISIT(v->values);
606         return 0;
609 static int Values_clear(PyObject *self) {
610         Values *v = (Values *) self;
611         Py_CLEAR(v->values);
612         return 0;
615 static void Values_dealloc(PyObject *self) {
616         Values_clear(self);
617         self->ob_type->tp_free(self);
620 static PyMemberDef Values_members[] = {
621         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
622         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
623         {NULL}
624 };
626 static PyMethodDef Values_methods[] = {
627         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
628         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
629         {NULL}
630 };
632 PyTypeObject ValuesType = {
633         CPY_INIT_TYPE
634         "collectd.Values",         /* tp_name */
635         sizeof(Values),            /* tp_basicsize */
636         0,                         /* Will be filled in later */
637         Values_dealloc,            /* tp_dealloc */
638         0,                         /* tp_print */
639         0,                         /* tp_getattr */
640         0,                         /* tp_setattr */
641         0,                         /* tp_compare */
642         0/*Values_repr*/,               /* tp_repr */
643         0,                         /* tp_as_number */
644         0,                         /* tp_as_sequence */
645         0,                         /* tp_as_mapping */
646         0,                         /* tp_hash */
647         0,                         /* tp_call */
648         0,                         /* tp_str */
649         0,                         /* tp_getattro */
650         0,                         /* tp_setattro */
651         0,                         /* tp_as_buffer */
652         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
653         Values_doc,                /* tp_doc */
654         Values_traverse,           /* tp_traverse */
655         Values_clear,              /* tp_clear */
656         0,                         /* tp_richcompare */
657         0,                         /* tp_weaklistoffset */
658         0,                         /* tp_iter */
659         0,                         /* tp_iternext */
660         Values_methods,            /* tp_methods */
661         Values_members,            /* tp_members */
662         0,                         /* tp_getset */
663         0,                         /* tp_base */
664         0,                         /* tp_dict */
665         0,                         /* tp_descr_get */
666         0,                         /* tp_descr_set */
667         0,                         /* tp_dictoffset */
668         Values_init,               /* tp_init */
669         0,                         /* tp_alloc */
670         Values_new                 /* tp_new */
671 };
673 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
674                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
676 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
678 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
679                 "It can be used to notify other plugins about bad stuff happening. It works\n"
680                 "similar to Values but has a severity and a message instead of interval\n"
681                 "and time.\n"
682                 "Notifications can be dispatched at any time and can be received with register_notification.";
684 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
685         Notification *self = (Notification *) s;
686         int severity = 0;
687         double time = 0;
688         const char *message = "";
689         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
690         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
691                         "plugin", "host", "time", "severity", NULL};
692         
693         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
694                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
695                         NULL, &plugin, NULL, &host, &time, &severity))
696                 return -1;
697         
698         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
699                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
700                 return -1;
701         }
703         sstrncpy(self->data.host, host, sizeof(self->data.host));
704         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
705         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
706         sstrncpy(self->data.type, type, sizeof(self->data.type));
707         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
708         self->data.time = time;
710         sstrncpy(self->message, message, sizeof(self->message));
711         self->severity = severity;
712         return 0;
715 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
716         int ret;
717         const data_set_t *ds;
718         notification_t notification;
719         double t = self->data.time;
720         int severity = self->severity;
721         const char *host = self->data.host;
722         const char *plugin = self->data.plugin;
723         const char *plugin_instance = self->data.plugin_instance;
724         const char *type = self->data.type;
725         const char *type_instance = self->data.type_instance;
726         const char *message = self->message;
727         
728         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
729                         "plugin", "host", "time", "severity", NULL};
730         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
731                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
732                         NULL, &plugin, NULL, &host, &t, &severity))
733                 return NULL;
735         if (type[0] == 0) {
736                 PyErr_SetString(PyExc_RuntimeError, "type not set");
737                 return NULL;
738         }
739         ds = plugin_get_ds(type);
740         if (ds == NULL) {
741                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
742                 return NULL;
743         }
745         notification.time = t;
746         notification.severity = severity;
747         sstrncpy(notification.message, message, sizeof(notification.message));
748         sstrncpy(notification.host, host, sizeof(notification.host));
749         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
750         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
751         sstrncpy(notification.type, type, sizeof(notification.type));
752         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
753         notification.meta = NULL;
754         if (notification.time < 1)
755                 notification.time = time(0);
756         if (notification.host[0] == 0)
757                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
758         if (notification.plugin[0] == 0)
759                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
760         Py_BEGIN_ALLOW_THREADS;
761         ret = plugin_dispatch_notification(&notification);
762         Py_END_ALLOW_THREADS;
763         if (ret != 0) {
764                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
765                 return NULL;
766         }
767         Py_RETURN_NONE;
770 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
771         Notification *self;
772         
773         self = (Notification *) PluginData_new(type, args, kwds);
774         if (self == NULL)
775                 return NULL;
776         
777         self->message[0] = 0;
778         self->severity = 0;
779         return (PyObject *) self;
782 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
783         char *old;
784         const char *new;
785         
786         if (value == NULL) {
787                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
788                 return -1;
789         }
790         Py_INCREF(value);
791         new = cpy_unicode_or_bytes_to_string(&value);
792         if (new == NULL) {
793                 Py_DECREF(value);
794                 return -1;
795         }
796         old = ((char *) self) + (intptr_t) data;
797         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
798         Py_DECREF(value);
799         return 0;
802 /*static PyObject *Notification_repr(PyObject *s) {
803         PyObject *ret;
804         Notification *self = (Notification *) s;
805         
806         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
807                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
808                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
809                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
810                         *self->data.host ? "',host='" : "", self->data.host,
811                         *self->message ? "',message='" : "", self->message,
812                         (long unsigned) self->data.time, self->severity);
813         return ret;
814 }*/
816 static PyMethodDef Notification_methods[] = {
817         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
818         {NULL}
819 };
821 static PyMemberDef Notification_members[] = {
822         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
823         {NULL}
824 };
826 static PyGetSetDef Notification_getseters[] = {
827         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
828         {NULL}
829 };
831 PyTypeObject NotificationType = {
832         CPY_INIT_TYPE
833         "collectd.Notification",   /* tp_name */
834         sizeof(Notification),      /* tp_basicsize */
835         0,                         /* Will be filled in later */
836         0,                         /* tp_dealloc */
837         0,                         /* tp_print */
838         0,                         /* tp_getattr */
839         0,                         /* tp_setattr */
840         0,                         /* tp_compare */
841         0/*Notification_repr*/,         /* tp_repr */
842         0,                         /* tp_as_number */
843         0,                         /* tp_as_sequence */
844         0,                         /* tp_as_mapping */
845         0,                         /* tp_hash */
846         0,                         /* tp_call */
847         0,                         /* tp_str */
848         0,                         /* tp_getattro */
849         0,                         /* tp_setattro */
850         0,                         /* tp_as_buffer */
851         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
852         Notification_doc,          /* tp_doc */
853         0,                         /* tp_traverse */
854         0,                         /* tp_clear */
855         0,                         /* tp_richcompare */
856         0,                         /* tp_weaklistoffset */
857         0,                         /* tp_iter */
858         0,                         /* tp_iternext */
859         Notification_methods,      /* tp_methods */
860         Notification_members,      /* tp_members */
861         Notification_getseters,    /* tp_getset */
862         0,                         /* tp_base */
863         0,                         /* tp_dict */
864         0,                         /* tp_descr_get */
865         0,                         /* tp_descr_set */
866         0,                         /* tp_dictoffset */
867         Notification_init,         /* tp_init */
868         0,                         /* tp_alloc */
869         Notification_new           /* tp_new */
870 };