Merge pull request #1842 from rubenk/declare-loop-variable-in-for-loop-controlling...
[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                 if (ds->ds->type == DS_TYPE_COUNTER) {
556                         num = PyNumber_Long(item); /* New reference. */
557                         if (num != NULL) {
558                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
559                                 Py_XDECREF(num);
560                         }
561                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
562                         num = PyNumber_Float(item); /* New reference. */
563                         if (num != NULL) {
564                                 value[i].gauge = PyFloat_AsDouble(num);
565                                 Py_XDECREF(num);
566                         }
567                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
568                         /* This might overflow without raising an exception.
569                          * Not much we can do about it */
570                         num = PyNumber_Long(item); /* New reference. */
571                         if (num != NULL) {
572                                 value[i].derive = PyLong_AsLongLong(num);
573                                 Py_XDECREF(num);
574                         }
575                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
576                         /* This might overflow without raising an exception.
577                          * Not much we can do about it */
578                         num = PyNumber_Long(item); /* New reference. */
579                         if (num != NULL) {
580                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
581                                 Py_XDECREF(num);
582                         }
583                 } else {
584                         free(value);
585                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
586                         return NULL;
587                 }
588                 if (PyErr_Occurred() != NULL) {
589                         free(value);
590                         return NULL;
591                 }
592         }
593         value_list.values = value;
594         value_list.meta = cpy_build_meta(meta);
595         value_list.values_len = size;
596         value_list.time = DOUBLE_TO_CDTIME_T(time);
597         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
598         if (value_list.host[0] == 0)
599                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
600         if (value_list.plugin[0] == 0)
601                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
602         Py_BEGIN_ALLOW_THREADS;
603         ret = plugin_dispatch_values(&value_list);
604         Py_END_ALLOW_THREADS;
605         meta_data_destroy(value_list.meta);
606         free(value);
607         if (ret != 0) {
608                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
609                 return NULL;
610         }
611         Py_RETURN_NONE;
612 }
613
614 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
615         int ret;
616         const data_set_t *ds;
617         size_t size;
618         value_t *value;
619         value_list_t value_list = VALUE_LIST_INIT;
620         PyObject *values = self->values, *meta = self->meta;
621         double time = self->data.time, interval = self->interval;
622         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
623
624         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
625                         "plugin", "host", "time", "interval", "meta", NULL};
626         if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
627                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
628                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
629                 return NULL;
630
631         sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
632         sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
633         sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
634         sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
635         sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
636         FreeAll();
637         if (value_list.type[0] == 0) {
638                 PyErr_SetString(PyExc_RuntimeError, "type not set");
639                 return NULL;
640         }
641         ds = plugin_get_ds(value_list.type);
642         if (ds == NULL) {
643                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
644                 return NULL;
645         }
646         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
647                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
648                 return NULL;
649         }
650         size = (size_t) PySequence_Length(values);
651         if (size != ds->ds_num) {
652                 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
653                 return NULL;
654         }
655         value = calloc(size, sizeof(*value));
656         for (size_t i = 0; i < size; ++i) {
657                 PyObject *item, *num;
658                 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
659                 if (ds->ds->type == DS_TYPE_COUNTER) {
660                         num = PyNumber_Long(item); /* New reference. */
661                         if (num != NULL) {
662                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
663                                 Py_XDECREF(num);
664                         }
665                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
666                         num = PyNumber_Float(item); /* New reference. */
667                         if (num != NULL) {
668                                 value[i].gauge = PyFloat_AsDouble(num);
669                                 Py_XDECREF(num);
670                         }
671                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
672                         /* This might overflow without raising an exception.
673                          * Not much we can do about it */
674                         num = PyNumber_Long(item); /* New reference. */
675                         if (num != NULL) {
676                                 value[i].derive = PyLong_AsLongLong(num);
677                                 Py_XDECREF(num);
678                         }
679                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
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].absolute = PyLong_AsUnsignedLongLong(num);
685                                 Py_XDECREF(num);
686                         }
687                 } else {
688                         free(value);
689                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
690                         return NULL;
691                 }
692                 if (PyErr_Occurred() != NULL) {
693                         free(value);
694                         return NULL;
695                 }
696         }
697         value_list.values = value;
698         value_list.values_len = size;
699         value_list.time = DOUBLE_TO_CDTIME_T(time);
700         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
701         value_list.meta = cpy_build_meta(meta);
702         if (value_list.host[0] == 0)
703                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
704         if (value_list.plugin[0] == 0)
705                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
706         Py_BEGIN_ALLOW_THREADS;
707         ret = plugin_write(dest, NULL, &value_list);
708         Py_END_ALLOW_THREADS;
709         meta_data_destroy(value_list.meta);
710         free(value);
711         if (ret != 0) {
712                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
713                 return NULL;
714         }
715         Py_RETURN_NONE;
716 }
717
718 static PyObject *Values_repr(PyObject *s) {
719         PyObject *ret, *tmp;
720         static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
721         Values *self = (Values *) s;
722
723         if (l_interval == NULL)
724                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
725         if (l_values == NULL)
726                 l_values = cpy_string_to_unicode_or_bytes(",values=");
727         if (l_meta == NULL)
728                 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
729         if (l_closing == NULL)
730                 l_closing = cpy_string_to_unicode_or_bytes(")");
731
732         if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
733                 return NULL;
734
735         ret = cpy_common_repr(s);
736         if (self->interval != 0) {
737                 CPY_STRCAT(&ret, l_interval);
738                 tmp = PyFloat_FromDouble(self->interval);
739                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
740                 CPY_STRCAT_AND_DEL(&ret, tmp);
741         }
742         if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
743                 CPY_STRCAT(&ret, l_values);
744                 tmp = PyObject_Repr(self->values);
745                 CPY_STRCAT_AND_DEL(&ret, tmp);
746         }
747         if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
748                 CPY_STRCAT(&ret, l_meta);
749                 tmp = PyObject_Repr(self->meta);
750                 CPY_STRCAT_AND_DEL(&ret, tmp);
751         }
752         CPY_STRCAT(&ret, l_closing);
753         return ret;
754 }
755
756 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
757         Values *v = (Values *) self;
758         Py_VISIT(v->values);
759         Py_VISIT(v->meta);
760         return 0;
761 }
762
763 static int Values_clear(PyObject *self) {
764         Values *v = (Values *) self;
765         Py_CLEAR(v->values);
766         Py_CLEAR(v->meta);
767         return 0;
768 }
769
770 static void Values_dealloc(PyObject *self) {
771         Values_clear(self);
772         self->ob_type->tp_free(self);
773 }
774
775 static PyMemberDef Values_members[] = {
776         {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
777         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
778         {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
779         {NULL}
780 };
781
782 static PyMethodDef Values_methods[] = {
783         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
784         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
785         {NULL}
786 };
787
788 PyTypeObject ValuesType = {
789         CPY_INIT_TYPE
790         "collectd.Values",         /* tp_name */
791         sizeof(Values),            /* tp_basicsize */
792         0,                         /* Will be filled in later */
793         Values_dealloc,            /* tp_dealloc */
794         0,                         /* tp_print */
795         0,                         /* tp_getattr */
796         0,                         /* tp_setattr */
797         0,                         /* tp_compare */
798         Values_repr,               /* tp_repr */
799         0,                         /* tp_as_number */
800         0,                         /* tp_as_sequence */
801         0,                         /* tp_as_mapping */
802         0,                         /* tp_hash */
803         0,                         /* tp_call */
804         0,                         /* tp_str */
805         0,                         /* tp_getattro */
806         0,                         /* tp_setattro */
807         0,                         /* tp_as_buffer */
808         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
809         Values_doc,                /* tp_doc */
810         Values_traverse,           /* tp_traverse */
811         Values_clear,              /* tp_clear */
812         0,                         /* tp_richcompare */
813         0,                         /* tp_weaklistoffset */
814         0,                         /* tp_iter */
815         0,                         /* tp_iternext */
816         Values_methods,            /* tp_methods */
817         Values_members,            /* tp_members */
818         0,                         /* tp_getset */
819         0,                         /* tp_base */
820         0,                         /* tp_dict */
821         0,                         /* tp_descr_get */
822         0,                         /* tp_descr_set */
823         0,                         /* tp_dictoffset */
824         Values_init,               /* tp_init */
825         0,                         /* tp_alloc */
826         Values_new                 /* tp_new */
827 };
828
829 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
830                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
831
832 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
833
834 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
835                 "It can be used to notify other plugins about bad stuff happening. It works\n"
836                 "similar to Values but has a severity and a message instead of interval\n"
837                 "and time.\n"
838                 "Notifications can be dispatched at any time and can be received with register_notification.";
839
840 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
841         Notification *self = (Notification *) s;
842         int severity = 0;
843         double time = 0;
844         char *message = NULL;
845         char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
846         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
847                         "plugin", "host", "time", "severity", NULL};
848
849         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
850                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
851                         NULL, &plugin, NULL, &host, &time, &severity))
852                 return -1;
853
854         if (type && plugin_get_ds(type) == NULL) {
855                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
856                 FreeAll();
857                 PyMem_Free(message);
858                 return -1;
859         }
860
861         sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
862         sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
863         sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
864         sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
865         sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
866         sstrncpy(self->message, message ? message : "", sizeof(self->message));
867         self->data.time = time;
868         self->severity = severity;
869
870         FreeAll();
871         PyMem_Free(message);
872         return 0;
873 }
874
875 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
876         int ret;
877         const data_set_t *ds;
878         notification_t notification;
879         double t = self->data.time;
880         int severity = self->severity;
881         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
882         char *message = NULL;
883
884         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
885                         "plugin", "host", "time", "severity", NULL};
886         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
887                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
888                         NULL, &plugin, NULL, &host, &t, &severity))
889                 return NULL;
890
891         notification.time = DOUBLE_TO_CDTIME_T(t);
892         notification.severity = severity;
893         sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
894         sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
895         sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
896         sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
897         sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
898         sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
899         notification.meta = NULL;
900         FreeAll();
901         PyMem_Free(message);
902
903         if (notification.type[0] == 0) {
904                 PyErr_SetString(PyExc_RuntimeError, "type not set");
905                 return NULL;
906         }
907         ds = plugin_get_ds(notification.type);
908         if (ds == NULL) {
909                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
910                 return NULL;
911         }
912
913         if (notification.time == 0)
914                 notification.time = cdtime();
915         if (notification.host[0] == 0)
916                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
917         if (notification.plugin[0] == 0)
918                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
919         Py_BEGIN_ALLOW_THREADS;
920         ret = plugin_dispatch_notification(&notification);
921         Py_END_ALLOW_THREADS;
922         if (ret != 0) {
923                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
924                 return NULL;
925         }
926         Py_RETURN_NONE;
927 }
928
929 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
930         Notification *self;
931
932         self = (Notification *) PluginData_new(type, args, kwds);
933         if (self == NULL)
934                 return NULL;
935
936         self->message[0] = 0;
937         self->severity = 0;
938         return (PyObject *) self;
939 }
940
941 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
942         char *old;
943         const char *new;
944
945         if (value == NULL) {
946                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
947                 return -1;
948         }
949         Py_INCREF(value);
950         new = cpy_unicode_or_bytes_to_string(&value);
951         if (new == NULL) {
952                 Py_DECREF(value);
953                 return -1;
954         }
955         old = ((char *) self) + (intptr_t) data;
956         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
957         Py_DECREF(value);
958         return 0;
959 }
960
961 static PyObject *Notification_repr(PyObject *s) {
962         PyObject *ret, *tmp;
963         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
964         Notification *self = (Notification *) s;
965
966         if (l_severity == NULL)
967                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
968         if (l_message == NULL)
969                 l_message = cpy_string_to_unicode_or_bytes(",message=");
970         if (l_closing == NULL)
971                 l_closing = cpy_string_to_unicode_or_bytes(")");
972
973         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
974                 return NULL;
975
976         ret = cpy_common_repr(s);
977         if (self->severity != 0) {
978                 CPY_STRCAT(&ret, l_severity);
979                 tmp = PyInt_FromLong(self->severity);
980                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
981                 CPY_STRCAT_AND_DEL(&ret, tmp);
982         }
983         if (self->message[0] != 0) {
984                 CPY_STRCAT(&ret, l_message);
985                 tmp = cpy_string_to_unicode_or_bytes(self->message);
986                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
987                 CPY_STRCAT_AND_DEL(&ret, tmp);
988         }
989         CPY_STRCAT(&ret, l_closing);
990         return ret;
991 }
992
993 static PyMethodDef Notification_methods[] = {
994         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
995         {NULL}
996 };
997
998 static PyMemberDef Notification_members[] = {
999         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1000         {NULL}
1001 };
1002
1003 static PyGetSetDef Notification_getseters[] = {
1004         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1005         {NULL}
1006 };
1007
1008 PyTypeObject NotificationType = {
1009         CPY_INIT_TYPE
1010         "collectd.Notification",   /* tp_name */
1011         sizeof(Notification),      /* tp_basicsize */
1012         0,                         /* Will be filled in later */
1013         0,                         /* tp_dealloc */
1014         0,                         /* tp_print */
1015         0,                         /* tp_getattr */
1016         0,                         /* tp_setattr */
1017         0,                         /* tp_compare */
1018         Notification_repr,         /* tp_repr */
1019         0,                         /* tp_as_number */
1020         0,                         /* tp_as_sequence */
1021         0,                         /* tp_as_mapping */
1022         0,                         /* tp_hash */
1023         0,                         /* tp_call */
1024         0,                         /* tp_str */
1025         0,                         /* tp_getattro */
1026         0,                         /* tp_setattro */
1027         0,                         /* tp_as_buffer */
1028         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1029         Notification_doc,          /* tp_doc */
1030         0,                         /* tp_traverse */
1031         0,                         /* tp_clear */
1032         0,                         /* tp_richcompare */
1033         0,                         /* tp_weaklistoffset */
1034         0,                         /* tp_iter */
1035         0,                         /* tp_iternext */
1036         Notification_methods,      /* tp_methods */
1037         Notification_members,      /* tp_members */
1038         Notification_getseters,    /* tp_getset */
1039         0,                         /* tp_base */
1040         0,                         /* tp_dict */
1041         0,                         /* tp_descr_get */
1042         0,                         /* tp_descr_set */
1043         0,                         /* tp_dictoffset */
1044         Notification_init,         /* tp_init */
1045         0,                         /* tp_alloc */
1046         Notification_new           /* tp_new */
1047 };
1048
1049 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1050                 "to choose the way it is stored in the meta data.";
1051
1052 PyTypeObject SignedType = {
1053         CPY_INIT_TYPE
1054         "collectd.Signed",         /* tp_name */
1055         sizeof(Signed),            /* tp_basicsize */
1056         0,                         /* Will be filled in later */
1057         0,                         /* tp_dealloc */
1058         0,                         /* tp_print */
1059         0,                         /* tp_getattr */
1060         0,                         /* tp_setattr */
1061         0,                         /* tp_compare */
1062         0,                         /* tp_repr */
1063         0,                         /* tp_as_number */
1064         0,                         /* tp_as_sequence */
1065         0,                         /* tp_as_mapping */
1066         0,                         /* tp_hash */
1067         0,                         /* tp_call */
1068         0,                         /* tp_str */
1069         0,                         /* tp_getattro */
1070         0,                         /* tp_setattro */
1071         0,                         /* tp_as_buffer */
1072         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1073         Signed_doc                 /* tp_doc */
1074 };
1075
1076 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1077                 "to choose the way it is stored in the meta data.";
1078
1079 PyTypeObject UnsignedType = {
1080         CPY_INIT_TYPE
1081         "collectd.Unsigned",       /* tp_name */
1082         sizeof(Unsigned),          /* tp_basicsize */
1083         0,                         /* Will be filled in later */
1084         0,                         /* tp_dealloc */
1085         0,                         /* tp_print */
1086         0,                         /* tp_getattr */
1087         0,                         /* tp_setattr */
1088         0,                         /* tp_compare */
1089         0,                         /* tp_repr */
1090         0,                         /* tp_as_number */
1091         0,                         /* tp_as_sequence */
1092         0,                         /* tp_as_mapping */
1093         0,                         /* tp_hash */
1094         0,                         /* tp_call */
1095         0,                         /* tp_str */
1096         0,                         /* tp_getattro */
1097         0,                         /* tp_setattro */
1098         0,                         /* tp_as_buffer */
1099         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1100         Unsigned_doc               /* tp_doc */
1101 };