Code

core: Changed internal API to allow for per-plugin intervals.
[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  **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include "collectd.h"
31 #include "common.h"
33 #include "cpython.h"
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);
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);
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         }
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         }
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         }
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         }
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 }
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.";
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.";
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.";
116 static char type_instance_doc[] = "";
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.";
121 static char plugin_instance_doc[] = "";
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.";
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;
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         }
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;
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;
184 static PyMemberDef PluginData_members[] = {
185         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
186         {NULL}
187 };
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);
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;
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         }
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         }
236         old = ((char *) self) + (intptr_t) data;
237         sstrncpy(old, new, DATA_MAX_NAME_LEN);
238         Py_DECREF(value);
239         return 0;
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 };
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 };
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.";
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.";
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.";
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.";
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";
337 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
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;
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         }
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;
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);
399         self->interval = interval;
400         return 0;
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;
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;
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(/* default interval = */ 0);
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;
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         if (interval > 0.0)
562                 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
563         else
564                 value_list.interval = cpy_get_plugin_interval();
565         sstrncpy(value_list.host, host, sizeof(value_list.host));
566         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
567         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
568         sstrncpy(value_list.type, type, sizeof(value_list.type));
569         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
570         if (value_list.host[0] == 0)
571                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
572         if (value_list.plugin[0] == 0)
573                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
574         Py_BEGIN_ALLOW_THREADS;
575         ret = plugin_dispatch_values(&value_list);
576         Py_END_ALLOW_THREADS;
577         if (ret != 0) {
578                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
579                 return NULL;
580         }
581         free(value);
582         Py_RETURN_NONE;
585 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
586         int i, ret;
587         const data_set_t *ds;
588         int size;
589         value_t *value;
590         value_list_t value_list = VALUE_LIST_INIT(/* default interval = */ 0);
591         PyObject *values = self->values, *meta = self->meta;
592         double time = self->data.time, 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, "|etOetetetetddO", kwlist,
603                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
604                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
605                 return NULL;
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 = DOUBLE_TO_CDTIME_T(time);
662         if (interval > 0.0)
663                 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
664         else
665                 value_list.interval = cpy_get_plugin_interval();
666         sstrncpy(value_list.host, host, sizeof(value_list.host));
667         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
668         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
669         sstrncpy(value_list.type, type, sizeof(value_list.type));
670         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
671         value_list.meta = cpy_build_meta(meta);;
672         if (value_list.host[0] == 0)
673                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
674         if (value_list.plugin[0] == 0)
675                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
676         Py_BEGIN_ALLOW_THREADS;
677         ret = plugin_write(dest, NULL, &value_list);
678         Py_END_ALLOW_THREADS;
679         if (ret != 0) {
680                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
681                 return NULL;
682         }
683         free(value);
684         Py_RETURN_NONE;
687 static PyObject *Values_repr(PyObject *s) {
688         PyObject *ret, *tmp;
689         static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
690         Values *self = (Values *) s;
691         
692         if (l_interval == NULL)
693                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
694         if (l_values == NULL)
695                 l_values = cpy_string_to_unicode_or_bytes(",values=");
696         if (l_meta == NULL)
697                 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
698         if (l_closing == NULL)
699                 l_closing = cpy_string_to_unicode_or_bytes(")");
700         
701         if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
702                 return NULL;
703         
704         ret = cpy_common_repr(s);
705         if (self->interval != 0) {
706                 CPY_STRCAT(&ret, l_interval);
707                 tmp = PyFloat_FromDouble(self->interval);
708                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
709                 CPY_STRCAT_AND_DEL(&ret, tmp);
710         }
711         if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
712                 CPY_STRCAT(&ret, l_values);
713                 tmp = PyObject_Repr(self->values);
714                 CPY_STRCAT_AND_DEL(&ret, tmp);
715         }
716         if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
717                 CPY_STRCAT(&ret, l_meta);
718                 tmp = PyObject_Repr(self->meta);
719                 CPY_STRCAT_AND_DEL(&ret, tmp);
720         }
721         CPY_STRCAT(&ret, l_closing);
722         return ret;
725 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
726         Values *v = (Values *) self;
727         Py_VISIT(v->values);
728         Py_VISIT(v->meta);
729         return 0;
732 static int Values_clear(PyObject *self) {
733         Values *v = (Values *) self;
734         Py_CLEAR(v->values);
735         Py_CLEAR(v->meta);
736         return 0;
739 static void Values_dealloc(PyObject *self) {
740         Values_clear(self);
741         self->ob_type->tp_free(self);
744 static PyMemberDef Values_members[] = {
745         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
746         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
747         {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
748         {NULL}
749 };
751 static PyMethodDef Values_methods[] = {
752         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
753         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
754         {NULL}
755 };
757 PyTypeObject ValuesType = {
758         CPY_INIT_TYPE
759         "collectd.Values",         /* tp_name */
760         sizeof(Values),            /* tp_basicsize */
761         0,                         /* Will be filled in later */
762         Values_dealloc,            /* tp_dealloc */
763         0,                         /* tp_print */
764         0,                         /* tp_getattr */
765         0,                         /* tp_setattr */
766         0,                         /* tp_compare */
767         Values_repr,               /* tp_repr */
768         0,                         /* tp_as_number */
769         0,                         /* tp_as_sequence */
770         0,                         /* tp_as_mapping */
771         0,                         /* tp_hash */
772         0,                         /* tp_call */
773         0,                         /* tp_str */
774         0,                         /* tp_getattro */
775         0,                         /* tp_setattro */
776         0,                         /* tp_as_buffer */
777         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
778         Values_doc,                /* tp_doc */
779         Values_traverse,           /* tp_traverse */
780         Values_clear,              /* tp_clear */
781         0,                         /* tp_richcompare */
782         0,                         /* tp_weaklistoffset */
783         0,                         /* tp_iter */
784         0,                         /* tp_iternext */
785         Values_methods,            /* tp_methods */
786         Values_members,            /* tp_members */
787         0,                         /* tp_getset */
788         0,                         /* tp_base */
789         0,                         /* tp_dict */
790         0,                         /* tp_descr_get */
791         0,                         /* tp_descr_set */
792         0,                         /* tp_dictoffset */
793         Values_init,               /* tp_init */
794         0,                         /* tp_alloc */
795         Values_new                 /* tp_new */
796 };
798 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
799                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
801 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
803 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
804                 "It can be used to notify other plugins about bad stuff happening. It works\n"
805                 "similar to Values but has a severity and a message instead of interval\n"
806                 "and time.\n"
807                 "Notifications can be dispatched at any time and can be received with register_notification.";
809 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
810         Notification *self = (Notification *) s;
811         int severity = 0;
812         double time = 0;
813         const char *message = "";
814         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
815         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
816                         "plugin", "host", "time", "severity", NULL};
817         
818         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
819                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
820                         NULL, &plugin, NULL, &host, &time, &severity))
821                 return -1;
822         
823         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
824                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
825                 return -1;
826         }
828         sstrncpy(self->data.host, host, sizeof(self->data.host));
829         sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
830         sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
831         sstrncpy(self->data.type, type, sizeof(self->data.type));
832         sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
833         self->data.time = time;
835         sstrncpy(self->message, message, sizeof(self->message));
836         self->severity = severity;
837         return 0;
840 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
841         int ret;
842         const data_set_t *ds;
843         notification_t notification;
844         double t = self->data.time;
845         int severity = self->severity;
846         const char *host = self->data.host;
847         const char *plugin = self->data.plugin;
848         const char *plugin_instance = self->data.plugin_instance;
849         const char *type = self->data.type;
850         const char *type_instance = self->data.type_instance;
851         const char *message = self->message;
852         
853         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
854                         "plugin", "host", "time", "severity", NULL};
855         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
856                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
857                         NULL, &plugin, NULL, &host, &t, &severity))
858                 return NULL;
860         if (type[0] == 0) {
861                 PyErr_SetString(PyExc_RuntimeError, "type not set");
862                 return NULL;
863         }
864         ds = plugin_get_ds(type);
865         if (ds == NULL) {
866                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
867                 return NULL;
868         }
870         notification.time = DOUBLE_TO_CDTIME_T(t);
871         notification.severity = severity;
872         sstrncpy(notification.message, message, sizeof(notification.message));
873         sstrncpy(notification.host, host, sizeof(notification.host));
874         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
875         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
876         sstrncpy(notification.type, type, sizeof(notification.type));
877         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
878         notification.meta = NULL;
879         if (notification.time == 0)
880                 notification.time = cdtime();
881         if (notification.host[0] == 0)
882                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
883         if (notification.plugin[0] == 0)
884                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
885         Py_BEGIN_ALLOW_THREADS;
886         ret = plugin_dispatch_notification(&notification);
887         Py_END_ALLOW_THREADS;
888         if (ret != 0) {
889                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
890                 return NULL;
891         }
892         Py_RETURN_NONE;
895 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
896         Notification *self;
897         
898         self = (Notification *) PluginData_new(type, args, kwds);
899         if (self == NULL)
900                 return NULL;
901         
902         self->message[0] = 0;
903         self->severity = 0;
904         return (PyObject *) self;
907 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
908         char *old;
909         const char *new;
910         
911         if (value == NULL) {
912                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
913                 return -1;
914         }
915         Py_INCREF(value);
916         new = cpy_unicode_or_bytes_to_string(&value);
917         if (new == NULL) {
918                 Py_DECREF(value);
919                 return -1;
920         }
921         old = ((char *) self) + (intptr_t) data;
922         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
923         Py_DECREF(value);
924         return 0;
927 static PyObject *Notification_repr(PyObject *s) {
928         PyObject *ret, *tmp;
929         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
930         Notification *self = (Notification *) s;
931         
932         if (l_severity == NULL)
933                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
934         if (l_message == NULL)
935                 l_message = cpy_string_to_unicode_or_bytes(",message=");
936         if (l_closing == NULL)
937                 l_closing = cpy_string_to_unicode_or_bytes(")");
938         
939         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
940                 return NULL;
941         
942         ret = cpy_common_repr(s);
943         if (self->severity != 0) {
944                 CPY_STRCAT(&ret, l_severity);
945                 tmp = PyInt_FromLong(self->severity);
946                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
947                 CPY_STRCAT_AND_DEL(&ret, tmp);
948         }
949         if (self->message[0] != 0) {
950                 CPY_STRCAT(&ret, l_message);
951                 tmp = cpy_string_to_unicode_or_bytes(self->message);
952                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
953                 CPY_STRCAT_AND_DEL(&ret, tmp);
954         }
955         CPY_STRCAT(&ret, l_closing);
956         return ret;
959 static PyMethodDef Notification_methods[] = {
960         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
961         {NULL}
962 };
964 static PyMemberDef Notification_members[] = {
965         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
966         {NULL}
967 };
969 static PyGetSetDef Notification_getseters[] = {
970         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
971         {NULL}
972 };
974 PyTypeObject NotificationType = {
975         CPY_INIT_TYPE
976         "collectd.Notification",   /* tp_name */
977         sizeof(Notification),      /* tp_basicsize */
978         0,                         /* Will be filled in later */
979         0,                         /* tp_dealloc */
980         0,                         /* tp_print */
981         0,                         /* tp_getattr */
982         0,                         /* tp_setattr */
983         0,                         /* tp_compare */
984         Notification_repr,         /* tp_repr */
985         0,                         /* tp_as_number */
986         0,                         /* tp_as_sequence */
987         0,                         /* tp_as_mapping */
988         0,                         /* tp_hash */
989         0,                         /* tp_call */
990         0,                         /* tp_str */
991         0,                         /* tp_getattro */
992         0,                         /* tp_setattro */
993         0,                         /* tp_as_buffer */
994         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
995         Notification_doc,          /* tp_doc */
996         0,                         /* tp_traverse */
997         0,                         /* tp_clear */
998         0,                         /* tp_richcompare */
999         0,                         /* tp_weaklistoffset */
1000         0,                         /* tp_iter */
1001         0,                         /* tp_iternext */
1002         Notification_methods,      /* tp_methods */
1003         Notification_members,      /* tp_members */
1004         Notification_getseters,    /* tp_getset */
1005         0,                         /* tp_base */
1006         0,                         /* tp_dict */
1007         0,                         /* tp_descr_get */
1008         0,                         /* tp_descr_set */
1009         0,                         /* tp_dictoffset */
1010         Notification_init,         /* tp_init */
1011         0,                         /* tp_alloc */
1012         Notification_new           /* tp_new */
1013 };
1015 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1016                 "to choose the way it is stored in the meta data.";
1018 PyTypeObject SignedType = {
1019         CPY_INIT_TYPE
1020         "collectd.Signed",         /* tp_name */
1021         sizeof(Signed),            /* tp_basicsize */
1022         0,                         /* Will be filled in later */
1023         0,                         /* tp_dealloc */
1024         0,                         /* tp_print */
1025         0,                         /* tp_getattr */
1026         0,                         /* tp_setattr */
1027         0,                         /* tp_compare */
1028         0,                         /* tp_repr */
1029         0,                         /* tp_as_number */
1030         0,                         /* tp_as_sequence */
1031         0,                         /* tp_as_mapping */
1032         0,                         /* tp_hash */
1033         0,                         /* tp_call */
1034         0,                         /* tp_str */
1035         0,                         /* tp_getattro */
1036         0,                         /* tp_setattro */
1037         0,                         /* tp_as_buffer */
1038         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1039         Signed_doc                 /* tp_doc */
1040 };
1042 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1043                 "to choose the way it is stored in the meta data.";
1045 PyTypeObject UnsignedType = {
1046         CPY_INIT_TYPE
1047         "collectd.Unsigned",       /* tp_name */
1048         sizeof(Unsigned),          /* tp_basicsize */
1049         0,                         /* Will be filled in later */
1050         0,                         /* tp_dealloc */
1051         0,                         /* tp_print */
1052         0,                         /* tp_getattr */
1053         0,                         /* tp_setattr */
1054         0,                         /* tp_compare */
1055         0,                         /* tp_repr */
1056         0,                         /* tp_as_number */
1057         0,                         /* tp_as_sequence */
1058         0,                         /* tp_as_mapping */
1059         0,                         /* tp_hash */
1060         0,                         /* tp_call */
1061         0,                         /* tp_str */
1062         0,                         /* tp_getattro */
1063         0,                         /* tp_setattro */
1064         0,                         /* tp_as_buffer */
1065         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1066         Unsigned_doc               /* tp_doc */
1067 };