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 PyObject *cpy_common_repr(PyObject *s) {
36 PyObject *ret, *tmp;
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;
41 if (l_type == NULL)
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=");
45 if (l_plugin == NULL)
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=");
49 if (l_host == NULL)
50 l_host = cpy_string_to_unicode_or_bytes(",host=");
51 if (l_time == NULL)
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)
55 return NULL;
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);
62 if (tmp)
63 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
64 Py_XDECREF(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);
70 if (tmp)
71 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
72 Py_XDECREF(tmp);
73 }
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);
79 if (tmp)
80 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
81 Py_XDECREF(tmp);
82 }
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);
88 if (tmp)
89 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
90 Py_XDECREF(tmp);
91 }
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);
97 if (tmp)
98 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
99 Py_XDECREF(tmp);
100 }
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);
106 if (tmp)
107 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
108 Py_XDECREF(tmp);
109 }
110 return ret;
111 }
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"
116 "it was set to 0.";
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) {
140 PluginData *self;
142 self = (PluginData *) type->tp_alloc(type, 0);
143 if (self == NULL)
144 return NULL;
146 self->time = 0;
147 self->host[0] = 0;
148 self->plugin[0] = 0;
149 self->plugin_instance[0] = 0;
150 self->type[0] = 0;
151 self->type_instance[0] = 0;
152 return (PyObject *) self;
153 }
155 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
156 PluginData *self = (PluginData *) s;
157 double time = 0;
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))
164 return -1;
166 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
167 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
168 return -1;
169 }
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));
177 self->time = time;
178 return 0;
179 }
181 static PyObject *PluginData_repr(PyObject *s) {
182 PyObject *ret;
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)
189 return NULL;
191 ret = cpy_common_repr(s);
192 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
193 return ret;
194 }
196 static PyMemberDef PluginData_members[] = {
197 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
198 {NULL}
199 };
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);
205 }
207 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
208 char *old;
209 const char *new;
211 if (value == NULL) {
212 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
213 return -1;
214 }
215 Py_INCREF(value);
216 new = cpy_unicode_or_bytes_to_string(&value);
217 if (new == NULL) {
218 Py_DECREF(value);
219 return -1;
220 }
221 old = ((char *) self) + (intptr_t) data;
222 sstrncpy(old, new, DATA_MAX_NAME_LEN);
223 Py_DECREF(value);
224 return 0;
225 }
227 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
228 char *old;
229 const char *new;
231 if (value == NULL) {
232 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
233 return -1;
234 }
235 Py_INCREF(value);
236 new = cpy_unicode_or_bytes_to_string(&value);
237 if (new == NULL) {
238 Py_DECREF(value);
239 return -1;
240 }
242 if (plugin_get_ds(new) == NULL) {
243 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
244 Py_DECREF(value);
245 return -1;
246 }
248 old = ((char *) self) + (intptr_t) data;
249 sstrncpy(old, new, DATA_MAX_NAME_LEN);
250 Py_DECREF(value);
251 return 0;
252 }
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)},
260 {NULL}
261 };
263 PyTypeObject PluginDataType = {
264 CPY_INIT_TYPE
265 "collectd.PluginData", /* tp_name */
266 sizeof(PluginData), /* tp_basicsize */
267 0, /* Will be filled in later */
268 0, /* tp_dealloc */
269 0, /* tp_print */
270 0, /* tp_getattr */
271 0, /* tp_setattr */
272 0, /* tp_compare */
273 PluginData_repr, /* tp_repr */
274 0, /* tp_as_number */
275 0, /* tp_as_sequence */
276 0, /* tp_as_mapping */
277 0, /* tp_hash */
278 0, /* tp_call */
279 0, /* tp_str */
280 0, /* tp_getattro */
281 0, /* tp_setattro */
282 0, /* tp_as_buffer */
283 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
284 PluginData_doc, /* tp_doc */
285 0, /* tp_traverse */
286 0, /* tp_clear */
287 0, /* tp_richcompare */
288 0, /* tp_weaklistoffset */
289 0, /* tp_iter */
290 0, /* tp_iternext */
291 0, /* tp_methods */
292 PluginData_members, /* tp_members */
293 PluginData_getseters, /* tp_getset */
294 0, /* tp_base */
295 0, /* tp_dict */
296 0, /* tp_descr_get */
297 0, /* tp_descr_set */
298 0, /* tp_dictoffset */
299 PluginData_init, /* tp_init */
300 0, /* tp_alloc */
301 PluginData_new /* tp_new */
302 };
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"
309 "\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"
317 "man page.\n"
318 "\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"
325 "\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"
329 "\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"
335 "\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) {
344 Values *self;
346 self = (Values *) PluginData_new(type, args, kwds);
347 if (self == NULL)
348 return NULL;
350 self->values = PyList_New(0);
351 self->interval = 0;
352 return (PyObject *) self;
353 }
355 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
356 Values *self = (Values *) s;
357 int interval = 0;
358 double time = 0;
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))
367 return -1;
369 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
370 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
371 return -1;
372 }
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);
383 PyErr_Clear();
384 } else {
385 Py_INCREF(values);
386 }
388 tmp = self->values;
389 self->values = values;
390 Py_XDECREF(tmp);
392 self->interval = interval;
393 return 0;
394 }
396 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
397 int i, ret;
398 const data_set_t *ds;
399 int size;
400 value_t *value;
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))
416 return NULL;
418 if (type[0] == 0) {
419 PyErr_SetString(PyExc_RuntimeError, "type not set");
420 return NULL;
421 }
422 ds = plugin_get_ds(type);
423 if (ds == NULL) {
424 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
425 return NULL;
426 }
427 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
428 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
429 return NULL;
430 }
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);
434 return NULL;
435 }
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);
442 if (num != NULL)
443 value[i].counter = PyLong_AsUnsignedLongLong(num);
444 } else if (ds->ds->type == DS_TYPE_GAUGE) {
445 num = PyNumber_Float(item);
446 if (num != NULL)
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);
452 if (num != NULL)
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);
458 if (num != NULL)
459 value[i].absolute = PyLong_AsUnsignedLongLong(num);
460 } else {
461 free(value);
462 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
463 return NULL;
464 }
465 if (PyErr_Occurred() != NULL) {
466 free(value);
467 return NULL;
468 }
469 }
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;
487 if (ret != 0) {
488 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
489 return NULL;
490 }
491 free(value);
492 Py_RETURN_NONE;
493 }
495 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
496 int i, ret;
497 const data_set_t *ds;
498 int size;
499 value_t *value;
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))
516 return NULL;
518 if (type[0] == 0) {
519 PyErr_SetString(PyExc_RuntimeError, "type not set");
520 return NULL;
521 }
522 ds = plugin_get_ds(type);
523 if (ds == NULL) {
524 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
525 return NULL;
526 }
527 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
528 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
529 return NULL;
530 }
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);
534 return NULL;
535 }
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);
542 if (num != NULL)
543 value[i].counter = PyLong_AsUnsignedLongLong(num);
544 } else if (ds->ds->type == DS_TYPE_GAUGE) {
545 num = PyNumber_Float(item);
546 if (num != NULL)
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);
552 if (num != NULL)
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);
558 if (num != NULL)
559 value[i].absolute = PyLong_AsUnsignedLongLong(num);
560 } else {
561 free(value);
562 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
563 return NULL;
564 }
565 if (PyErr_Occurred() != NULL) {
566 free(value);
567 return NULL;
568 }
569 }
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;
587 if (ret != 0) {
588 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
589 return NULL;
590 }
591 free(value);
592 Py_RETURN_NONE;
593 }
595 static PyObject *Values_repr(PyObject *s) {
596 PyObject *ret, *tmp;
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)
608 return 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);
615 if (tmp)
616 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
617 Py_XDECREF(tmp);
618 }
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);
622 if (tmp)
623 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
624 Py_XDECREF(tmp);
625 }
626 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
627 return ret;
628 }
630 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
631 Values *v = (Values *) self;
632 Py_VISIT(v->values);
633 return 0;
634 }
636 static int Values_clear(PyObject *self) {
637 Values *v = (Values *) self;
638 Py_CLEAR(v->values);
639 return 0;
640 }
642 static void Values_dealloc(PyObject *self) {
643 Values_clear(self);
644 self->ob_type->tp_free(self);
645 }
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},
650 {NULL}
651 };
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},
656 {NULL}
657 };
659 PyTypeObject ValuesType = {
660 CPY_INIT_TYPE
661 "collectd.Values", /* tp_name */
662 sizeof(Values), /* tp_basicsize */
663 0, /* Will be filled in later */
664 Values_dealloc, /* tp_dealloc */
665 0, /* tp_print */
666 0, /* tp_getattr */
667 0, /* tp_setattr */
668 0, /* tp_compare */
669 Values_repr, /* tp_repr */
670 0, /* tp_as_number */
671 0, /* tp_as_sequence */
672 0, /* tp_as_mapping */
673 0, /* tp_hash */
674 0, /* tp_call */
675 0, /* tp_str */
676 0, /* tp_getattro */
677 0, /* tp_setattro */
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 */
685 0, /* tp_iter */
686 0, /* tp_iternext */
687 Values_methods, /* tp_methods */
688 Values_members, /* tp_members */
689 0, /* tp_getset */
690 0, /* tp_base */
691 0, /* tp_dict */
692 0, /* tp_descr_get */
693 0, /* tp_descr_set */
694 0, /* tp_dictoffset */
695 Values_init, /* tp_init */
696 0, /* tp_alloc */
697 Values_new /* tp_new */
698 };
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"
708 "and time.\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;
713 int severity = 0;
714 double time = 0;
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))
723 return -1;
725 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
726 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
727 return -1;
728 }
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;
739 return 0;
740 }
742 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
743 int ret;
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))
760 return NULL;
762 if (type[0] == 0) {
763 PyErr_SetString(PyExc_RuntimeError, "type not set");
764 return NULL;
765 }
766 ds = plugin_get_ds(type);
767 if (ds == NULL) {
768 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
769 return NULL;
770 }
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;
790 if (ret != 0) {
791 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
792 return NULL;
793 }
794 Py_RETURN_NONE;
795 }
797 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
798 Notification *self;
800 self = (Notification *) PluginData_new(type, args, kwds);
801 if (self == NULL)
802 return NULL;
804 self->message[0] = 0;
805 self->severity = 0;
806 return (PyObject *) self;
807 }
809 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
810 char *old;
811 const char *new;
813 if (value == NULL) {
814 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
815 return -1;
816 }
817 Py_INCREF(value);
818 new = cpy_unicode_or_bytes_to_string(&value);
819 if (new == NULL) {
820 Py_DECREF(value);
821 return -1;
822 }
823 old = ((char *) self) + (intptr_t) data;
824 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
825 Py_DECREF(value);
826 return 0;
827 }
829 static PyObject *Notification_repr(PyObject *s) {
830 PyObject *ret, *tmp;
831 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
832 Notification *self = (Notification *) s;
834 if (l_severity == NULL)
835 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
836 if (l_message == NULL)
837 l_message = cpy_string_to_unicode_or_bytes(",message=");
838 if (l_closing == NULL)
839 l_closing = cpy_string_to_unicode_or_bytes(")");
841 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
842 return NULL;
844 ret = cpy_common_repr(s);
845 if (self->severity != 0) {
846 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_severity);
847 tmp = PyInt_FromLong(self->severity);
848 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
849 if (tmp)
850 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
851 Py_XDECREF(tmp);
852 }
853 if (self->message[0] != 0) {
854 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_message);
855 tmp = cpy_string_to_unicode_or_bytes(self->message);
856 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
857 if (tmp)
858 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, tmp);
859 Py_XDECREF(tmp);
860 }
861 CPY_SUBSTITUTE(CPY_STRCAT, ret, ret, l_closing);
862 return ret;
863 }
865 static PyMethodDef Notification_methods[] = {
866 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
867 {NULL}
868 };
870 static PyMemberDef Notification_members[] = {
871 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
872 {NULL}
873 };
875 static PyGetSetDef Notification_getseters[] = {
876 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
877 {NULL}
878 };
880 PyTypeObject NotificationType = {
881 CPY_INIT_TYPE
882 "collectd.Notification", /* tp_name */
883 sizeof(Notification), /* tp_basicsize */
884 0, /* Will be filled in later */
885 0, /* tp_dealloc */
886 0, /* tp_print */
887 0, /* tp_getattr */
888 0, /* tp_setattr */
889 0, /* tp_compare */
890 Notification_repr, /* tp_repr */
891 0, /* tp_as_number */
892 0, /* tp_as_sequence */
893 0, /* tp_as_mapping */
894 0, /* tp_hash */
895 0, /* tp_call */
896 0, /* tp_str */
897 0, /* tp_getattro */
898 0, /* tp_setattro */
899 0, /* tp_as_buffer */
900 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
901 Notification_doc, /* tp_doc */
902 0, /* tp_traverse */
903 0, /* tp_clear */
904 0, /* tp_richcompare */
905 0, /* tp_weaklistoffset */
906 0, /* tp_iter */
907 0, /* tp_iternext */
908 Notification_methods, /* tp_methods */
909 Notification_members, /* tp_members */
910 Notification_getseters, /* tp_getset */
911 0, /* tp_base */
912 0, /* tp_dict */
913 0, /* tp_descr_get */
914 0, /* tp_descr_set */
915 0, /* tp_dictoffset */
916 Notification_init, /* tp_init */
917 0, /* tp_alloc */
918 Notification_new /* tp_new */
919 };