Somewhat workable python3 support. This breaks python2 support and the __repr__ funct...
[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  **/
26
27 #include <Python.h>
28 #include <structmember.h>
29
30 #include "collectd.h"
31 #include "common.h"
32
33 #include "cpython.h"
34
35 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
36                 "For dispatching values this can be set to 0 which means \"now\".\n"
37                 "This means the time the value is actually dispatched, not the time\n"
38                 "it was set to 0.";
39
40 static char host_doc[] = "The hostname of the host this value was read from.\n"
41                 "For dispatching this can be set to an empty string which means\n"
42                 "the local hostname as defined in the collectd.conf.";
43
44 static char type_doc[] = "The type of this value. This type has to be defined\n"
45                 "in your types.db. Attempting to set it to any other value will\n"
46                 "raise a TypeError exception.\n"
47                 "Assigning a type is mandetory, calling dispatch without doing\n"
48                 "so will raise a RuntimeError exception.";
49
50 static char type_instance_doc[] = "";
51
52 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
53                 "member to an empty string will insert \"python\" upon dispatching.";
54
55 static char plugin_instance_doc[] = "";
56
57 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
58                 "and Notification. It is pretty useless by itself and was therefore not\n"
59                 "exported to the collectd module.";
60
61 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
62         PluginData *self;
63         
64         self = (PluginData *) type->tp_alloc(type, 0);
65         if (self == NULL)
66                 return NULL;
67         
68         self->time = 0;
69         self->host[0] = 0;
70         self->plugin[0] = 0;
71         self->plugin_instance[0] = 0;
72         self->type[0] = 0;
73         self->type_instance[0] = 0;
74         return (PyObject *) self;
75 }
76
77 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
78         PluginData *self = (PluginData *) s;
79         double time = 0;
80         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
81         static char *kwlist[] = {"type", "plugin_instance", "type_instance",
82                         "plugin", "host", "time", NULL};
83         
84         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
85                         &plugin_instance, &type_instance, &plugin, &host, &time))
86                 return -1;
87         
88         if (type[0] != 0 && plugin_get_ds(type) == NULL) {
89                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
90                 return -1;
91         }
92
93         sstrncpy(self->host, host, sizeof(self->host));
94         sstrncpy(self->plugin, plugin, sizeof(self->plugin));
95         sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
96         sstrncpy(self->type, type, sizeof(self->type));
97         sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
98         
99         self->time = time;
100         return 0;
101 }
102
103 /*static PyObject *PluginData_repr(PyObject *s) {
104         PluginData *self = (PluginData *) s;
105         
106         return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
107                         *self->type_instance ? "',type_instance='" : "", self->type_instance,
108                         *self->plugin ? "',plugin='" : "", self->plugin,
109                         *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
110                         *self->host ? "',host='" : "", self->host,
111                         (long unsigned) self->time);
112 }*/
113
114 static PyMemberDef PluginData_members[] = {
115         {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
116         {NULL}
117 };
118
119 static PyObject *PluginData_getstring(PyObject *self, void *data) {
120         const char *value = ((char *) self) + (intptr_t) data;
121         
122         return cpy_string_to_unicode_or_bytes(value);
123 }
124
125 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
126         char *old;
127         const char *new;
128         
129         if (value == NULL) {
130                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
131                 return -1;
132         }
133         Py_INCREF(value);
134         new = cpy_unicode_or_bytes_to_string(&value);
135         if (new == NULL) {
136                 Py_DECREF(value);
137                 return -1;
138         }
139         old = ((char *) self) + (intptr_t) data;
140         sstrncpy(old, new, DATA_MAX_NAME_LEN);
141         Py_DECREF(value);
142         return 0;
143 }
144
145 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
146         char *old;
147         const char *new;
148         
149         if (value == NULL) {
150                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
151                 return -1;
152         }
153         Py_INCREF(value);
154         new = cpy_unicode_or_bytes_to_string(&value);
155         if (new == NULL) {
156                 Py_DECREF(value);
157                 return -1;
158         }
159
160         if (plugin_get_ds(new) == NULL) {
161                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
162                 Py_DECREF(value);
163                 return -1;
164         }
165
166         old = ((char *) self) + (intptr_t) data;
167         sstrncpy(old, new, DATA_MAX_NAME_LEN);
168         Py_DECREF(value);
169         return 0;
170 }
171
172 static PyGetSetDef PluginData_getseters[] = {
173         {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
174         {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
175         {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
176         {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
177         {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
178         {NULL}
179 };
180
181 PyTypeObject PluginDataType = {
182         CPY_INIT_TYPE
183         "collectd.PluginData",     /* tp_name */
184         sizeof(PluginData),        /* tp_basicsize */
185         0,                         /* Will be filled in later */
186         0,                         /* tp_dealloc */
187         0,                         /* tp_print */
188         0,                         /* tp_getattr */
189         0,                         /* tp_setattr */
190         0,                         /* tp_compare */
191         0/*PluginData_repr*/,           /* tp_repr */
192         0,                         /* tp_as_number */
193         0,                         /* tp_as_sequence */
194         0,                         /* tp_as_mapping */
195         0,                         /* tp_hash */
196         0,                         /* tp_call */
197         0,                         /* tp_str */
198         0,                         /* tp_getattro */
199         0,                         /* tp_setattro */
200         0,                         /* tp_as_buffer */
201         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
202         PluginData_doc,            /* tp_doc */
203         0,                         /* tp_traverse */
204         0,                         /* tp_clear */
205         0,                         /* tp_richcompare */
206         0,                         /* tp_weaklistoffset */
207         0,                         /* tp_iter */
208         0,                         /* tp_iternext */
209         0,                         /* tp_methods */
210         PluginData_members,        /* tp_members */
211         PluginData_getseters,      /* tp_getset */
212         0,                         /* tp_base */
213         0,                         /* tp_dict */
214         0,                         /* tp_descr_get */
215         0,                         /* tp_descr_set */
216         0,                         /* tp_dictoffset */
217         PluginData_init,           /* tp_init */
218         0,                         /* tp_alloc */
219         PluginData_new             /* tp_new */
220 };
221
222 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
223                 "the same data source. This value has to be a positive integer, so you can't\n"
224                 "submit more than one value per second. If this member is set to a\n"
225                 "non-positive value, the default value as specified in the config file will\n"
226                 "be used (default: 10).\n"
227                 "\n"
228                 "If you submit values more often than the specified interval, the average\n"
229                 "will be used. If you submit less values, your graphs will have gaps.";
230
231 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
232                 "It has to be a sequence (a tuple or list) of numbers.\n"
233                 "The size of the sequence and the type of its content depend on the type\n"
234                 "member your types.db file. For more information on this read the types.db\n"
235                 "man page.\n"
236                 "\n"
237                 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
238                 "exception will be raised. If the content of the sequence is not a number,\n"
239                 "a TypeError exception will be raised.";
240
241 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
242                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
243                 "\n"
244                 "Dispatch this instance to the collectd process. The object has members\n"
245                 "for each of the possible arguments for this method. For a detailed explanation\n"
246                 "of these parameters see the member of the same same.\n"
247                 "\n"
248                 "If you do not submit a parameter the value saved in its member will be submitted.\n"
249                 "If you do provide a parameter it will be used instead, without altering the member.";
250
251 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
252                 "[, plugin][, host][, time][, interval]) -> None.  Dispatch a value list.\n"
253                 "\n"
254                 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
255                 "This will bypass the main collectd process and all filtering and caching.\n"
256                 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
257                 "used instead of 'write'.\n";
258
259 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
260
261 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
262         Values *self;
263         
264         self = (Values *) PluginData_new(type, args, kwds);
265         if (self == NULL)
266                 return NULL;
267         
268         self->values = PyList_New(0);
269         self->interval = 0;
270         return (PyObject *) self;
271 }
272
273 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
274         Values *self = (Values *) s;
275         int interval = 0, ret;
276         double time = 0;
277         PyObject *values = NULL, *tmp;
278         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
279         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
280                         "plugin", "host", "time", "interval", NULL};
281         
282         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
283                         &type, &values, &plugin_instance, &type_instance,
284                         &plugin, &host, &time, &interval))
285                 return -1;
286         
287         tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
288         if (tmp == NULL)
289                 return -1;
290         ret = PluginDataType.tp_init(s, tmp, NULL);
291         Py_DECREF(tmp);
292         if (ret != 0)
293                 return -1;
294         
295         if (values == NULL) {
296                 values = PyList_New(0);
297                 PyErr_Clear();
298         } else {
299                 Py_INCREF(values);
300         }
301         
302         tmp = self->values;
303         self->values = values;
304         Py_XDECREF(tmp);
305         
306         self->interval = interval;
307         return 0;
308 }
309
310 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
311         int i, ret;
312         const data_set_t *ds;
313         int size;
314         value_t *value;
315         value_list_t value_list = VALUE_LIST_INIT;
316         PyObject *values = self->values;
317         double time = self->data.time;
318         int interval = self->interval;
319         const char *host = self->data.host;
320         const char *plugin = self->data.plugin;
321         const char *plugin_instance = self->data.plugin_instance;
322         const char *type = self->data.type;
323         const char *type_instance = self->data.type_instance;
324         
325         static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
326                         "plugin", "host", "time", "interval", NULL};
327         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
328                         &type, &values, &plugin_instance, &type_instance,
329                         &plugin, &host, &time, &interval))
330                 return NULL;
331
332         if (type[0] == 0) {
333                 PyErr_SetString(PyExc_RuntimeError, "type not set");
334                 return NULL;
335         }
336         ds = plugin_get_ds(type);
337         if (ds == NULL) {
338                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
339                 return NULL;
340         }
341         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
342                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
343                 return NULL;
344         }
345         size = (int) PySequence_Length(values);
346         if (size != ds->ds_num) {
347                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
348                 return NULL;
349         }
350         value = malloc(size * sizeof(*value));
351         for (i = 0; i < size; ++i) {
352                 PyObject *item, *num;
353                 item = PySequence_GetItem(values, i);
354                 if (ds->ds->type == DS_TYPE_COUNTER) {
355                         num = PyNumber_Long(item);
356                         if (num != NULL)
357                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
358                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
359                         num = PyNumber_Float(item);
360                         if (num != NULL)
361                                 value[i].gauge = PyFloat_AsDouble(num);
362                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
363                         /* This might overflow without raising an exception.
364                          * Not much we can do about it */
365                         num = PyNumber_Long(item);
366                         if (num != NULL)
367                                 value[i].derive = PyLong_AsLongLong(num);
368                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
369                         /* This might overflow without raising an exception.
370                          * Not much we can do about it */
371                         num = PyNumber_Long(item);
372                         if (num != NULL)
373                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
374                 } else {
375                         free(value);
376                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
377                         return NULL;
378                 }
379                 if (PyErr_Occurred() != NULL) {
380                         free(value);
381                         return NULL;
382                 }
383         }
384         value_list.values = value;
385         value_list.values_len = size;
386         value_list.time = time;
387         value_list.interval = interval;
388         sstrncpy(value_list.host, host, sizeof(value_list.host));
389         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
390         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
391         sstrncpy(value_list.type, type, sizeof(value_list.type));
392         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
393         value_list.meta = NULL;
394         if (value_list.host[0] == 0)
395                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
396         if (value_list.plugin[0] == 0)
397                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
398         Py_BEGIN_ALLOW_THREADS;
399         ret = plugin_dispatch_values(&value_list);
400         Py_END_ALLOW_THREADS;
401         if (ret != 0) {
402                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
403                 return NULL;
404         }
405         free(value);
406         Py_RETURN_NONE;
407 }
408
409 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
410         int i, ret;
411         const data_set_t *ds;
412         int size;
413         value_t *value;
414         value_list_t value_list = VALUE_LIST_INIT;
415         PyObject *values = self->values;
416         double time = self->data.time;
417         int interval = self->interval;
418         const char *host = self->data.host;
419         const char *plugin = self->data.plugin;
420         const char *plugin_instance = self->data.plugin_instance;
421         const char *type = self->data.type;
422         const char *type_instance = self->data.type_instance;
423         const char *dest = NULL;
424         
425         static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
426                         "plugin", "host", "time", "interval", NULL};
427         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
428                         &type, &values, &plugin_instance, &type_instance,
429                         &plugin, &host, &time, &interval))
430                 return NULL;
431
432         if (type[0] == 0) {
433                 PyErr_SetString(PyExc_RuntimeError, "type not set");
434                 return NULL;
435         }
436         ds = plugin_get_ds(type);
437         if (ds == NULL) {
438                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
439                 return NULL;
440         }
441         if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
442                 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
443                 return NULL;
444         }
445         size = (int) PySequence_Length(values);
446         if (size != ds->ds_num) {
447                 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
448                 return NULL;
449         }
450         value = malloc(size * sizeof(*value));
451         for (i = 0; i < size; ++i) {
452                 PyObject *item, *num;
453                 item = PySequence_GetItem(values, i);
454                 if (ds->ds->type == DS_TYPE_COUNTER) {
455                         num = PyNumber_Long(item);
456                         if (num != NULL)
457                                 value[i].counter = PyLong_AsUnsignedLongLong(num);
458                 } else if (ds->ds->type == DS_TYPE_GAUGE) {
459                         num = PyNumber_Float(item);
460                         if (num != NULL)
461                                 value[i].gauge = PyFloat_AsDouble(num);
462                 } else if (ds->ds->type == DS_TYPE_DERIVE) {
463                         /* This might overflow without raising an exception.
464                          * Not much we can do about it */
465                         num = PyNumber_Long(item);
466                         if (num != NULL)
467                                 value[i].derive = PyLong_AsLongLong(num);
468                 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
469                         /* This might overflow without raising an exception.
470                          * Not much we can do about it */
471                         num = PyNumber_Long(item);
472                         if (num != NULL)
473                                 value[i].absolute = PyLong_AsUnsignedLongLong(num);
474                 } else {
475                         free(value);
476                         PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
477                         return NULL;
478                 }
479                 if (PyErr_Occurred() != NULL) {
480                         free(value);
481                         return NULL;
482                 }
483         }
484         value_list.values = value;
485         value_list.values_len = size;
486         value_list.time = time;
487         value_list.interval = interval;
488         sstrncpy(value_list.host, host, sizeof(value_list.host));
489         sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
490         sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
491         sstrncpy(value_list.type, type, sizeof(value_list.type));
492         sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
493         value_list.meta = NULL;
494         if (value_list.host[0] == 0)
495                 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
496         if (value_list.plugin[0] == 0)
497                 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
498         Py_BEGIN_ALLOW_THREADS;
499         ret = plugin_write(dest, NULL, &value_list);
500         Py_END_ALLOW_THREADS;
501         if (ret != 0) {
502                 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
503                 return NULL;
504         }
505         free(value);
506         Py_RETURN_NONE;
507 }
508
509 /*static PyObject *Values_repr(PyObject *s) {
510         PyObject *ret, *valuestring = NULL;
511         Values *self = (Values *) s;
512         
513         if (self->values != NULL)
514                 valuestring = PyObject_Repr(self->values);
515         if (valuestring == NULL)
516                 return NULL;
517         
518         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
519                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
520                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
521                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
522                         *self->data.host ? "',host='" : "", self->data.host,
523                         (long unsigned) self->data.time, self->interval,
524                         valuestring ? cpy_unicode_or_bytes_to_string(valuestring) : "[]");
525         Py_XDECREF(valuestring);
526         return ret;
527 }*/
528
529 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
530         Values *v = (Values *) self;
531         Py_VISIT(v->values);
532         return 0;
533 }
534
535 static int Values_clear(PyObject *self) {
536         Values *v = (Values *) self;
537         Py_CLEAR(v->values);
538         return 0;
539 }
540
541 static void Values_dealloc(PyObject *self) {
542         Values_clear(self);
543         self->ob_type->tp_free(self);
544 }
545
546 static PyMemberDef Values_members[] = {
547         {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
548         {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
549         {NULL}
550 };
551
552 static PyMethodDef Values_methods[] = {
553         {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
554         {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
555         {NULL}
556 };
557
558 PyTypeObject ValuesType = {
559         CPY_INIT_TYPE
560         "collectd.Values",         /* tp_name */
561         sizeof(Values),            /* tp_basicsize */
562         0,                         /* Will be filled in later */
563         Values_dealloc,            /* tp_dealloc */
564         0,                         /* tp_print */
565         0,                         /* tp_getattr */
566         0,                         /* tp_setattr */
567         0,                         /* tp_compare */
568         0/*Values_repr*/,               /* tp_repr */
569         0,                         /* tp_as_number */
570         0,                         /* tp_as_sequence */
571         0,                         /* tp_as_mapping */
572         0,                         /* tp_hash */
573         0,                         /* tp_call */
574         0,                         /* tp_str */
575         0,                         /* tp_getattro */
576         0,                         /* tp_setattro */
577         0,                         /* tp_as_buffer */
578         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
579         Values_doc,                /* tp_doc */
580         Values_traverse,           /* tp_traverse */
581         Values_clear,              /* tp_clear */
582         0,                         /* tp_richcompare */
583         0,                         /* tp_weaklistoffset */
584         0,                         /* tp_iter */
585         0,                         /* tp_iternext */
586         Values_methods,            /* tp_methods */
587         Values_members,            /* tp_members */
588         0,                         /* tp_getset */
589         0,                         /* tp_base */
590         0,                         /* tp_dict */
591         0,                         /* tp_descr_get */
592         0,                         /* tp_descr_set */
593         0,                         /* tp_dictoffset */
594         Values_init,               /* tp_init */
595         0,                         /* tp_alloc */
596         Values_new                 /* tp_new */
597 };
598
599 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
600                 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
601
602 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
603
604 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
605                 "It can be used to notify other plugins about bad stuff happening. It works\n"
606                 "similar to Values but has a severity and a message instead of interval\n"
607                 "and time.\n"
608                 "Notifications can be dispatched at any time and can be received with register_notification.";
609
610 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
611         Notification *self = (Notification *) s;
612         PyObject *tmp;
613         int severity = 0, ret;
614         double time = 0;
615         const char *message = "";
616         const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
617         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
618                         "plugin", "host", "time", "severity", NULL};
619         
620         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
621                         &type, &message, &plugin_instance, &type_instance,
622                         &plugin, &host, &time, &severity))
623                 return -1;
624         
625         tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
626         if (tmp == NULL)
627                 return -1;
628         ret = PluginDataType.tp_init(s, tmp, NULL);
629         Py_DECREF(tmp);
630         if (ret != 0)
631                 return -1;
632         
633         sstrncpy(self->message, message, sizeof(self->message));
634         self->severity = severity;
635         return 0;
636 }
637
638 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
639         int ret;
640         const data_set_t *ds;
641         notification_t notification;
642         double t = self->data.time;
643         int severity = self->severity;
644         const char *host = self->data.host;
645         const char *plugin = self->data.plugin;
646         const char *plugin_instance = self->data.plugin_instance;
647         const char *type = self->data.type;
648         const char *type_instance = self->data.type_instance;
649         const char *message = self->message;
650         
651         static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
652                         "plugin", "host", "time", "severity", NULL};
653         if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
654                         &type, &message, &plugin_instance, &type_instance,
655                         &plugin, &host, &t, &severity))
656                 return NULL;
657
658         if (type[0] == 0) {
659                 PyErr_SetString(PyExc_RuntimeError, "type not set");
660                 return NULL;
661         }
662         ds = plugin_get_ds(type);
663         if (ds == NULL) {
664                 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
665                 return NULL;
666         }
667
668         notification.time = t;
669         notification.severity = severity;
670         sstrncpy(notification.message, message, sizeof(notification.message));
671         sstrncpy(notification.host, host, sizeof(notification.host));
672         sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
673         sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
674         sstrncpy(notification.type, type, sizeof(notification.type));
675         sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
676         notification.meta = NULL;
677         if (notification.time < 1)
678                 notification.time = time(0);
679         if (notification.host[0] == 0)
680                 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
681         if (notification.plugin[0] == 0)
682                 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
683         Py_BEGIN_ALLOW_THREADS;
684         ret = plugin_dispatch_notification(&notification);
685         Py_END_ALLOW_THREADS;
686         if (ret != 0) {
687                 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
688                 return NULL;
689         }
690         Py_RETURN_NONE;
691 }
692
693 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
694         Notification *self;
695         
696         self = (Notification *) PluginData_new(type, args, kwds);
697         if (self == NULL)
698                 return NULL;
699         
700         self->message[0] = 0;
701         self->severity = 0;
702         return (PyObject *) self;
703 }
704
705 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
706         char *old;
707         const char *new;
708         
709         if (value == NULL) {
710                 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
711                 return -1;
712         }
713         Py_INCREF(value);
714         new = cpy_unicode_or_bytes_to_string(&value);
715         if (new == NULL) {
716                 Py_DECREF(value);
717                 return -1;
718         }
719         old = ((char *) self) + (intptr_t) data;
720         sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
721         Py_DECREF(value);
722         return 0;
723 }
724
725 /*static PyObject *Notification_repr(PyObject *s) {
726         PyObject *ret;
727         Notification *self = (Notification *) s;
728         
729         ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
730                         *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
731                         *self->data.plugin ? "',plugin='" : "", self->data.plugin,
732                         *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
733                         *self->data.host ? "',host='" : "", self->data.host,
734                         *self->message ? "',message='" : "", self->message,
735                         (long unsigned) self->data.time, self->severity);
736         return ret;
737 }*/
738
739 static PyMethodDef Notification_methods[] = {
740         {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
741         {NULL}
742 };
743
744 static PyMemberDef Notification_members[] = {
745         {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
746         {NULL}
747 };
748
749 static PyGetSetDef Notification_getseters[] = {
750         {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
751         {NULL}
752 };
753
754 PyTypeObject NotificationType = {
755         CPY_INIT_TYPE
756         "collectd.Notification",   /* tp_name */
757         sizeof(Notification),      /* tp_basicsize */
758         0,                         /* Will be filled in later */
759         0,                         /* tp_dealloc */
760         0,                         /* tp_print */
761         0,                         /* tp_getattr */
762         0,                         /* tp_setattr */
763         0,                         /* tp_compare */
764         0/*Notification_repr*/,         /* tp_repr */
765         0,                         /* tp_as_number */
766         0,                         /* tp_as_sequence */
767         0,                         /* tp_as_mapping */
768         0,                         /* tp_hash */
769         0,                         /* tp_call */
770         0,                         /* tp_str */
771         0,                         /* tp_getattro */
772         0,                         /* tp_setattro */
773         0,                         /* tp_as_buffer */
774         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
775         Notification_doc,          /* tp_doc */
776         0,                         /* tp_traverse */
777         0,                         /* tp_clear */
778         0,                         /* tp_richcompare */
779         0,                         /* tp_weaklistoffset */
780         0,                         /* tp_iter */
781         0,                         /* tp_iternext */
782         Notification_methods,      /* tp_methods */
783         Notification_members,      /* tp_members */
784         Notification_getseters,    /* tp_getset */
785         0,                         /* tp_base */
786         0,                         /* tp_dict */
787         0,                         /* tp_descr_get */
788         0,                         /* tp_descr_set */
789         0,                         /* tp_dictoffset */
790         Notification_init,         /* tp_init */
791         0,                         /* tp_alloc */
792         Notification_new           /* tp_new */
793 };