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