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