Code

Added support for receiving meta data in write callbacks.
[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 meta_doc[] = "These are the meta data for this Value object.\n"
312                 "It has to be a dictionary of numbers, strings or bools.\n"
313                 "If the dict contains anything else a TypeError exception will be raised.";
315 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
316                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
317                 "\n"
318                 "Dispatch this instance to the collectd process. The object has members\n"
319                 "for each of the possible arguments for this method. For a detailed explanation\n"
320                 "of these parameters see the member of the same same.\n"
321                 "\n"
322                 "If you do not submit a parameter the value saved in its member will be submitted.\n"
323                 "If you do provide a parameter it will be used instead, without altering the member.";
325 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
326                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
327                 "\n"
328                 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
329                 "This will bypass the main collectd process and all filtering and caching.\n"
330                 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
331                 "used instead of 'write'.\n";
333 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
335 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
336         Values *self;
337         
338         self = (Values *) PluginData_new(type, args, kwds);
339         if (self == NULL)
340                 return NULL;
341         
342         self->values = PyList_New(0);
343         self->interval = 0;
344         return (PyObject *) self;
347 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
348         Values *self = (Values *) s;
349         int interval = 0;
350         double time = 0;
351         PyObject *values = NULL, *meta = NULL, *tmp;
352         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
353         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
354                         "plugin", "host", "time", "interval", "meta", NULL};
355         
356         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
357                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
358                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
359                 return -1;
360         
361         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
362                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
363                 return -1;
364         }
366         sstrncpy(self->data.host, host, sizeof(self->data.host));
367         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
368         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
369         sstrncpy(self->data.type, type, sizeof(self->data.type));
370         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
371         self->data.time = time;
373         if (values == NULL) {
374                 values = PyList_New(0);
375                 PyErr_Clear();
376         } else {
377                 Py_INCREF(values);
378         }
379         
380         if (meta == NULL) {
381                 meta = PyDict_New();
382                 PyErr_Clear();
383         } else {
384                 Py_INCREF(meta);
385         }
386         
387         tmp = self->values;
388         self->values = values;
389         self->meta = meta;
390         Py_XDECREF(tmp);
391         
392         self->interval = interval;
393         return 0;
396 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
397         int i, ret;
398         const data_set_t *ds;
399         int size;
400         value_t *value;
401         value_list_t value_list = VALUE_LIST_INIT;
402         PyObject *values = self->values;
403         double time = self->data.time;
404         int interval = self->interval;
405         const char *host = self->data.host;
406         const char *plugin = self->data.plugin;
407         const char *plugin_instance = self->data.plugin_instance;
408         const char *type = self->data.type;
409         const char *type_instance = self->data.type_instance;
410         
411         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
412                         "plugin", "host", "time", "interval", NULL};
413         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
414                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
415                         NULL, &plugin, NULL, &host, &time, &interval))
416                 return NULL;
418         if (type[0] == 0) {
419                 PyErr_SetString(PyExc_RuntimeError, "type not set");
420                 return NULL;
421         }
422         ds = plugin_get_ds(type);
423         if (ds == NULL) {
424                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
425                 return NULL;
426         }
427         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
428                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
429                 return NULL;
430         }
431         size = (int) PySequence_Length(values);
432         if (size != ds->ds_num) {
433                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
434                 return NULL;
435         }
436         value = malloc(size * sizeof(*value));
437         for (i = 0; i < size; ++i) {
438                 PyObject *item, *num;
439                 item = PySequence_GetItem(values, i);
440                 if (ds->ds->type == DS_TYPE_COUNTER) {
441                         num = PyNumber_Long(item);
442                         if (num != NULL)
443                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
444                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
445                         num = PyNumber_Float(item);
446                         if (num != NULL)
447                                 value[i].gauge = PyFloat_AsDouble(num);
448                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
449                         /* This might overflow without raising an exception.
450                          * Not much we can do about it */
451                         num = PyNumber_Long(item);
452                         if (num != NULL)
453                                 value[i].derive = PyLong_AsLongLong(num);
454                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
455                         /* This might overflow without raising an exception.
456                          * Not much we can do about it */
457                         num = PyNumber_Long(item);
458                         if (num != NULL)
459                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
460                 } else {
461                         free(value);
462                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
463                         return NULL;
464                 }
465                 if (PyErr_Occurred() != NULL) {
466                         free(value);
467                         return NULL;
468                 }
469         }
470         value_list.values = value;
471         value_list.values_len = size;
472         value_list.time = time;
473         value_list.interval = interval;
474         sstrncpy(value_list.host, host, sizeof(value_list.host));
475         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
476         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
477         sstrncpy(value_list.type, type, sizeof(value_list.type));
478         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
479         value_list.meta = NULL;
480         if (value_list.host[0] == 0)
481                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
482         if (value_list.plugin[0] == 0)
483                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
484         Py_BEGIN_ALLOW_THREADS;
485         ret = plugin_dispatch_values(&value_list);
486         Py_END_ALLOW_THREADS;
487         if (ret != 0) {
488                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
489                 return NULL;
490         }
491         free(value);
492         Py_RETURN_NONE;
495 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
496         int i, ret;
497         const data_set_t *ds;
498         int size;
499         value_t *value;
500         value_list_t value_list = VALUE_LIST_INIT;
501         PyObject *values = self->values;
502         double time = self->data.time;
503         int interval = self->interval;
504         const char *host = self->data.host;
505         const char *plugin = self->data.plugin;
506         const char *plugin_instance = self->data.plugin_instance;
507         const char *type = self->data.type;
508         const char *type_instance = self->data.type_instance;
509         const char *dest = NULL;
510         
511         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
512                         "plugin", "host", "time", "interval", NULL};
513         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
514                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
515                         NULL, &plugin, NULL, &host, &time, &interval))
516                 return NULL;
518         if (type[0] == 0) {
519                 PyErr_SetString(PyExc_RuntimeError, "type not set");
520                 return NULL;
521         }
522         ds = plugin_get_ds(type);
523         if (ds == NULL) {
524                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
525                 return NULL;
526         }
527         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
528                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
529                 return NULL;
530         }
531         size = (int) PySequence_Length(values);
532         if (size != ds->ds_num) {
533                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
534                 return NULL;
535         }
536         value = malloc(size * sizeof(*value));
537         for (i = 0; i < size; ++i) {
538                 PyObject *item, *num;
539                 item = PySequence_GetItem(values, i);
540                 if (ds->ds->type == DS_TYPE_COUNTER) {
541                         num = PyNumber_Long(item);
542                         if (num != NULL)
543                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
544                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
545                         num = PyNumber_Float(item);
546                         if (num != NULL)
547                                 value[i].gauge = PyFloat_AsDouble(num);
548                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
549                         /* This might overflow without raising an exception.
550                          * Not much we can do about it */
551                         num = PyNumber_Long(item);
552                         if (num != NULL)
553                                 value[i].derive = PyLong_AsLongLong(num);
554                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
555                         /* This might overflow without raising an exception.
556                          * Not much we can do about it */
557                         num = PyNumber_Long(item);
558                         if (num != NULL)
559                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
560                 } else {
561                         free(value);
562                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
563                         return NULL;
564                 }
565                 if (PyErr_Occurred() != NULL) {
566                         free(value);
567                         return NULL;
568                 }
569         }
570         value_list.values = value;
571         value_list.values_len = size;
572         value_list.time = time;
573         value_list.interval = interval;
574         sstrncpy(value_list.host, host, sizeof(value_list.host));
575         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
576         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
577         sstrncpy(value_list.type, type, sizeof(value_list.type));
578         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
579         value_list.meta = NULL;
580         if (value_list.host[0] == 0)
581                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
582         if (value_list.plugin[0] == 0)
583                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
584         Py_BEGIN_ALLOW_THREADS;
585         ret = plugin_write(dest, NULL, &value_list);
586         Py_END_ALLOW_THREADS;
587         if (ret != 0) {
588                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
589                 return NULL;
590         }
591         free(value);
592         Py_RETURN_NONE;
595 static PyObject *Values_repr(PyObject *s) {
596         PyObject *ret, *tmp;
597         static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
598         Values *self = (Values *) s;
599         
600         if (l_interval == NULL)
601                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
602         if (l_values == NULL)
603                 l_values = cpy_string_to_unicode_or_bytes(",values=");
604         if (l_meta == NULL)
605                 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
606         if (l_closing == NULL)
607                 l_closing = cpy_string_to_unicode_or_bytes(")");
608         
609         if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
610                 return NULL;
611         
612         ret = cpy_common_repr(s);
613         if (self->interval != 0) {
614                 CPY_STRCAT(&ret, l_interval);
615                 tmp = PyInt_FromLong(self->interval);
616                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
617                 CPY_STRCAT_AND_DEL(&ret, tmp);
618         }
619         if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
620                 CPY_STRCAT(&ret, l_values);
621                 tmp = PyObject_Repr(self->values);
622                 CPY_STRCAT_AND_DEL(&ret, tmp);
623         }
624         if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
625                 CPY_STRCAT(&ret, l_meta);
626                 tmp = PyObject_Repr(self->meta);
627                 CPY_STRCAT_AND_DEL(&ret, tmp);
628         }
629         CPY_STRCAT(&ret, l_closing);
630         return ret;
633 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
634         Values *v = (Values *) self;
635         Py_VISIT(v->values);
636         Py_VISIT(v->meta);
637         return 0;
640 static int Values_clear(PyObject *self) {
641         Values *v = (Values *) self;
642         Py_CLEAR(v->values);
643         Py_CLEAR(v->meta);
644         return 0;
647 static void Values_dealloc(PyObject *self) {
648         Values_clear(self);
649         self->ob_type->tp_free(self);
652 static PyMemberDef Values_members[] = {
653         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
654         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
655         {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
656         {NULL}
657 };
659 static PyMethodDef Values_methods[] = {
660         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
661         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
662         {NULL}
663 };
665 PyTypeObject ValuesType = {
666         CPY_INIT_TYPE
667         "collectd.Values",         /* tp_name */
668         sizeof(Values),            /* tp_basicsize */
669         0,                         /* Will be filled in later */
670         Values_dealloc,            /* tp_dealloc */
671         0,                         /* tp_print */
672         0,                         /* tp_getattr */
673         0,                         /* tp_setattr */
674         0,                         /* tp_compare */
675         Values_repr,               /* tp_repr */
676         0,                         /* tp_as_number */
677         0,                         /* tp_as_sequence */
678         0,                         /* tp_as_mapping */
679         0,                         /* tp_hash */
680         0,                         /* tp_call */
681         0,                         /* tp_str */
682         0,                         /* tp_getattro */
683         0,                         /* tp_setattro */
684         0,                         /* tp_as_buffer */
685         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
686         Values_doc,                /* tp_doc */
687         Values_traverse,           /* tp_traverse */
688         Values_clear,              /* tp_clear */
689         0,                         /* tp_richcompare */
690         0,                         /* tp_weaklistoffset */
691         0,                         /* tp_iter */
692         0,                         /* tp_iternext */
693         Values_methods,            /* tp_methods */
694         Values_members,            /* tp_members */
695         0,                         /* tp_getset */
696         0,                         /* tp_base */
697         0,                         /* tp_dict */
698         0,                         /* tp_descr_get */
699         0,                         /* tp_descr_set */
700         0,                         /* tp_dictoffset */
701         Values_init,               /* tp_init */
702         0,                         /* tp_alloc */
703         Values_new                 /* tp_new */
704 };
706 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
707                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
709 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
711 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
712                 "It can be used to notify other plugins about bad stuff happening. It works\n"
713                 "similar to Values but has a severity and a message instead of interval\n"
714                 "and time.\n"
715                 "Notifications can be dispatched at any time and can be received with register_notification.";
717 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
718         Notification *self = (Notification *) s;
719         int severity = 0;
720         double time = 0;
721         const char *message = "";
722         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
723         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
724                         "plugin", "host", "time", "severity", NULL};
725         
726         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
727                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
728                         NULL, &plugin, NULL, &host, &time, &severity))
729                 return -1;
730         
731         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
732                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
733                 return -1;
734         }
736         sstrncpy(self->data.host, host, sizeof(self->data.host));
737         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
738         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
739         sstrncpy(self->data.type, type, sizeof(self->data.type));
740         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
741         self->data.time = time;
743         sstrncpy(self->message, message, sizeof(self->message));
744         self->severity = severity;
745         return 0;
748 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
749         int ret;
750         const data_set_t *ds;
751         notification_t notification;
752         double t = self->data.time;
753         int severity = self->severity;
754         const char *host = self->data.host;
755         const char *plugin = self->data.plugin;
756         const char *plugin_instance = self->data.plugin_instance;
757         const char *type = self->data.type;
758         const char *type_instance = self->data.type_instance;
759         const char *message = self->message;
760         
761         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
762                         "plugin", "host", "time", "severity", NULL};
763         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
764                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
765                         NULL, &plugin, NULL, &host, &t, &severity))
766                 return NULL;
768         if (type[0] == 0) {
769                 PyErr_SetString(PyExc_RuntimeError, "type not set");
770                 return NULL;
771         }
772         ds = plugin_get_ds(type);
773         if (ds == NULL) {
774                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
775                 return NULL;
776         }
778         notification.time = t;
779         notification.severity = severity;
780         sstrncpy(notification.message, message, sizeof(notification.message));
781         sstrncpy(notification.host, host, sizeof(notification.host));
782         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
783         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
784         sstrncpy(notification.type, type, sizeof(notification.type));
785         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
786         notification.meta = NULL;
787         if (notification.time < 1)
788                 notification.time = time(0);
789         if (notification.host[0] == 0)
790                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
791         if (notification.plugin[0] == 0)
792                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
793         Py_BEGIN_ALLOW_THREADS;
794         ret = plugin_dispatch_notification(&notification);
795         Py_END_ALLOW_THREADS;
796         if (ret != 0) {
797                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
798                 return NULL;
799         }
800         Py_RETURN_NONE;
803 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
804         Notification *self;
805         
806         self = (Notification *) PluginData_new(type, args, kwds);
807         if (self == NULL)
808                 return NULL;
809         
810         self->message[0] = 0;
811         self->severity = 0;
812         return (PyObject *) self;
815 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
816         char *old;
817         const char *new;
818         
819         if (value == NULL) {
820                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
821                 return -1;
822         }
823         Py_INCREF(value);
824         new = cpy_unicode_or_bytes_to_string(&value);
825         if (new == NULL) {
826                 Py_DECREF(value);
827                 return -1;
828         }
829         old = ((char *) self) + (intptr_t) data;
830         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
831         Py_DECREF(value);
832         return 0;
835 static PyObject *Notification_repr(PyObject *s) {
836         PyObject *ret, *tmp;
837         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
838         Notification *self = (Notification *) s;
839         
840         if (l_severity == NULL)
841                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
842         if (l_message == NULL)
843                 l_message = cpy_string_to_unicode_or_bytes(",message=");
844         if (l_closing == NULL)
845                 l_closing = cpy_string_to_unicode_or_bytes(")");
846         
847         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
848                 return NULL;
849         
850         ret = cpy_common_repr(s);
851         if (self->severity != 0) {
852                 CPY_STRCAT(&ret, l_severity);
853                 tmp = PyInt_FromLong(self->severity);
854                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
855                 CPY_STRCAT_AND_DEL(&ret, tmp);
856         }
857         if (self->message[0] != 0) {
858                 CPY_STRCAT(&ret, l_message);
859                 tmp = cpy_string_to_unicode_or_bytes(self->message);
860                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
861                 CPY_STRCAT_AND_DEL(&ret, tmp);
862         }
863         CPY_STRCAT(&ret, l_closing);
864         return ret;
867 static PyMethodDef Notification_methods[] = {
868         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
869         {NULL}
870 };
872 static PyMemberDef Notification_members[] = {
873         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
874         {NULL}
875 };
877 static PyGetSetDef Notification_getseters[] = {
878         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
879         {NULL}
880 };
882 PyTypeObject NotificationType = {
883         CPY_INIT_TYPE
884         "collectd.Notification",   /* tp_name */
885         sizeof(Notification),      /* tp_basicsize */
886         0,                         /* Will be filled in later */
887         0,                         /* tp_dealloc */
888         0,                         /* tp_print */
889         0,                         /* tp_getattr */
890         0,                         /* tp_setattr */
891         0,                         /* tp_compare */
892         Notification_repr,         /* tp_repr */
893         0,                         /* tp_as_number */
894         0,                         /* tp_as_sequence */
895         0,                         /* tp_as_mapping */
896         0,                         /* tp_hash */
897         0,                         /* tp_call */
898         0,                         /* tp_str */
899         0,                         /* tp_getattro */
900         0,                         /* tp_setattro */
901         0,                         /* tp_as_buffer */
902         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
903         Notification_doc,          /* tp_doc */
904         0,                         /* tp_traverse */
905         0,                         /* tp_clear */
906         0,                         /* tp_richcompare */
907         0,                         /* tp_weaklistoffset */
908         0,                         /* tp_iter */
909         0,                         /* tp_iternext */
910         Notification_methods,      /* tp_methods */
911         Notification_members,      /* tp_members */
912         Notification_getseters,    /* tp_getset */
913         0,                         /* tp_base */
914         0,                         /* tp_dict */
915         0,                         /* tp_descr_get */
916         0,                         /* tp_descr_set */
917         0,                         /* tp_dictoffset */
918         Notification_init,         /* tp_init */
919         0,                         /* tp_alloc */
920         Notification_new           /* tp_new */
921 };