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