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