Code

Merge branch 'master' into ff/routeros
[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 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);
59         CPY_STRCAT(&ret, l_type);
60         tmp = cpy_string_to_unicode_or_bytes(self->type);
61         CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
62         CPY_STRCAT_AND_DEL(&ret, tmp);
64         if (self->type_instance[0] != 0) {
65                 CPY_STRCAT(&ret, l_type_instance);
66                 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
67                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
68                 CPY_STRCAT_AND_DEL(&ret, tmp);
69         }
71         if (self->plugin[0] != 0) {
72                 CPY_STRCAT(&ret, l_plugin);
73                 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
74                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
75                 CPY_STRCAT_AND_DEL(&ret, tmp);
76         }
78         if (self->plugin_instance[0] != 0) {
79                 CPY_STRCAT(&ret, l_plugin_instance);
80                 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
81                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
82                 CPY_STRCAT_AND_DEL(&ret, tmp);
83         }
85         if (self->host[0] != 0) {
86                 CPY_STRCAT(&ret, l_host);
87                 tmp = cpy_string_to_unicode_or_bytes(self->host);
88                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
89                 CPY_STRCAT_AND_DEL(&ret, tmp);
90         }
92         if (self->time != 0) {
93                 CPY_STRCAT(&ret, l_time);
94                 tmp = PyInt_FromLong(self->time);
95                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
96                 CPY_STRCAT_AND_DEL(&ret, tmp);
97         }
98         return ret;
99 }
101 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
102                 "For dispatching values this can be set to 0 which means \"now\".\n"
103                 "This means the time the value is actually dispatched, not the time\n"
104                 "it was set to 0.";
106 static char host_doc[] = "The hostname of the host this value was read from.\n"
107                 "For dispatching this can be set to an empty string which means\n"
108                 "the local hostname as defined in the collectd.conf.";
110 static char type_doc[] = "The type of this value. This type has to be defined\n"
111                 "in your types.db. Attempting to set it to any other value will\n"
112                 "raise a TypeError exception.\n"
113                 "Assigning a type is mandetory, calling dispatch without doing\n"
114                 "so will raise a RuntimeError exception.";
116 static char type_instance_doc[] = "";
118 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
119                 "member to an empty string will insert \"python\" upon dispatching.";
121 static char plugin_instance_doc[] = "";
123 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
124                 "and Notification. It is pretty useless by itself and was therefore not\n"
125                 "exported to the collectd module.";
127 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
128         PluginData *self;
129         
130         self = (PluginData *) type->tp_alloc(type, 0);
131         if (self == NULL)
132                 return NULL;
133         
134         self->time = 0;
135         self->host[0] = 0;
136         self->plugin[0] = 0;
137         self->plugin_instance[0] = 0;
138         self->type[0] = 0;
139         self->type_instance[0] = 0;
140         return (PyObject *) self;
143 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
144         PluginData *self = (PluginData *) s;
145         double time = 0;
146         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
147         static char *kwlist[] = {"type", "plugin_instance", "type_instance",
148                         "plugin", "host", "time", NULL};
149         
150         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
151                         NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
152                 return -1;
153         
154         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
155                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
156                 return -1;
157         }
159         sstrncpy(self->host, host, sizeof(self->host));
160         sstrncpy(self->plugin, plugin, sizeof(self->plugin));
161         sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
162         sstrncpy(self->type, type, sizeof(self->type));
163         sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
164         
165         self->time = time;
166         return 0;
169 static PyObject *PluginData_repr(PyObject *s) {
170         PyObject *ret;
171         static PyObject *l_closing = NULL;
172         
173         if (l_closing == NULL)
174                 l_closing = cpy_string_to_unicode_or_bytes(")");
175         
176         if (l_closing == NULL)
177                 return NULL;
178         
179         ret = cpy_common_repr(s);
180         CPY_STRCAT(&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, *tmp;
585         static PyObject *l_interval = NULL, *l_values = NULL, *l_closing = NULL;
586         Values *self = (Values *) s;
587         
588         if (l_interval == NULL)
589                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
590         if (l_values == NULL)
591                 l_values = cpy_string_to_unicode_or_bytes(",values=");
592         if (l_closing == NULL)
593                 l_closing = cpy_string_to_unicode_or_bytes(")");
594         
595         if (l_interval == NULL || l_values == NULL || l_closing == NULL)
596                 return NULL;
597         
598         ret = cpy_common_repr(s);
599         if (self->interval != 0) {
600                 CPY_STRCAT(&ret, l_interval);
601                 tmp = PyInt_FromLong(self->interval);
602                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
603                 CPY_STRCAT_AND_DEL(&ret, tmp);
604         }
605         if (self->values != NULL && PySequence_Length(self->values) > 0) {
606                 CPY_STRCAT(&ret, l_values);
607                 tmp = PyObject_Repr(self->values);
608                 CPY_STRCAT_AND_DEL(&ret, tmp);
609         }
610         CPY_STRCAT(&ret, l_closing);
611         return ret;
614 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
615         Values *v = (Values *) self;
616         Py_VISIT(v->values);
617         return 0;
620 static int Values_clear(PyObject *self) {
621         Values *v = (Values *) self;
622         Py_CLEAR(v->values);
623         return 0;
626 static void Values_dealloc(PyObject *self) {
627         Values_clear(self);
628         self->ob_type->tp_free(self);
631 static PyMemberDef Values_members[] = {
632         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
633         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
634         {NULL}
635 };
637 static PyMethodDef Values_methods[] = {
638         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
639         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
640         {NULL}
641 };
643 PyTypeObject ValuesType = {
644         CPY_INIT_TYPE
645         "collectd.Values",         /* tp_name */
646         sizeof(Values),            /* tp_basicsize */
647         0,                         /* Will be filled in later */
648         Values_dealloc,            /* tp_dealloc */
649         0,                         /* tp_print */
650         0,                         /* tp_getattr */
651         0,                         /* tp_setattr */
652         0,                         /* tp_compare */
653         Values_repr,               /* tp_repr */
654         0,                         /* tp_as_number */
655         0,                         /* tp_as_sequence */
656         0,                         /* tp_as_mapping */
657         0,                         /* tp_hash */
658         0,                         /* tp_call */
659         0,                         /* tp_str */
660         0,                         /* tp_getattro */
661         0,                         /* tp_setattro */
662         0,                         /* tp_as_buffer */
663         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
664         Values_doc,                /* tp_doc */
665         Values_traverse,           /* tp_traverse */
666         Values_clear,              /* tp_clear */
667         0,                         /* tp_richcompare */
668         0,                         /* tp_weaklistoffset */
669         0,                         /* tp_iter */
670         0,                         /* tp_iternext */
671         Values_methods,            /* tp_methods */
672         Values_members,            /* tp_members */
673         0,                         /* tp_getset */
674         0,                         /* tp_base */
675         0,                         /* tp_dict */
676         0,                         /* tp_descr_get */
677         0,                         /* tp_descr_set */
678         0,                         /* tp_dictoffset */
679         Values_init,               /* tp_init */
680         0,                         /* tp_alloc */
681         Values_new                 /* tp_new */
682 };
684 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
685                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
687 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
689 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
690                 "It can be used to notify other plugins about bad stuff happening. It works\n"
691                 "similar to Values but has a severity and a message instead of interval\n"
692                 "and time.\n"
693                 "Notifications can be dispatched at any time and can be received with register_notification.";
695 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
696         Notification *self = (Notification *) s;
697         int severity = 0;
698         double time = 0;
699         const char *message = "";
700         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
701         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
702                         "plugin", "host", "time", "severity", NULL};
703         
704         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
705                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
706                         NULL, &plugin, NULL, &host, &time, &severity))
707                 return -1;
708         
709         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
710                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
711                 return -1;
712         }
714         sstrncpy(self->data.host, host, sizeof(self->data.host));
715         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
716         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
717         sstrncpy(self->data.type, type, sizeof(self->data.type));
718         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
719         self->data.time = time;
721         sstrncpy(self->message, message, sizeof(self->message));
722         self->severity = severity;
723         return 0;
726 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
727         int ret;
728         const data_set_t *ds;
729         notification_t notification;
730         double t = self->data.time;
731         int severity = self->severity;
732         const char *host = self->data.host;
733         const char *plugin = self->data.plugin;
734         const char *plugin_instance = self->data.plugin_instance;
735         const char *type = self->data.type;
736         const char *type_instance = self->data.type_instance;
737         const char *message = self->message;
738         
739         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
740                         "plugin", "host", "time", "severity", NULL};
741         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
742                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
743                         NULL, &plugin, NULL, &host, &t, &severity))
744                 return NULL;
746         if (type[0] == 0) {
747                 PyErr_SetString(PyExc_RuntimeError, "type not set");
748                 return NULL;
749         }
750         ds = plugin_get_ds(type);
751         if (ds == NULL) {
752                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
753                 return NULL;
754         }
756         notification.time = t;
757         notification.severity = severity;
758         sstrncpy(notification.message, message, sizeof(notification.message));
759         sstrncpy(notification.host, host, sizeof(notification.host));
760         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
761         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
762         sstrncpy(notification.type, type, sizeof(notification.type));
763         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
764         notification.meta = NULL;
765         if (notification.time < 1)
766                 notification.time = time(0);
767         if (notification.host[0] == 0)
768                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
769         if (notification.plugin[0] == 0)
770                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
771         Py_BEGIN_ALLOW_THREADS;
772         ret = plugin_dispatch_notification(&notification);
773         Py_END_ALLOW_THREADS;
774         if (ret != 0) {
775                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
776                 return NULL;
777         }
778         Py_RETURN_NONE;
781 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
782         Notification *self;
783         
784         self = (Notification *) PluginData_new(type, args, kwds);
785         if (self == NULL)
786                 return NULL;
787         
788         self->message[0] = 0;
789         self->severity = 0;
790         return (PyObject *) self;
793 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
794         char *old;
795         const char *new;
796         
797         if (value == NULL) {
798                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
799                 return -1;
800         }
801         Py_INCREF(value);
802         new = cpy_unicode_or_bytes_to_string(&value);
803         if (new == NULL) {
804                 Py_DECREF(value);
805                 return -1;
806         }
807         old = ((char *) self) + (intptr_t) data;
808         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
809         Py_DECREF(value);
810         return 0;
813 static PyObject *Notification_repr(PyObject *s) {
814         PyObject *ret, *tmp;
815         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
816         Notification *self = (Notification *) s;
817         
818         if (l_severity == NULL)
819                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
820         if (l_message == NULL)
821                 l_message = cpy_string_to_unicode_or_bytes(",message=");
822         if (l_closing == NULL)
823                 l_closing = cpy_string_to_unicode_or_bytes(")");
824         
825         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
826                 return NULL;
827         
828         ret = cpy_common_repr(s);
829         if (self->severity != 0) {
830                 CPY_STRCAT(&ret, l_severity);
831                 tmp = PyInt_FromLong(self->severity);
832                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
833                 CPY_STRCAT_AND_DEL(&ret, tmp);
834         }
835         if (self->message[0] != 0) {
836                 CPY_STRCAT(&ret, l_message);
837                 tmp = cpy_string_to_unicode_or_bytes(self->message);
838                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
839                 CPY_STRCAT_AND_DEL(&ret, tmp);
840         }
841         CPY_STRCAT(&ret, l_closing);
842         return ret;
845 static PyMethodDef Notification_methods[] = {
846         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
847         {NULL}
848 };
850 static PyMemberDef Notification_members[] = {
851         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
852         {NULL}
853 };
855 static PyGetSetDef Notification_getseters[] = {
856         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
857         {NULL}
858 };
860 PyTypeObject NotificationType = {
861         CPY_INIT_TYPE
862         "collectd.Notification",   /* tp_name */
863         sizeof(Notification),      /* tp_basicsize */
864         0,                         /* Will be filled in later */
865         0,                         /* tp_dealloc */
866         0,                         /* tp_print */
867         0,                         /* tp_getattr */
868         0,                         /* tp_setattr */
869         0,                         /* tp_compare */
870         Notification_repr,         /* tp_repr */
871         0,                         /* tp_as_number */
872         0,                         /* tp_as_sequence */
873         0,                         /* tp_as_mapping */
874         0,                         /* tp_hash */
875         0,                         /* tp_call */
876         0,                         /* tp_str */
877         0,                         /* tp_getattro */
878         0,                         /* tp_setattro */
879         0,                         /* tp_as_buffer */
880         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
881         Notification_doc,          /* tp_doc */
882         0,                         /* tp_traverse */
883         0,                         /* tp_clear */
884         0,                         /* tp_richcompare */
885         0,                         /* tp_weaklistoffset */
886         0,                         /* tp_iter */
887         0,                         /* tp_iternext */
888         Notification_methods,      /* tp_methods */
889         Notification_members,      /* tp_members */
890         Notification_getseters,    /* tp_getset */
891         0,                         /* tp_base */
892         0,                         /* tp_dict */
893         0,                         /* tp_descr_get */
894         0,                         /* tp_descr_set */
895         0,                         /* tp_dictoffset */
896         Notification_init,         /* tp_init */
897         0,                         /* tp_alloc */
898         Notification_new           /* tp_new */
899 };