Code

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