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