2 * collectd - src/pyvalues.c
3 * Copyright (C) 2009 Sven Trenkel
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 * Sven Trenkel <collectd at semidefinite.de>
28 #include <structmember.h>
35 static PyObject *cpy_common_repr(PyObject *s) {
37 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
38 static PyObject *l_host = NULL, *l_time = NULL;
39 PluginData *self = (PluginData *) s;
42 l_type = cpy_string_to_unicode_or_bytes("(type=");
43 if (l_type_instance == NULL)
44 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
46 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
47 if (l_plugin_instance == NULL)
48 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
50 l_host = cpy_string_to_unicode_or_bytes(",host=");
52 l_time = cpy_string_to_unicode_or_bytes(",time=");
54 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
57 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
59 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type);
60 tmp = cpy_string_to_unicode_or_bytes(self->type);
61 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
63 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
66 if (self->type_instance[0] != 0) {
67 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_type_instance);
68 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
69 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
71 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
75 if (self->plugin[0] != 0) {
76 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin);
77 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
78 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
80 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
84 if (self->plugin_instance[0] != 0) {
85 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_plugin_instance);
86 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
87 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
89 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
93 if (self->host[0] != 0) {
94 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_host);
95 tmp = cpy_string_to_unicode_or_bytes(self->host);
96 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
98 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
102 if (self->time != 0) {
103 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_time);
104 tmp = PyInt_FromLong(self->time);
105 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
107 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
113 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
114 "For dispatching values this can be set to 0 which means \"now\".\n"
115 "This means the time the value is actually dispatched, not the time\n"
118 static char host_doc[] = "The hostname of the host this value was read from.\n"
119 "For dispatching this can be set to an empty string which means\n"
120 "the local hostname as defined in the collectd.conf.";
122 static char type_doc[] = "The type of this value. This type has to be defined\n"
123 "in your types.db. Attempting to set it to any other value will\n"
124 "raise a TypeError exception.\n"
125 "Assigning a type is mandetory, calling dispatch without doing\n"
126 "so will raise a RuntimeError exception.";
128 static char type_instance_doc[] = "";
130 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
131 "member to an empty string will insert \"python\" upon dispatching.";
133 static char plugin_instance_doc[] = "";
135 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
136 "and Notification. It is pretty useless by itself and was therefore not\n"
137 "exported to the collectd module.";
139 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
142 self = (PluginData *) type->tp_alloc(type, 0);
149 self->plugin_instance[0] = 0;
151 self->type_instance[0] = 0;
152 return (PyObject *) self;
155 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
156 PluginData *self = (PluginData *) s;
158 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
159 static char *kwlist[] = {"type", "plugin_instance", "type_instance",
160 "plugin", "host", "time", NULL};
162 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
163 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
166 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
167 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
171 sstrncpy(self->host, host, sizeof(self->host));
172 sstrncpy(self->plugin, plugin, sizeof(self->plugin));
173 sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
174 sstrncpy(self->type, type, sizeof(self->type));
175 sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
181 static PyObject *PluginData_repr(PyObject *s) {
183 static PyObject *l_closing = NULL;
185 if (l_closing == NULL)
186 l_closing = cpy_string_to_unicode_or_bytes(")");
188 if (l_closing == NULL)
191 ret = cpy_common_repr(s);
192 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
196 static PyMemberDef PluginData_members[] = {
197 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
201 static PyObject *PluginData_getstring(PyObject *self, void *data) {
202 const char *value = ((char *) self) + (intptr_t) data;
204 return cpy_string_to_unicode_or_bytes(value);
207 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
212 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
216 new = cpy_unicode_or_bytes_to_string(&value);
221 old = ((char *) self) + (intptr_t) data;
222 sstrncpy(old, new, DATA_MAX_NAME_LEN);
227 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
232 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
236 new = cpy_unicode_or_bytes_to_string(&value);
242 if (plugin_get_ds(new) == NULL) {
243 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
248 old = ((char *) self) + (intptr_t) data;
249 sstrncpy(old, new, DATA_MAX_NAME_LEN);
254 static PyGetSetDef PluginData_getseters[] = {
255 {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
256 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
257 {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
258 {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
259 {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
263 PyTypeObject PluginDataType = {
265 "collectd.PluginData", /* tp_name */
266 sizeof(PluginData), /* tp_basicsize */
267 0, /* Will be filled in later */
273 PluginData_repr, /* tp_repr */
274 0, /* tp_as_number */
275 0, /* tp_as_sequence */
276 0, /* tp_as_mapping */
282 0, /* tp_as_buffer */
283 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
284 PluginData_doc, /* tp_doc */
287 0, /* tp_richcompare */
288 0, /* tp_weaklistoffset */
292 PluginData_members, /* tp_members */
293 PluginData_getseters, /* tp_getset */
296 0, /* tp_descr_get */
297 0, /* tp_descr_set */
298 0, /* tp_dictoffset */
299 PluginData_init, /* tp_init */
301 PluginData_new /* tp_new */
304 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
305 "the same data source. This value has to be a positive integer, so you can't\n"
306 "submit more than one value per second. If this member is set to a\n"
307 "non-positive value, the default value as specified in the config file will\n"
308 "be used (default: 10).\n"
310 "If you submit values more often than the specified interval, the average\n"
311 "will be used. If you submit less values, your graphs will have gaps.";
313 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
314 "It has to be a sequence (a tuple or list) of numbers.\n"
315 "The size of the sequence and the type of its content depend on the type\n"
316 "member your types.db file. For more information on this read the types.db\n"
319 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
320 "exception will be raised. If the content of the sequence is not a number,\n"
321 "a TypeError exception will be raised.";
323 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
324 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
326 "Dispatch this instance to the collectd process. The object has members\n"
327 "for each of the possible arguments for this method. For a detailed explanation\n"
328 "of these parameters see the member of the same same.\n"
330 "If you do not submit a parameter the value saved in its member will be submitted.\n"
331 "If you do provide a parameter it will be used instead, without altering the member.";
333 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
334 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
336 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
337 "This will bypass the main collectd process and all filtering and caching.\n"
338 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
339 "used instead of 'write'.\n";
341 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
343 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
346 self = (Values *) PluginData_new(type, args, kwds);
350 self->values = PyList_New(0);
352 return (PyObject *) self;
355 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
356 Values *self = (Values *) s;
359 PyObject *values = NULL, *tmp;
360 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
361 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
362 "plugin", "host", "time", "interval", NULL};
364 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
365 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
366 NULL, &plugin, NULL, &host, &time, &interval))
369 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
370 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
374 sstrncpy(self->data.host, host, sizeof(self->data.host));
375 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
376 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
377 sstrncpy(self->data.type, type, sizeof(self->data.type));
378 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
379 self->data.time = time;
381 if (values == NULL) {
382 values = PyList_New(0);
389 self->values = values;
392 self->interval = interval;
396 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
398 const data_set_t *ds;
401 value_list_t value_list = VALUE_LIST_INIT;
402 PyObject *values = self->values;
403 double time = self->data.time;
404 int interval = self->interval;
405 const char *host = self->data.host;
406 const char *plugin = self->data.plugin;
407 const char *plugin_instance = self->data.plugin_instance;
408 const char *type = self->data.type;
409 const char *type_instance = self->data.type_instance;
411 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
412 "plugin", "host", "time", "interval", NULL};
413 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
414 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
415 NULL, &plugin, NULL, &host, &time, &interval))
419 PyErr_SetString(PyExc_RuntimeError, "type not set");
422 ds = plugin_get_ds(type);
424 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
427 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
428 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
431 size = (int) PySequence_Length(values);
432 if (size != ds->ds_num) {
433 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
436 value = malloc(size * sizeof(*value));
437 for (i = 0; i < size; ++i) {
438 PyObject *item, *num;
439 item = PySequence_GetItem(values, i);
440 if (ds->ds->type == DS_TYPE_COUNTER) {
441 num = PyNumber_Long(item);
443 value[i].counter = PyLong_AsUnsignedLongLong(num);
444 } else if (ds->ds->type == DS_TYPE_GAUGE) {
445 num = PyNumber_Float(item);
447 value[i].gauge = PyFloat_AsDouble(num);
448 } else if (ds->ds->type == DS_TYPE_DERIVE) {
449 /* This might overflow without raising an exception.
450 * Not much we can do about it */
451 num = PyNumber_Long(item);
453 value[i].derive = PyLong_AsLongLong(num);
454 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
455 /* This might overflow without raising an exception.
456 * Not much we can do about it */
457 num = PyNumber_Long(item);
459 value[i].absolute = PyLong_AsUnsignedLongLong(num);
462 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
465 if (PyErr_Occurred() != NULL) {
470 value_list.values = value;
471 value_list.values_len = size;
472 value_list.time = time;
473 value_list.interval = interval;
474 sstrncpy(value_list.host, host, sizeof(value_list.host));
475 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
476 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
477 sstrncpy(value_list.type, type, sizeof(value_list.type));
478 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
479 value_list.meta = NULL;
480 if (value_list.host[0] == 0)
481 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
482 if (value_list.plugin[0] == 0)
483 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
484 Py_BEGIN_ALLOW_THREADS;
485 ret = plugin_dispatch_values(&value_list);
486 Py_END_ALLOW_THREADS;
488 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
495 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
497 const data_set_t *ds;
500 value_list_t value_list = VALUE_LIST_INIT;
501 PyObject *values = self->values;
502 double time = self->data.time;
503 int interval = self->interval;
504 const char *host = self->data.host;
505 const char *plugin = self->data.plugin;
506 const char *plugin_instance = self->data.plugin_instance;
507 const char *type = self->data.type;
508 const char *type_instance = self->data.type_instance;
509 const char *dest = NULL;
511 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
512 "plugin", "host", "time", "interval", NULL};
513 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdi", kwlist,
514 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
515 NULL, &plugin, NULL, &host, &time, &interval))
519 PyErr_SetString(PyExc_RuntimeError, "type not set");
522 ds = plugin_get_ds(type);
524 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
527 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
528 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
531 size = (int) PySequence_Length(values);
532 if (size != ds->ds_num) {
533 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
536 value = malloc(size * sizeof(*value));
537 for (i = 0; i < size; ++i) {
538 PyObject *item, *num;
539 item = PySequence_GetItem(values, i);
540 if (ds->ds->type == DS_TYPE_COUNTER) {
541 num = PyNumber_Long(item);
543 value[i].counter = PyLong_AsUnsignedLongLong(num);
544 } else if (ds->ds->type == DS_TYPE_GAUGE) {
545 num = PyNumber_Float(item);
547 value[i].gauge = PyFloat_AsDouble(num);
548 } else if (ds->ds->type == DS_TYPE_DERIVE) {
549 /* This might overflow without raising an exception.
550 * Not much we can do about it */
551 num = PyNumber_Long(item);
553 value[i].derive = PyLong_AsLongLong(num);
554 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
555 /* This might overflow without raising an exception.
556 * Not much we can do about it */
557 num = PyNumber_Long(item);
559 value[i].absolute = PyLong_AsUnsignedLongLong(num);
562 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
565 if (PyErr_Occurred() != NULL) {
570 value_list.values = value;
571 value_list.values_len = size;
572 value_list.time = time;
573 value_list.interval = interval;
574 sstrncpy(value_list.host, host, sizeof(value_list.host));
575 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
576 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
577 sstrncpy(value_list.type, type, sizeof(value_list.type));
578 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
579 value_list.meta = NULL;
580 if (value_list.host[0] == 0)
581 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
582 if (value_list.plugin[0] == 0)
583 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
584 Py_BEGIN_ALLOW_THREADS;
585 ret = plugin_write(dest, NULL, &value_list);
586 Py_END_ALLOW_THREADS;
588 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
595 static PyObject *Values_repr(PyObject *s) {
597 static PyObject *l_interval = NULL, *l_values = NULL, *l_closing = NULL;
598 Values *self = (Values *) s;
600 if (l_interval == NULL)
601 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
602 if (l_values == NULL)
603 l_values = cpy_string_to_unicode_or_bytes(",values=");
604 if (l_closing == NULL)
605 l_closing = cpy_string_to_unicode_or_bytes(")");
607 if (l_interval == NULL || l_values == NULL || l_closing == NULL)
610 ret = cpy_common_repr(s);
611 if (self->interval != 0) {
612 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_interval);
613 tmp = PyInt_FromLong(self->interval);
614 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
616 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
619 if (self->values != NULL && PySequence_Length(self->values) > 0) {
620 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_values);
621 tmp = PyObject_Repr(self->values);
623 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
626 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
630 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
631 Values *v = (Values *) self;
636 static int Values_clear(PyObject *self) {
637 Values *v = (Values *) self;
642 static void Values_dealloc(PyObject *self) {
644 self->ob_type->tp_free(self);
647 static PyMemberDef Values_members[] = {
648 {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
649 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
653 static PyMethodDef Values_methods[] = {
654 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
655 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
659 PyTypeObject ValuesType = {
661 "collectd.Values", /* tp_name */
662 sizeof(Values), /* tp_basicsize */
663 0, /* Will be filled in later */
664 Values_dealloc, /* tp_dealloc */
669 Values_repr, /* tp_repr */
670 0, /* tp_as_number */
671 0, /* tp_as_sequence */
672 0, /* tp_as_mapping */
678 0, /* tp_as_buffer */
679 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
680 Values_doc, /* tp_doc */
681 Values_traverse, /* tp_traverse */
682 Values_clear, /* tp_clear */
683 0, /* tp_richcompare */
684 0, /* tp_weaklistoffset */
687 Values_methods, /* tp_methods */
688 Values_members, /* tp_members */
692 0, /* tp_descr_get */
693 0, /* tp_descr_set */
694 0, /* tp_dictoffset */
695 Values_init, /* tp_init */
697 Values_new /* tp_new */
700 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
701 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
703 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
705 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
706 "It can be used to notify other plugins about bad stuff happening. It works\n"
707 "similar to Values but has a severity and a message instead of interval\n"
709 "Notifications can be dispatched at any time and can be received with register_notification.";
711 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
712 Notification *self = (Notification *) s;
715 const char *message = "";
716 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
717 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
718 "plugin", "host", "time", "severity", NULL};
720 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
721 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
722 NULL, &plugin, NULL, &host, &time, &severity))
725 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
726 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
730 sstrncpy(self->data.host, host, sizeof(self->data.host));
731 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
732 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
733 sstrncpy(self->data.type, type, sizeof(self->data.type));
734 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
735 self->data.time = time;
737 sstrncpy(self->message, message, sizeof(self->message));
738 self->severity = severity;
742 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
744 const data_set_t *ds;
745 notification_t notification;
746 double t = self->data.time;
747 int severity = self->severity;
748 const char *host = self->data.host;
749 const char *plugin = self->data.plugin;
750 const char *plugin_instance = self->data.plugin_instance;
751 const char *type = self->data.type;
752 const char *type_instance = self->data.type_instance;
753 const char *message = self->message;
755 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
756 "plugin", "host", "time", "severity", NULL};
757 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
758 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
759 NULL, &plugin, NULL, &host, &t, &severity))
763 PyErr_SetString(PyExc_RuntimeError, "type not set");
766 ds = plugin_get_ds(type);
768 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
772 notification.time = t;
773 notification.severity = severity;
774 sstrncpy(notification.message, message, sizeof(notification.message));
775 sstrncpy(notification.host, host, sizeof(notification.host));
776 sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
777 sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
778 sstrncpy(notification.type, type, sizeof(notification.type));
779 sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
780 notification.meta = NULL;
781 if (notification.time < 1)
782 notification.time = time(0);
783 if (notification.host[0] == 0)
784 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
785 if (notification.plugin[0] == 0)
786 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
787 Py_BEGIN_ALLOW_THREADS;
788 ret = plugin_dispatch_notification(¬ification);
789 Py_END_ALLOW_THREADS;
791 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
797 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
800 self = (Notification *) PluginData_new(type, args, kwds);
804 self->message[0] = 0;
806 return (PyObject *) self;
809 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
814 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
818 new = cpy_unicode_or_bytes_to_string(&value);
823 old = ((char *) self) + (intptr_t) data;
824 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
829 /*static PyObject *Notification_repr(PyObject *s) {
831 Notification *self = (Notification *) s;
833 ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
834 *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
835 *self->data.plugin ? "',plugin='" : "", self->data.plugin,
836 *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
837 *self->data.host ? "',host='" : "", self->data.host,
838 *self->message ? "',message='" : "", self->message,
839 (long unsigned) self->data.time, self->severity);
843 static PyMethodDef Notification_methods[] = {
844 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
848 static PyMemberDef Notification_members[] = {
849 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
853 static PyGetSetDef Notification_getseters[] = {
854 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
858 PyTypeObject NotificationType = {
860 "collectd.Notification", /* tp_name */
861 sizeof(Notification), /* tp_basicsize */
862 0, /* Will be filled in later */
868 0/*Notification_repr*/, /* tp_repr */
869 0, /* tp_as_number */
870 0, /* tp_as_sequence */
871 0, /* tp_as_mapping */
877 0, /* tp_as_buffer */
878 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
879 Notification_doc, /* tp_doc */
882 0, /* tp_richcompare */
883 0, /* tp_weaklistoffset */
886 Notification_methods, /* tp_methods */
887 Notification_members, /* tp_members */
888 Notification_getseters, /* tp_getset */
891 0, /* tp_descr_get */
892 0, /* tp_descr_set */
893 0, /* tp_dictoffset */
894 Notification_init, /* tp_init */
896 Notification_new /* tp_new */