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