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