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