Code

Merge branch 'collectd-5.5' into collectd-5.6
[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                 switch (ds->ds[i].type) {
556                 case DS_TYPE_COUNTER:
557                         num = PyNumber_Long(item); /* New reference. */
558                         if (num != NULL) {
559                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
560                                 Py_XDECREF(num);
561                         }
562                         break;
563                 case DS_TYPE_GAUGE:
564                         num = PyNumber_Float(item); /* New reference. */
565                         if (num != NULL) {
566                                 value[i].gauge = PyFloat_AsDouble(num);
567                                 Py_XDECREF(num);
568                         }
569                         break;
570                 case DS_TYPE_DERIVE:
571                         /* This might overflow without raising an exception.
572                          * Not much we can do about it */
573                         num = PyNumber_Long(item); /* New reference. */
574                         if (num != NULL) {
575                                 value[i].derive = PyLong_AsLongLong(num);
576                                 Py_XDECREF(num);
577                         }
578                         break;
579                 case DS_TYPE_ABSOLUTE:
580                         /* This might overflow without raising an exception.
581                          * Not much we can do about it */
582                         num = PyNumber_Long(item); /* New reference. */
583                         if (num != NULL) {
584                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
585                                 Py_XDECREF(num);
586                         }
587                         break;
588                 default:
589                         free(value);
590                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds[i].type, value_list.type);
591                         return NULL;
592                 }
593                 if (PyErr_Occurred() != NULL) {
594                         free(value);
595                         return NULL;
596                 }
597         }
598         value_list.values = value;
599         value_list.meta = cpy_build_meta(meta);
600         value_list.values_len = size;
601         value_list.time = DOUBLE_TO_CDTIME_T(time);
602         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
603         if (value_list.host[0] == 0)
604                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
605         if (value_list.plugin[0] == 0)
606                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
607         Py_BEGIN_ALLOW_THREADS;
608         ret = plugin_dispatch_values(&value_list);
609         Py_END_ALLOW_THREADS;
610         meta_data_destroy(value_list.meta);
611         free(value);
612         if (ret != 0) {
613                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
614                 return NULL;
615         }
616         Py_RETURN_NONE;
619 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
620         int ret;
621         const data_set_t *ds;
622         size_t size;
623         value_t *value;
624         value_list_t value_list = VALUE_LIST_INIT;
625         PyObject *values = self->values, *meta = self->meta;
626         double time = self->data.time, interval = self->interval;
627         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
629         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
630                         "plugin", "host", "time", "interval", "meta", NULL};
631         if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
632                         NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
633                         NULL, &plugin, NULL, &host, &time, &interval, &meta))
634                 return NULL;
636         sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
637         sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
638         sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
639         sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
640         sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
641         FreeAll();
642         if (value_list.type[0] == 0) {
643                 PyErr_SetString(PyExc_RuntimeError, "type not set");
644                 return NULL;
645         }
646         ds = plugin_get_ds(value_list.type);
647         if (ds == NULL) {
648                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
649                 return NULL;
650         }
651         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
652                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
653                 return NULL;
654         }
655         size = (size_t) PySequence_Length(values);
656         if (size != ds->ds_num) {
657                 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
658                 return NULL;
659         }
660         value = calloc(size, sizeof(*value));
661         for (size_t i = 0; i < size; ++i) {
662                 PyObject *item, *num;
663                 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
664                 switch (ds->ds[i].type) {
665                 case DS_TYPE_COUNTER:
666                         num = PyNumber_Long(item); /* New reference. */
667                         if (num != NULL) {
668                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
669                                 Py_XDECREF(num);
670                         }
671                         break;
672                 case DS_TYPE_GAUGE:
673                         num = PyNumber_Float(item); /* New reference. */
674                         if (num != NULL) {
675                                 value[i].gauge = PyFloat_AsDouble(num);
676                                 Py_XDECREF(num);
677                         }
678                         break;
679                 case DS_TYPE_DERIVE:
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].derive = PyLong_AsLongLong(num);
685                                 Py_XDECREF(num);
686                         }
687                         break;
688                 case DS_TYPE_ABSOLUTE:
689                         /* This might overflow without raising an exception.
690                          * Not much we can do about it */
691                         num = PyNumber_Long(item); /* New reference. */
692                         if (num != NULL) {
693                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
694                                 Py_XDECREF(num);
695                         }
696                         break;
697                 default:
698                         free(value);
699                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds[i].type, value_list.type);
700                         return NULL;
701                 }
702                 if (PyErr_Occurred() != NULL) {
703                         free(value);
704                         return NULL;
705                 }
706         }
707         value_list.values = value;
708         value_list.values_len = size;
709         value_list.time = DOUBLE_TO_CDTIME_T(time);
710         value_list.interval = DOUBLE_TO_CDTIME_T(interval);
711         value_list.meta = cpy_build_meta(meta);
712         if (value_list.host[0] == 0)
713                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
714         if (value_list.plugin[0] == 0)
715                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
716         Py_BEGIN_ALLOW_THREADS;
717         ret = plugin_write(dest, NULL, &value_list);
718         Py_END_ALLOW_THREADS;
719         meta_data_destroy(value_list.meta);
720         free(value);
721         if (ret != 0) {
722                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
723                 return NULL;
724         }
725         Py_RETURN_NONE;
728 static PyObject *Values_repr(PyObject *s) {
729         PyObject *ret, *tmp;
730         static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
731         Values *self = (Values *) s;
733         if (l_interval == NULL)
734                 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
735         if (l_values == NULL)
736                 l_values = cpy_string_to_unicode_or_bytes(",values=");
737         if (l_meta == NULL)
738                 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
739         if (l_closing == NULL)
740                 l_closing = cpy_string_to_unicode_or_bytes(")");
742         if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
743                 return NULL;
745         ret = cpy_common_repr(s);
746         if (self->interval != 0) {
747                 CPY_STRCAT(&ret, l_interval);
748                 tmp = PyFloat_FromDouble(self->interval);
749                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
750                 CPY_STRCAT_AND_DEL(&ret, tmp);
751         }
752         if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
753                 CPY_STRCAT(&ret, l_values);
754                 tmp = PyObject_Repr(self->values);
755                 CPY_STRCAT_AND_DEL(&ret, tmp);
756         }
757         if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
758                 CPY_STRCAT(&ret, l_meta);
759                 tmp = PyObject_Repr(self->meta);
760                 CPY_STRCAT_AND_DEL(&ret, tmp);
761         }
762         CPY_STRCAT(&ret, l_closing);
763         return ret;
766 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
767         Values *v = (Values *) self;
768         Py_VISIT(v->values);
769         Py_VISIT(v->meta);
770         return 0;
773 static int Values_clear(PyObject *self) {
774         Values *v = (Values *) self;
775         Py_CLEAR(v->values);
776         Py_CLEAR(v->meta);
777         return 0;
780 static void Values_dealloc(PyObject *self) {
781         Values_clear(self);
782         self->ob_type->tp_free(self);
785 static PyMemberDef Values_members[] = {
786         {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
787         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
788         {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
789         {NULL}
790 };
792 static PyMethodDef Values_methods[] = {
793         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
794         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
795         {NULL}
796 };
798 PyTypeObject ValuesType = {
799         CPY_INIT_TYPE
800         "collectd.Values",         /* tp_name */
801         sizeof(Values),            /* tp_basicsize */
802         0,                         /* Will be filled in later */
803         Values_dealloc,            /* tp_dealloc */
804         0,                         /* tp_print */
805         0,                         /* tp_getattr */
806         0,                         /* tp_setattr */
807         0,                         /* tp_compare */
808         Values_repr,               /* tp_repr */
809         0,                         /* tp_as_number */
810         0,                         /* tp_as_sequence */
811         0,                         /* tp_as_mapping */
812         0,                         /* tp_hash */
813         0,                         /* tp_call */
814         0,                         /* tp_str */
815         0,                         /* tp_getattro */
816         0,                         /* tp_setattro */
817         0,                         /* tp_as_buffer */
818         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
819         Values_doc,                /* tp_doc */
820         Values_traverse,           /* tp_traverse */
821         Values_clear,              /* tp_clear */
822         0,                         /* tp_richcompare */
823         0,                         /* tp_weaklistoffset */
824         0,                         /* tp_iter */
825         0,                         /* tp_iternext */
826         Values_methods,            /* tp_methods */
827         Values_members,            /* tp_members */
828         0,                         /* tp_getset */
829         0,                         /* tp_base */
830         0,                         /* tp_dict */
831         0,                         /* tp_descr_get */
832         0,                         /* tp_descr_set */
833         0,                         /* tp_dictoffset */
834         Values_init,               /* tp_init */
835         0,                         /* tp_alloc */
836         Values_new                 /* tp_new */
837 };
839 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
840                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
842 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
844 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
845                 "It can be used to notify other plugins about bad stuff happening. It works\n"
846                 "similar to Values but has a severity and a message instead of interval\n"
847                 "and time.\n"
848                 "Notifications can be dispatched at any time and can be received with register_notification.";
850 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
851         Notification *self = (Notification *) s;
852         int severity = 0;
853         double time = 0;
854         char *message = NULL;
855         char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
856         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
857                         "plugin", "host", "time", "severity", NULL};
859         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
860                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
861                         NULL, &plugin, NULL, &host, &time, &severity))
862                 return -1;
864         if (type && plugin_get_ds(type) == NULL) {
865                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
866                 FreeAll();
867                 PyMem_Free(message);
868                 return -1;
869         }
871         sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
872         sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
873         sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
874         sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
875         sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
876         sstrncpy(self->message, message ? message : "", sizeof(self->message));
877         self->data.time = time;
878         self->severity = severity;
880         FreeAll();
881         PyMem_Free(message);
882         return 0;
885 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
886         int ret;
887         const data_set_t *ds;
888         notification_t notification;
889         double t = self->data.time;
890         int severity = self->severity;
891         char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
892         char *message = NULL;
894         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
895                         "plugin", "host", "time", "severity", NULL};
896         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
897                         NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
898                         NULL, &plugin, NULL, &host, &t, &severity))
899                 return NULL;
901         notification.time = DOUBLE_TO_CDTIME_T(t);
902         notification.severity = severity;
903         sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
904         sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
905         sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
906         sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
907         sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
908         sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
909         notification.meta = NULL;
910         FreeAll();
911         PyMem_Free(message);
913         if (notification.type[0] == 0) {
914                 PyErr_SetString(PyExc_RuntimeError, "type not set");
915                 return NULL;
916         }
917         ds = plugin_get_ds(notification.type);
918         if (ds == NULL) {
919                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
920                 return NULL;
921         }
923         if (notification.time == 0)
924                 notification.time = cdtime();
925         if (notification.host[0] == 0)
926                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
927         if (notification.plugin[0] == 0)
928                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
929         Py_BEGIN_ALLOW_THREADS;
930         ret = plugin_dispatch_notification(&notification);
931         Py_END_ALLOW_THREADS;
932         if (ret != 0) {
933                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
934                 return NULL;
935         }
936         Py_RETURN_NONE;
939 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
940         Notification *self;
942         self = (Notification *) PluginData_new(type, args, kwds);
943         if (self == NULL)
944                 return NULL;
946         self->message[0] = 0;
947         self->severity = 0;
948         return (PyObject *) self;
951 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
952         char *old;
953         const char *new;
955         if (value == NULL) {
956                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
957                 return -1;
958         }
959         Py_INCREF(value);
960         new = cpy_unicode_or_bytes_to_string(&value);
961         if (new == NULL) {
962                 Py_DECREF(value);
963                 return -1;
964         }
965         old = ((char *) self) + (intptr_t) data;
966         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
967         Py_DECREF(value);
968         return 0;
971 static PyObject *Notification_repr(PyObject *s) {
972         PyObject *ret, *tmp;
973         static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
974         Notification *self = (Notification *) s;
976         if (l_severity == NULL)
977                 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
978         if (l_message == NULL)
979                 l_message = cpy_string_to_unicode_or_bytes(",message=");
980         if (l_closing == NULL)
981                 l_closing = cpy_string_to_unicode_or_bytes(")");
983         if (l_severity == NULL || l_message == NULL || l_closing == NULL)
984                 return NULL;
986         ret = cpy_common_repr(s);
987         if (self->severity != 0) {
988                 CPY_STRCAT(&ret, l_severity);
989                 tmp = PyInt_FromLong(self->severity);
990                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
991                 CPY_STRCAT_AND_DEL(&ret, tmp);
992         }
993         if (self->message[0] != 0) {
994                 CPY_STRCAT(&ret, l_message);
995                 tmp = cpy_string_to_unicode_or_bytes(self->message);
996                 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
997                 CPY_STRCAT_AND_DEL(&ret, tmp);
998         }
999         CPY_STRCAT(&ret, l_closing);
1000         return ret;
1003 static PyMethodDef Notification_methods[] = {
1004         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
1005         {NULL}
1006 };
1008 static PyMemberDef Notification_members[] = {
1009         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1010         {NULL}
1011 };
1013 static PyGetSetDef Notification_getseters[] = {
1014         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1015         {NULL}
1016 };
1018 PyTypeObject NotificationType = {
1019         CPY_INIT_TYPE
1020         "collectd.Notification",   /* tp_name */
1021         sizeof(Notification),      /* 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         Notification_repr,         /* 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         Notification_doc,          /* tp_doc */
1040         0,                         /* tp_traverse */
1041         0,                         /* tp_clear */
1042         0,                         /* tp_richcompare */
1043         0,                         /* tp_weaklistoffset */
1044         0,                         /* tp_iter */
1045         0,                         /* tp_iternext */
1046         Notification_methods,      /* tp_methods */
1047         Notification_members,      /* tp_members */
1048         Notification_getseters,    /* tp_getset */
1049         0,                         /* tp_base */
1050         0,                         /* tp_dict */
1051         0,                         /* tp_descr_get */
1052         0,                         /* tp_descr_set */
1053         0,                         /* tp_dictoffset */
1054         Notification_init,         /* tp_init */
1055         0,                         /* tp_alloc */
1056         Notification_new           /* tp_new */
1057 };
1059 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1060                 "to choose the way it is stored in the meta data.";
1062 PyTypeObject SignedType = {
1063         CPY_INIT_TYPE
1064         "collectd.Signed",         /* tp_name */
1065         sizeof(Signed),            /* tp_basicsize */
1066         0,                         /* Will be filled in later */
1067         0,                         /* tp_dealloc */
1068         0,                         /* tp_print */
1069         0,                         /* tp_getattr */
1070         0,                         /* tp_setattr */
1071         0,                         /* tp_compare */
1072         0,                         /* tp_repr */
1073         0,                         /* tp_as_number */
1074         0,                         /* tp_as_sequence */
1075         0,                         /* tp_as_mapping */
1076         0,                         /* tp_hash */
1077         0,                         /* tp_call */
1078         0,                         /* tp_str */
1079         0,                         /* tp_getattro */
1080         0,                         /* tp_setattro */
1081         0,                         /* tp_as_buffer */
1082         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1083         Signed_doc                 /* tp_doc */
1084 };
1086 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1087                 "to choose the way it is stored in the meta data.";
1089 PyTypeObject UnsignedType = {
1090         CPY_INIT_TYPE
1091         "collectd.Unsigned",       /* tp_name */
1092         sizeof(Unsigned),          /* tp_basicsize */
1093         0,                         /* Will be filled in later */
1094         0,                         /* tp_dealloc */
1095         0,                         /* tp_print */
1096         0,                         /* tp_getattr */
1097         0,                         /* tp_setattr */
1098         0,                         /* tp_compare */
1099         0,                         /* tp_repr */
1100         0,                         /* tp_as_number */
1101         0,                         /* tp_as_sequence */
1102         0,                         /* tp_as_mapping */
1103         0,                         /* tp_hash */
1104         0,                         /* tp_call */
1105         0,                         /* tp_str */
1106         0,                         /* tp_getattro */
1107         0,                         /* tp_setattro */
1108         0,                         /* tp_as_buffer */
1109         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1110         Unsigned_doc               /* tp_doc */
1111 };