Code

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