1 /**
2 * collectd - src/pyvalues.c
3 * Copyright (C) 2009 Sven Trenkel
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Sven Trenkel <collectd at semidefinite.de>
25 **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include "collectd.h"
31 #include "common.h"
33 #include "cpython.h"
35 static 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.";
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.";
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.";
50 static char type_instance_doc[] = "";
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.";
55 static char plugin_instance_doc[] = "";
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.";
61 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
62 PluginData *self;
64 self = (PluginData *) type->tp_alloc(type, 0);
65 if (self == NULL)
66 return NULL;
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 }
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};
84 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
85 &plugin_instance, &type_instance, &plugin, &host, &time))
86 return -1;
88 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
89 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
90 return -1;
91 }
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));
99 self->time = time;
100 return 0;
101 }
103 /*static PyObject *PluginData_repr(PyObject *s) {
104 PluginData *self = (PluginData *) s;
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 }*/
114 static PyMemberDef PluginData_members[] = {
115 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
116 {NULL}
117 };
119 static PyObject *PluginData_getstring(PyObject *self, void *data) {
120 const char *value = ((char *) self) + (intptr_t) data;
122 return cpy_string_to_unicode_or_bytes(value);
123 }
125 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
126 char *old;
127 const char *new;
129 if (value == NULL) {
130 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
131 return -1;
132 }
133 Py_INCREF(value);
134 new = cpy_unicode_or_bytes_to_string(&value);
135 if (new == NULL) {
136 Py_DECREF(value);
137 return -1;
138 }
139 old = ((char *) self) + (intptr_t) data;
140 sstrncpy(old, new, DATA_MAX_NAME_LEN);
141 Py_DECREF(value);
142 return 0;
143 }
145 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
146 char *old;
147 const char *new;
149 if (value == NULL) {
150 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
151 return -1;
152 }
153 Py_INCREF(value);
154 new = cpy_unicode_or_bytes_to_string(&value);
155 if (new == NULL) {
156 Py_DECREF(value);
157 return -1;
158 }
160 if (plugin_get_ds(new) == NULL) {
161 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
162 Py_DECREF(value);
163 return -1;
164 }
166 old = ((char *) self) + (intptr_t) data;
167 sstrncpy(old, new, DATA_MAX_NAME_LEN);
168 Py_DECREF(value);
169 return 0;
170 }
172 static PyGetSetDef PluginData_getseters[] = {
173 {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
174 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
175 {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
176 {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
177 {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
178 {NULL}
179 };
181 PyTypeObject PluginDataType = {
182 CPY_INIT_TYPE
183 "collectd.PluginData", /* tp_name */
184 sizeof(PluginData), /* tp_basicsize */
185 0, /* Will be filled in later */
186 0, /* tp_dealloc */
187 0, /* tp_print */
188 0, /* tp_getattr */
189 0, /* tp_setattr */
190 0, /* tp_compare */
191 0/*PluginData_repr*/, /* tp_repr */
192 0, /* tp_as_number */
193 0, /* tp_as_sequence */
194 0, /* tp_as_mapping */
195 0, /* tp_hash */
196 0, /* tp_call */
197 0, /* tp_str */
198 0, /* tp_getattro */
199 0, /* tp_setattro */
200 0, /* tp_as_buffer */
201 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
202 PluginData_doc, /* tp_doc */
203 0, /* tp_traverse */
204 0, /* tp_clear */
205 0, /* tp_richcompare */
206 0, /* tp_weaklistoffset */
207 0, /* tp_iter */
208 0, /* tp_iternext */
209 0, /* tp_methods */
210 PluginData_members, /* tp_members */
211 PluginData_getseters, /* tp_getset */
212 0, /* tp_base */
213 0, /* tp_dict */
214 0, /* tp_descr_get */
215 0, /* tp_descr_set */
216 0, /* tp_dictoffset */
217 PluginData_init, /* tp_init */
218 0, /* tp_alloc */
219 PluginData_new /* tp_new */
220 };
222 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
223 "the same data source. This value has to be a positive integer, so you can't\n"
224 "submit more than one value per second. If this member is set to a\n"
225 "non-positive value, the default value as specified in the config file will\n"
226 "be used (default: 10).\n"
227 "\n"
228 "If you submit values more often than the specified interval, the average\n"
229 "will be used. If you submit less values, your graphs will have gaps.";
231 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
232 "It has to be a sequence (a tuple or list) of numbers.\n"
233 "The size of the sequence and the type of its content depend on the type\n"
234 "member your types.db file. For more information on this read the types.db\n"
235 "man page.\n"
236 "\n"
237 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
238 "exception will be raised. If the content of the sequence is not a number,\n"
239 "a TypeError exception will be raised.";
241 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
242 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
243 "\n"
244 "Dispatch this instance to the collectd process. The object has members\n"
245 "for each of the possible arguments for this method. For a detailed explanation\n"
246 "of these parameters see the member of the same same.\n"
247 "\n"
248 "If you do not submit a parameter the value saved in its member will be submitted.\n"
249 "If you do provide a parameter it will be used instead, without altering the member.";
251 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
252 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
253 "\n"
254 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
255 "This will bypass the main collectd process and all filtering and caching.\n"
256 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
257 "used instead of 'write'.\n";
259 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
261 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
262 Values *self;
264 self = (Values *) PluginData_new(type, args, kwds);
265 if (self == NULL)
266 return NULL;
268 self->values = PyList_New(0);
269 self->interval = 0;
270 return (PyObject *) self;
271 }
273 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
274 Values *self = (Values *) s;
275 int interval = 0, ret;
276 double time = 0;
277 PyObject *values = NULL, *tmp;
278 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
279 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
280 "plugin", "host", "time", "interval", NULL};
282 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
283 &type, &values, &plugin_instance, &type_instance,
284 &plugin, &host, &time, &interval))
285 return -1;
287 tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
288 if (tmp == NULL)
289 return -1;
290 ret = PluginDataType.tp_init(s, tmp, NULL);
291 Py_DECREF(tmp);
292 if (ret != 0)
293 return -1;
295 if (values == NULL) {
296 values = PyList_New(0);
297 PyErr_Clear();
298 } else {
299 Py_INCREF(values);
300 }
302 tmp = self->values;
303 self->values = values;
304 Py_XDECREF(tmp);
306 self->interval = interval;
307 return 0;
308 }
310 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
311 int i, ret;
312 const data_set_t *ds;
313 int size;
314 value_t *value;
315 value_list_t value_list = VALUE_LIST_INIT;
316 PyObject *values = self->values;
317 double time = self->data.time;
318 int interval = self->interval;
319 const char *host = self->data.host;
320 const char *plugin = self->data.plugin;
321 const char *plugin_instance = self->data.plugin_instance;
322 const char *type = self->data.type;
323 const char *type_instance = self->data.type_instance;
325 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
326 "plugin", "host", "time", "interval", NULL};
327 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
328 &type, &values, &plugin_instance, &type_instance,
329 &plugin, &host, &time, &interval))
330 return NULL;
332 if (type[0] == 0) {
333 PyErr_SetString(PyExc_RuntimeError, "type not set");
334 return NULL;
335 }
336 ds = plugin_get_ds(type);
337 if (ds == NULL) {
338 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
339 return NULL;
340 }
341 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
342 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
343 return NULL;
344 }
345 size = (int) PySequence_Length(values);
346 if (size != ds->ds_num) {
347 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
348 return NULL;
349 }
350 value = malloc(size * sizeof(*value));
351 for (i = 0; i < size; ++i) {
352 PyObject *item, *num;
353 item = PySequence_GetItem(values, i);
354 if (ds->ds->type == DS_TYPE_COUNTER) {
355 num = PyNumber_Long(item);
356 if (num != NULL)
357 value[i].counter = PyLong_AsUnsignedLongLong(num);
358 } else if (ds->ds->type == DS_TYPE_GAUGE) {
359 num = PyNumber_Float(item);
360 if (num != NULL)
361 value[i].gauge = PyFloat_AsDouble(num);
362 } else if (ds->ds->type == DS_TYPE_DERIVE) {
363 /* This might overflow without raising an exception.
364 * Not much we can do about it */
365 num = PyNumber_Long(item);
366 if (num != NULL)
367 value[i].derive = PyLong_AsLongLong(num);
368 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
369 /* This might overflow without raising an exception.
370 * Not much we can do about it */
371 num = PyNumber_Long(item);
372 if (num != NULL)
373 value[i].absolute = PyLong_AsUnsignedLongLong(num);
374 } else {
375 free(value);
376 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
377 return NULL;
378 }
379 if (PyErr_Occurred() != NULL) {
380 free(value);
381 return NULL;
382 }
383 }
384 value_list.values = value;
385 value_list.values_len = size;
386 value_list.time = time;
387 value_list.interval = interval;
388 sstrncpy(value_list.host, host, sizeof(value_list.host));
389 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
390 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
391 sstrncpy(value_list.type, type, sizeof(value_list.type));
392 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
393 value_list.meta = NULL;
394 if (value_list.host[0] == 0)
395 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
396 if (value_list.plugin[0] == 0)
397 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
398 Py_BEGIN_ALLOW_THREADS;
399 ret = plugin_dispatch_values(&value_list);
400 Py_END_ALLOW_THREADS;
401 if (ret != 0) {
402 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
403 return NULL;
404 }
405 free(value);
406 Py_RETURN_NONE;
407 }
409 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
410 int i, ret;
411 const data_set_t *ds;
412 int size;
413 value_t *value;
414 value_list_t value_list = VALUE_LIST_INIT;
415 PyObject *values = self->values;
416 double time = self->data.time;
417 int interval = self->interval;
418 const char *host = self->data.host;
419 const char *plugin = self->data.plugin;
420 const char *plugin_instance = self->data.plugin_instance;
421 const char *type = self->data.type;
422 const char *type_instance = self->data.type_instance;
423 const char *dest = NULL;
425 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
426 "plugin", "host", "time", "interval", NULL};
427 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
428 &type, &values, &plugin_instance, &type_instance,
429 &plugin, &host, &time, &interval))
430 return NULL;
432 if (type[0] == 0) {
433 PyErr_SetString(PyExc_RuntimeError, "type not set");
434 return NULL;
435 }
436 ds = plugin_get_ds(type);
437 if (ds == NULL) {
438 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
439 return NULL;
440 }
441 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
442 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
443 return NULL;
444 }
445 size = (int) PySequence_Length(values);
446 if (size != ds->ds_num) {
447 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
448 return NULL;
449 }
450 value = malloc(size * sizeof(*value));
451 for (i = 0; i < size; ++i) {
452 PyObject *item, *num;
453 item = PySequence_GetItem(values, i);
454 if (ds->ds->type == DS_TYPE_COUNTER) {
455 num = PyNumber_Long(item);
456 if (num != NULL)
457 value[i].counter = PyLong_AsUnsignedLongLong(num);
458 } else if (ds->ds->type == DS_TYPE_GAUGE) {
459 num = PyNumber_Float(item);
460 if (num != NULL)
461 value[i].gauge = PyFloat_AsDouble(num);
462 } else if (ds->ds->type == DS_TYPE_DERIVE) {
463 /* This might overflow without raising an exception.
464 * Not much we can do about it */
465 num = PyNumber_Long(item);
466 if (num != NULL)
467 value[i].derive = PyLong_AsLongLong(num);
468 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
469 /* This might overflow without raising an exception.
470 * Not much we can do about it */
471 num = PyNumber_Long(item);
472 if (num != NULL)
473 value[i].absolute = PyLong_AsUnsignedLongLong(num);
474 } else {
475 free(value);
476 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
477 return NULL;
478 }
479 if (PyErr_Occurred() != NULL) {
480 free(value);
481 return NULL;
482 }
483 }
484 value_list.values = value;
485 value_list.values_len = size;
486 value_list.time = time;
487 value_list.interval = interval;
488 sstrncpy(value_list.host, host, sizeof(value_list.host));
489 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
490 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
491 sstrncpy(value_list.type, type, sizeof(value_list.type));
492 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
493 value_list.meta = NULL;
494 if (value_list.host[0] == 0)
495 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
496 if (value_list.plugin[0] == 0)
497 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
498 Py_BEGIN_ALLOW_THREADS;
499 ret = plugin_write(dest, NULL, &value_list);
500 Py_END_ALLOW_THREADS;
501 if (ret != 0) {
502 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
503 return NULL;
504 }
505 free(value);
506 Py_RETURN_NONE;
507 }
509 /*static PyObject *Values_repr(PyObject *s) {
510 PyObject *ret, *valuestring = NULL;
511 Values *self = (Values *) s;
513 if (self->values != NULL)
514 valuestring = PyObject_Repr(self->values);
515 if (valuestring == NULL)
516 return NULL;
518 ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
519 *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
520 *self->data.plugin ? "',plugin='" : "", self->data.plugin,
521 *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
522 *self->data.host ? "',host='" : "", self->data.host,
523 (long unsigned) self->data.time, self->interval,
524 valuestring ? cpy_unicode_or_bytes_to_string(valuestring) : "[]");
525 Py_XDECREF(valuestring);
526 return ret;
527 }*/
529 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
530 Values *v = (Values *) self;
531 Py_VISIT(v->values);
532 return 0;
533 }
535 static int Values_clear(PyObject *self) {
536 Values *v = (Values *) self;
537 Py_CLEAR(v->values);
538 return 0;
539 }
541 static void Values_dealloc(PyObject *self) {
542 Values_clear(self);
543 self->ob_type->tp_free(self);
544 }
546 static PyMemberDef Values_members[] = {
547 {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
548 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
549 {NULL}
550 };
552 static PyMethodDef Values_methods[] = {
553 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
554 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
555 {NULL}
556 };
558 PyTypeObject ValuesType = {
559 CPY_INIT_TYPE
560 "collectd.Values", /* tp_name */
561 sizeof(Values), /* tp_basicsize */
562 0, /* Will be filled in later */
563 Values_dealloc, /* tp_dealloc */
564 0, /* tp_print */
565 0, /* tp_getattr */
566 0, /* tp_setattr */
567 0, /* tp_compare */
568 0/*Values_repr*/, /* tp_repr */
569 0, /* tp_as_number */
570 0, /* tp_as_sequence */
571 0, /* tp_as_mapping */
572 0, /* tp_hash */
573 0, /* tp_call */
574 0, /* tp_str */
575 0, /* tp_getattro */
576 0, /* tp_setattro */
577 0, /* tp_as_buffer */
578 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
579 Values_doc, /* tp_doc */
580 Values_traverse, /* tp_traverse */
581 Values_clear, /* tp_clear */
582 0, /* tp_richcompare */
583 0, /* tp_weaklistoffset */
584 0, /* tp_iter */
585 0, /* tp_iternext */
586 Values_methods, /* tp_methods */
587 Values_members, /* tp_members */
588 0, /* tp_getset */
589 0, /* tp_base */
590 0, /* tp_dict */
591 0, /* tp_descr_get */
592 0, /* tp_descr_set */
593 0, /* tp_dictoffset */
594 Values_init, /* tp_init */
595 0, /* tp_alloc */
596 Values_new /* tp_new */
597 };
599 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
600 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
602 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
604 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
605 "It can be used to notify other plugins about bad stuff happening. It works\n"
606 "similar to Values but has a severity and a message instead of interval\n"
607 "and time.\n"
608 "Notifications can be dispatched at any time and can be received with register_notification.";
610 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
611 Notification *self = (Notification *) s;
612 PyObject *tmp;
613 int severity = 0, ret;
614 double time = 0;
615 const char *message = "";
616 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
617 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
618 "plugin", "host", "time", "severity", NULL};
620 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
621 &type, &message, &plugin_instance, &type_instance,
622 &plugin, &host, &time, &severity))
623 return -1;
625 tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
626 if (tmp == NULL)
627 return -1;
628 ret = PluginDataType.tp_init(s, tmp, NULL);
629 Py_DECREF(tmp);
630 if (ret != 0)
631 return -1;
633 sstrncpy(self->message, message, sizeof(self->message));
634 self->severity = severity;
635 return 0;
636 }
638 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
639 int ret;
640 const data_set_t *ds;
641 notification_t notification;
642 double t = self->data.time;
643 int severity = self->severity;
644 const char *host = self->data.host;
645 const char *plugin = self->data.plugin;
646 const char *plugin_instance = self->data.plugin_instance;
647 const char *type = self->data.type;
648 const char *type_instance = self->data.type_instance;
649 const char *message = self->message;
651 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
652 "plugin", "host", "time", "severity", NULL};
653 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
654 &type, &message, &plugin_instance, &type_instance,
655 &plugin, &host, &t, &severity))
656 return NULL;
658 if (type[0] == 0) {
659 PyErr_SetString(PyExc_RuntimeError, "type not set");
660 return NULL;
661 }
662 ds = plugin_get_ds(type);
663 if (ds == NULL) {
664 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
665 return NULL;
666 }
668 notification.time = t;
669 notification.severity = severity;
670 sstrncpy(notification.message, message, sizeof(notification.message));
671 sstrncpy(notification.host, host, sizeof(notification.host));
672 sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
673 sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
674 sstrncpy(notification.type, type, sizeof(notification.type));
675 sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
676 notification.meta = NULL;
677 if (notification.time < 1)
678 notification.time = time(0);
679 if (notification.host[0] == 0)
680 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
681 if (notification.plugin[0] == 0)
682 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
683 Py_BEGIN_ALLOW_THREADS;
684 ret = plugin_dispatch_notification(¬ification);
685 Py_END_ALLOW_THREADS;
686 if (ret != 0) {
687 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
688 return NULL;
689 }
690 Py_RETURN_NONE;
691 }
693 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
694 Notification *self;
696 self = (Notification *) PluginData_new(type, args, kwds);
697 if (self == NULL)
698 return NULL;
700 self->message[0] = 0;
701 self->severity = 0;
702 return (PyObject *) self;
703 }
705 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
706 char *old;
707 const char *new;
709 if (value == NULL) {
710 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
711 return -1;
712 }
713 Py_INCREF(value);
714 new = cpy_unicode_or_bytes_to_string(&value);
715 if (new == NULL) {
716 Py_DECREF(value);
717 return -1;
718 }
719 old = ((char *) self) + (intptr_t) data;
720 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
721 Py_DECREF(value);
722 return 0;
723 }
725 /*static PyObject *Notification_repr(PyObject *s) {
726 PyObject *ret;
727 Notification *self = (Notification *) s;
729 ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
730 *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
731 *self->data.plugin ? "',plugin='" : "", self->data.plugin,
732 *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
733 *self->data.host ? "',host='" : "", self->data.host,
734 *self->message ? "',message='" : "", self->message,
735 (long unsigned) self->data.time, self->severity);
736 return ret;
737 }*/
739 static PyMethodDef Notification_methods[] = {
740 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
741 {NULL}
742 };
744 static PyMemberDef Notification_members[] = {
745 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
746 {NULL}
747 };
749 static PyGetSetDef Notification_getseters[] = {
750 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
751 {NULL}
752 };
754 PyTypeObject NotificationType = {
755 CPY_INIT_TYPE
756 "collectd.Notification", /* tp_name */
757 sizeof(Notification), /* tp_basicsize */
758 0, /* Will be filled in later */
759 0, /* tp_dealloc */
760 0, /* tp_print */
761 0, /* tp_getattr */
762 0, /* tp_setattr */
763 0, /* tp_compare */
764 0/*Notification_repr*/, /* tp_repr */
765 0, /* tp_as_number */
766 0, /* tp_as_sequence */
767 0, /* tp_as_mapping */
768 0, /* tp_hash */
769 0, /* tp_call */
770 0, /* tp_str */
771 0, /* tp_getattro */
772 0, /* tp_setattro */
773 0, /* tp_as_buffer */
774 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
775 Notification_doc, /* tp_doc */
776 0, /* tp_traverse */
777 0, /* tp_clear */
778 0, /* tp_richcompare */
779 0, /* tp_weaklistoffset */
780 0, /* tp_iter */
781 0, /* tp_iternext */
782 Notification_methods, /* tp_methods */
783 Notification_members, /* tp_members */
784 Notification_getseters, /* tp_getset */
785 0, /* tp_base */
786 0, /* tp_dict */
787 0, /* tp_descr_get */
788 0, /* tp_descr_set */
789 0, /* tp_dictoffset */
790 Notification_init, /* tp_init */
791 0, /* tp_alloc */
792 Notification_new /* tp_new */
793 };