ed0270f5d0ba8a611b45dc80f041b48c356863d4
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_STRCAT(&ret, l_type);
60 tmp = cpy_string_to_unicode_or_bytes(self->type);
61 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
62 CPY_STRCAT_AND_DEL(&ret, tmp);
64 if (self->type_instance[0] != 0) {
65 CPY_STRCAT(&ret, l_type_instance);
66 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
67 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
68 CPY_STRCAT_AND_DEL(&ret, tmp);
69 }
71 if (self->plugin[0] != 0) {
72 CPY_STRCAT(&ret, l_plugin);
73 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
74 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
75 CPY_STRCAT_AND_DEL(&ret, tmp);
76 }
78 if (self->plugin_instance[0] != 0) {
79 CPY_STRCAT(&ret, l_plugin_instance);
80 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
81 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
82 CPY_STRCAT_AND_DEL(&ret, tmp);
83 }
85 if (self->host[0] != 0) {
86 CPY_STRCAT(&ret, l_host);
87 tmp = cpy_string_to_unicode_or_bytes(self->host);
88 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
89 CPY_STRCAT_AND_DEL(&ret, tmp);
90 }
92 if (self->time != 0) {
93 CPY_STRCAT(&ret, l_time);
94 tmp = PyInt_FromLong(self->time);
95 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
96 CPY_STRCAT_AND_DEL(&ret, tmp);
97 }
98 return ret;
99 }
101 static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
102 "For dispatching values this can be set to 0 which means \"now\".\n"
103 "This means the time the value is actually dispatched, not the time\n"
104 "it was set to 0.";
106 static char host_doc[] = "The hostname of the host this value was read from.\n"
107 "For dispatching this can be set to an empty string which means\n"
108 "the local hostname as defined in the collectd.conf.";
110 static char type_doc[] = "The type of this value. This type has to be defined\n"
111 "in your types.db. Attempting to set it to any other value will\n"
112 "raise a TypeError exception.\n"
113 "Assigning a type is mandetory, calling dispatch without doing\n"
114 "so will raise a RuntimeError exception.";
116 static char type_instance_doc[] = "";
118 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
119 "member to an empty string will insert \"python\" upon dispatching.";
121 static char plugin_instance_doc[] = "";
123 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
124 "and Notification. It is pretty useless by itself and was therefore not\n"
125 "exported to the collectd module.";
127 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
128 PluginData *self;
130 self = (PluginData *) type->tp_alloc(type, 0);
131 if (self == NULL)
132 return NULL;
134 self->time = 0;
135 self->host[0] = 0;
136 self->plugin[0] = 0;
137 self->plugin_instance[0] = 0;
138 self->type[0] = 0;
139 self->type_instance[0] = 0;
140 return (PyObject *) self;
141 }
143 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
144 PluginData *self = (PluginData *) s;
145 double time = 0;
146 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
147 static char *kwlist[] = {"type", "plugin_instance", "type_instance",
148 "plugin", "host", "time", NULL};
150 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
151 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
152 return -1;
154 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
155 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
156 return -1;
157 }
159 sstrncpy(self->host, host, sizeof(self->host));
160 sstrncpy(self->plugin, plugin, sizeof(self->plugin));
161 sstrncpy(self->plugin_instance, plugin_instance, sizeof(self->plugin_instance));
162 sstrncpy(self->type, type, sizeof(self->type));
163 sstrncpy(self->type_instance, type_instance, sizeof(self->type_instance));
165 self->time = time;
166 return 0;
167 }
169 static PyObject *PluginData_repr(PyObject *s) {
170 PyObject *ret;
171 static PyObject *l_closing = NULL;
173 if (l_closing == NULL)
174 l_closing = cpy_string_to_unicode_or_bytes(")");
176 if (l_closing == NULL)
177 return NULL;
179 ret = cpy_common_repr(s);
180 CPY_STRCAT(&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 meta_doc[] = "These are the meta data for this Value object.\n"
312 "It has to be a dictionary of numbers, strings or bools.\n"
313 "If the dict contains anything else a TypeError exception will be raised.";
315 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
316 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
317 "\n"
318 "Dispatch this instance to the collectd process. The object has members\n"
319 "for each of the possible arguments for this method. For a detailed explanation\n"
320 "of these parameters see the member of the same same.\n"
321 "\n"
322 "If you do not submit a parameter the value saved in its member will be submitted.\n"
323 "If you do provide a parameter it will be used instead, without altering the member.";
325 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
326 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
327 "\n"
328 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
329 "This will bypass the main collectd process and all filtering and caching.\n"
330 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
331 "used instead of 'write'.\n";
333 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
335 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
336 Values *self;
338 self = (Values *) PluginData_new(type, args, kwds);
339 if (self == NULL)
340 return NULL;
342 self->values = PyList_New(0);
343 self->interval = 0;
344 return (PyObject *) self;
345 }
347 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
348 Values *self = (Values *) s;
349 int interval = 0;
350 double time = 0;
351 PyObject *values = NULL, *meta = NULL, *tmp;
352 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
353 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
354 "plugin", "host", "time", "interval", "meta", NULL};
356 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
357 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
358 NULL, &plugin, NULL, &host, &time, &interval, &meta))
359 return -1;
361 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
362 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
363 return -1;
364 }
366 sstrncpy(self->data.host, host, sizeof(self->data.host));
367 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
368 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
369 sstrncpy(self->data.type, type, sizeof(self->data.type));
370 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
371 self->data.time = time;
373 if (values == NULL) {
374 values = PyList_New(0);
375 PyErr_Clear();
376 } else {
377 Py_INCREF(values);
378 }
380 if (meta == NULL) {
381 meta = PyDict_New();
382 PyErr_Clear();
383 } else {
384 Py_INCREF(meta);
385 }
387 tmp = self->values;
388 self->values = values;
389 self->meta = meta;
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_meta = 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_meta == NULL)
605 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
606 if (l_closing == NULL)
607 l_closing = cpy_string_to_unicode_or_bytes(")");
609 if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
610 return NULL;
612 ret = cpy_common_repr(s);
613 if (self->interval != 0) {
614 CPY_STRCAT(&ret, l_interval);
615 tmp = PyInt_FromLong(self->interval);
616 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
617 CPY_STRCAT_AND_DEL(&ret, tmp);
618 }
619 if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
620 CPY_STRCAT(&ret, l_values);
621 tmp = PyObject_Repr(self->values);
622 CPY_STRCAT_AND_DEL(&ret, tmp);
623 }
624 if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
625 CPY_STRCAT(&ret, l_meta);
626 tmp = PyObject_Repr(self->meta);
627 CPY_STRCAT_AND_DEL(&ret, tmp);
628 }
629 CPY_STRCAT(&ret, l_closing);
630 return ret;
631 }
633 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
634 Values *v = (Values *) self;
635 Py_VISIT(v->values);
636 Py_VISIT(v->meta);
637 return 0;
638 }
640 static int Values_clear(PyObject *self) {
641 Values *v = (Values *) self;
642 Py_CLEAR(v->values);
643 Py_CLEAR(v->meta);
644 return 0;
645 }
647 static void Values_dealloc(PyObject *self) {
648 Values_clear(self);
649 self->ob_type->tp_free(self);
650 }
652 static PyMemberDef Values_members[] = {
653 {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
654 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
655 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
656 {NULL}
657 };
659 static PyMethodDef Values_methods[] = {
660 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
661 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
662 {NULL}
663 };
665 PyTypeObject ValuesType = {
666 CPY_INIT_TYPE
667 "collectd.Values", /* tp_name */
668 sizeof(Values), /* tp_basicsize */
669 0, /* Will be filled in later */
670 Values_dealloc, /* tp_dealloc */
671 0, /* tp_print */
672 0, /* tp_getattr */
673 0, /* tp_setattr */
674 0, /* tp_compare */
675 Values_repr, /* tp_repr */
676 0, /* tp_as_number */
677 0, /* tp_as_sequence */
678 0, /* tp_as_mapping */
679 0, /* tp_hash */
680 0, /* tp_call */
681 0, /* tp_str */
682 0, /* tp_getattro */
683 0, /* tp_setattro */
684 0, /* tp_as_buffer */
685 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
686 Values_doc, /* tp_doc */
687 Values_traverse, /* tp_traverse */
688 Values_clear, /* tp_clear */
689 0, /* tp_richcompare */
690 0, /* tp_weaklistoffset */
691 0, /* tp_iter */
692 0, /* tp_iternext */
693 Values_methods, /* tp_methods */
694 Values_members, /* tp_members */
695 0, /* tp_getset */
696 0, /* tp_base */
697 0, /* tp_dict */
698 0, /* tp_descr_get */
699 0, /* tp_descr_set */
700 0, /* tp_dictoffset */
701 Values_init, /* tp_init */
702 0, /* tp_alloc */
703 Values_new /* tp_new */
704 };
706 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
707 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
709 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
711 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
712 "It can be used to notify other plugins about bad stuff happening. It works\n"
713 "similar to Values but has a severity and a message instead of interval\n"
714 "and time.\n"
715 "Notifications can be dispatched at any time and can be received with register_notification.";
717 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
718 Notification *self = (Notification *) s;
719 int severity = 0;
720 double time = 0;
721 const char *message = "";
722 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
723 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
724 "plugin", "host", "time", "severity", NULL};
726 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
727 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
728 NULL, &plugin, NULL, &host, &time, &severity))
729 return -1;
731 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
732 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
733 return -1;
734 }
736 sstrncpy(self->data.host, host, sizeof(self->data.host));
737 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
738 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
739 sstrncpy(self->data.type, type, sizeof(self->data.type));
740 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
741 self->data.time = time;
743 sstrncpy(self->message, message, sizeof(self->message));
744 self->severity = severity;
745 return 0;
746 }
748 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
749 int ret;
750 const data_set_t *ds;
751 notification_t notification;
752 double t = self->data.time;
753 int severity = self->severity;
754 const char *host = self->data.host;
755 const char *plugin = self->data.plugin;
756 const char *plugin_instance = self->data.plugin_instance;
757 const char *type = self->data.type;
758 const char *type_instance = self->data.type_instance;
759 const char *message = self->message;
761 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
762 "plugin", "host", "time", "severity", NULL};
763 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
764 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
765 NULL, &plugin, NULL, &host, &t, &severity))
766 return NULL;
768 if (type[0] == 0) {
769 PyErr_SetString(PyExc_RuntimeError, "type not set");
770 return NULL;
771 }
772 ds = plugin_get_ds(type);
773 if (ds == NULL) {
774 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
775 return NULL;
776 }
778 notification.time = t;
779 notification.severity = severity;
780 sstrncpy(notification.message, message, sizeof(notification.message));
781 sstrncpy(notification.host, host, sizeof(notification.host));
782 sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
783 sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
784 sstrncpy(notification.type, type, sizeof(notification.type));
785 sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
786 notification.meta = NULL;
787 if (notification.time < 1)
788 notification.time = time(0);
789 if (notification.host[0] == 0)
790 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
791 if (notification.plugin[0] == 0)
792 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
793 Py_BEGIN_ALLOW_THREADS;
794 ret = plugin_dispatch_notification(¬ification);
795 Py_END_ALLOW_THREADS;
796 if (ret != 0) {
797 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
798 return NULL;
799 }
800 Py_RETURN_NONE;
801 }
803 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
804 Notification *self;
806 self = (Notification *) PluginData_new(type, args, kwds);
807 if (self == NULL)
808 return NULL;
810 self->message[0] = 0;
811 self->severity = 0;
812 return (PyObject *) self;
813 }
815 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
816 char *old;
817 const char *new;
819 if (value == NULL) {
820 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
821 return -1;
822 }
823 Py_INCREF(value);
824 new = cpy_unicode_or_bytes_to_string(&value);
825 if (new == NULL) {
826 Py_DECREF(value);
827 return -1;
828 }
829 old = ((char *) self) + (intptr_t) data;
830 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
831 Py_DECREF(value);
832 return 0;
833 }
835 static PyObject *Notification_repr(PyObject *s) {
836 PyObject *ret, *tmp;
837 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
838 Notification *self = (Notification *) s;
840 if (l_severity == NULL)
841 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
842 if (l_message == NULL)
843 l_message = cpy_string_to_unicode_or_bytes(",message=");
844 if (l_closing == NULL)
845 l_closing = cpy_string_to_unicode_or_bytes(")");
847 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
848 return NULL;
850 ret = cpy_common_repr(s);
851 if (self->severity != 0) {
852 CPY_STRCAT(&ret, l_severity);
853 tmp = PyInt_FromLong(self->severity);
854 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
855 CPY_STRCAT_AND_DEL(&ret, tmp);
856 }
857 if (self->message[0] != 0) {
858 CPY_STRCAT(&ret, l_message);
859 tmp = cpy_string_to_unicode_or_bytes(self->message);
860 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
861 CPY_STRCAT_AND_DEL(&ret, tmp);
862 }
863 CPY_STRCAT(&ret, l_closing);
864 return ret;
865 }
867 static PyMethodDef Notification_methods[] = {
868 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
869 {NULL}
870 };
872 static PyMemberDef Notification_members[] = {
873 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
874 {NULL}
875 };
877 static PyGetSetDef Notification_getseters[] = {
878 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
879 {NULL}
880 };
882 PyTypeObject NotificationType = {
883 CPY_INIT_TYPE
884 "collectd.Notification", /* tp_name */
885 sizeof(Notification), /* tp_basicsize */
886 0, /* Will be filled in later */
887 0, /* tp_dealloc */
888 0, /* tp_print */
889 0, /* tp_getattr */
890 0, /* tp_setattr */
891 0, /* tp_compare */
892 Notification_repr, /* tp_repr */
893 0, /* tp_as_number */
894 0, /* tp_as_sequence */
895 0, /* tp_as_mapping */
896 0, /* tp_hash */
897 0, /* tp_call */
898 0, /* tp_str */
899 0, /* tp_getattro */
900 0, /* tp_setattro */
901 0, /* tp_as_buffer */
902 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
903 Notification_doc, /* tp_doc */
904 0, /* tp_traverse */
905 0, /* tp_clear */
906 0, /* tp_richcompare */
907 0, /* tp_weaklistoffset */
908 0, /* tp_iter */
909 0, /* tp_iternext */
910 Notification_methods, /* tp_methods */
911 Notification_members, /* tp_members */
912 Notification_getseters, /* tp_getset */
913 0, /* tp_base */
914 0, /* tp_dict */
915 0, /* tp_descr_get */
916 0, /* tp_descr_set */
917 0, /* tp_dictoffset */
918 Notification_init, /* tp_init */
919 0, /* tp_alloc */
920 Notification_new /* tp_new */
921 };