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