Code

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