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