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 #define FreeAll() do {\
36 PyMem_Free(type);\
37 PyMem_Free(plugin_instance);\
38 PyMem_Free(type_instance);\
39 PyMem_Free(plugin);\
40 PyMem_Free(host);\
41 } while(0)
43 static PyObject *cpy_common_repr(PyObject *s) {
44 PyObject *ret, *tmp;
45 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
46 static PyObject *l_host = NULL, *l_time = NULL;
47 PluginData *self = (PluginData *) s;
49 if (l_type == NULL)
50 l_type = cpy_string_to_unicode_or_bytes("(type=");
51 if (l_type_instance == NULL)
52 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
53 if (l_plugin == NULL)
54 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
55 if (l_plugin_instance == NULL)
56 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
57 if (l_host == NULL)
58 l_host = cpy_string_to_unicode_or_bytes(",host=");
59 if (l_time == NULL)
60 l_time = cpy_string_to_unicode_or_bytes(",time=");
62 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
63 return NULL;
65 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
67 CPY_STRCAT(&ret, l_type);
68 tmp = cpy_string_to_unicode_or_bytes(self->type);
69 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
70 CPY_STRCAT_AND_DEL(&ret, tmp);
72 if (self->type_instance[0] != 0) {
73 CPY_STRCAT(&ret, l_type_instance);
74 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
75 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
76 CPY_STRCAT_AND_DEL(&ret, tmp);
77 }
79 if (self->plugin[0] != 0) {
80 CPY_STRCAT(&ret, l_plugin);
81 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
82 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
83 CPY_STRCAT_AND_DEL(&ret, tmp);
84 }
86 if (self->plugin_instance[0] != 0) {
87 CPY_STRCAT(&ret, l_plugin_instance);
88 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
89 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
90 CPY_STRCAT_AND_DEL(&ret, tmp);
91 }
93 if (self->host[0] != 0) {
94 CPY_STRCAT(&ret, l_host);
95 tmp = cpy_string_to_unicode_or_bytes(self->host);
96 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
97 CPY_STRCAT_AND_DEL(&ret, tmp);
98 }
100 if (self->time != 0) {
101 CPY_STRCAT(&ret, l_time);
102 tmp = PyFloat_FromDouble(self->time);
103 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
104 CPY_STRCAT_AND_DEL(&ret, tmp);
105 }
106 return ret;
107 }
109 static char time_doc[] = "This is the Unix timestamp of the time this value was read.\n"
110 "For dispatching values this can be set to 0 which means \"now\".\n"
111 "This means the time the value is actually dispatched, not the time\n"
112 "it was set to 0.";
114 static char host_doc[] = "The hostname of the host this value was read from.\n"
115 "For dispatching this can be set to an empty string which means\n"
116 "the local hostname as defined in collectd.conf.";
118 static char type_doc[] = "The type of this value. This type has to be defined\n"
119 "in the types.db file. Attempting to set it to any other value\n"
120 "will raise a TypeError exception.\n"
121 "Assigning a type is mandatory, calling dispatch without doing\n"
122 "so will raise a RuntimeError exception.";
124 static char type_instance_doc[] = "";
126 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
127 "member to an empty string will insert \"python\" upon dispatching.";
129 static char plugin_instance_doc[] = "";
131 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
132 "and Notification. It is pretty useless by itself and is therefore not\n"
133 "exported to the collectd module.";
135 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
136 PluginData *self;
138 self = (PluginData *) type->tp_alloc(type, 0);
139 if (self == NULL)
140 return NULL;
142 self->time = 0;
143 self->host[0] = 0;
144 self->plugin[0] = 0;
145 self->plugin_instance[0] = 0;
146 self->type[0] = 0;
147 self->type_instance[0] = 0;
148 return (PyObject *) self;
149 }
151 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
152 PluginData *self = (PluginData *) s;
153 double time = 0;
154 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
155 static char *kwlist[] = {"type", "plugin_instance", "type_instance",
156 "plugin", "host", "time", NULL};
158 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
159 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
160 return -1;
162 if (type && plugin_get_ds(type) == NULL) {
163 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
164 FreeAll();
165 return -1;
166 }
168 sstrncpy(self->host, host ? host : "", sizeof(self->host));
169 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
170 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
171 sstrncpy(self->type, type ? type : "", sizeof(self->type));
172 sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
173 self->time = time;
175 FreeAll();
177 return 0;
178 }
180 static PyObject *PluginData_repr(PyObject *s) {
181 PyObject *ret;
182 static PyObject *l_closing = NULL;
184 if (l_closing == NULL)
185 l_closing = cpy_string_to_unicode_or_bytes(")");
187 if (l_closing == NULL)
188 return NULL;
190 ret = cpy_common_repr(s);
191 CPY_STRCAT(&ret, l_closing);
192 return ret;
193 }
195 static PyMemberDef PluginData_members[] = {
196 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
197 {NULL}
198 };
200 static PyObject *PluginData_getstring(PyObject *self, void *data) {
201 const char *value = ((char *) self) + (intptr_t) data;
203 return cpy_string_to_unicode_or_bytes(value);
204 }
206 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
207 char *old;
208 const char *new;
210 if (value == NULL) {
211 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
212 return -1;
213 }
214 Py_INCREF(value);
215 new = cpy_unicode_or_bytes_to_string(&value);
216 if (new == NULL) {
217 Py_DECREF(value);
218 return -1;
219 }
220 old = ((char *) self) + (intptr_t) data;
221 sstrncpy(old, new, DATA_MAX_NAME_LEN);
222 Py_DECREF(value);
223 return 0;
224 }
226 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
227 char *old;
228 const char *new;
230 if (value == NULL) {
231 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
232 return -1;
233 }
234 Py_INCREF(value);
235 new = cpy_unicode_or_bytes_to_string(&value);
236 if (new == NULL) {
237 Py_DECREF(value);
238 return -1;
239 }
241 if (plugin_get_ds(new) == NULL) {
242 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
243 Py_DECREF(value);
244 return -1;
245 }
247 old = ((char *) self) + (intptr_t) data;
248 sstrncpy(old, new, DATA_MAX_NAME_LEN);
249 Py_DECREF(value);
250 return 0;
251 }
253 static PyGetSetDef PluginData_getseters[] = {
254 {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
255 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
256 {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
257 {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
258 {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
259 {NULL}
260 };
262 PyTypeObject PluginDataType = {
263 CPY_INIT_TYPE
264 "collectd.PluginData", /* tp_name */
265 sizeof(PluginData), /* tp_basicsize */
266 0, /* Will be filled in later */
267 0, /* tp_dealloc */
268 0, /* tp_print */
269 0, /* tp_getattr */
270 0, /* tp_setattr */
271 0, /* tp_compare */
272 PluginData_repr, /* tp_repr */
273 0, /* tp_as_number */
274 0, /* tp_as_sequence */
275 0, /* tp_as_mapping */
276 0, /* tp_hash */
277 0, /* tp_call */
278 0, /* tp_str */
279 0, /* tp_getattro */
280 0, /* tp_setattro */
281 0, /* tp_as_buffer */
282 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
283 PluginData_doc, /* tp_doc */
284 0, /* tp_traverse */
285 0, /* tp_clear */
286 0, /* tp_richcompare */
287 0, /* tp_weaklistoffset */
288 0, /* tp_iter */
289 0, /* tp_iternext */
290 0, /* tp_methods */
291 PluginData_members, /* tp_members */
292 PluginData_getseters, /* tp_getset */
293 0, /* tp_base */
294 0, /* tp_dict */
295 0, /* tp_descr_get */
296 0, /* tp_descr_set */
297 0, /* tp_dictoffset */
298 PluginData_init, /* tp_init */
299 0, /* tp_alloc */
300 PluginData_new /* tp_new */
301 };
303 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
304 "the same data source. This value has to be a positive integer, so you can't\n"
305 "submit more than one value per second. If this member is set to a\n"
306 "non-positive value, the default value as specified in the config file will\n"
307 "be used (default: 10).\n"
308 "\n"
309 "If you submit values more often than the specified interval, the average\n"
310 "will be used. If you submit less values, your graphs will have gaps.";
312 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
313 "It has to be a sequence (a tuple or list) of numbers.\n"
314 "The size of the sequence and the type of its content depend on the type\n"
315 "member in the types.db file. For more information on this read the\n"
316 "types.db man page.\n"
317 "\n"
318 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
319 "exception will be raised. If the content of the sequence is not a number,\n"
320 "a TypeError exception will be raised.";
322 static char meta_doc[] = "These are the meta data for this Value object.\n"
323 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
324 "strings. int and long objects will be dispatched as signed integers unless\n"
325 "they are between 2**63 and 2**64-1, which will result in an unsigned integer.\n"
326 "You can force one of these storage classes by using the classes\n"
327 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
328 "callback will always contain Signed or Unsigned objects.";
330 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
331 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
332 "\n"
333 "Dispatch this instance to the collectd process. The object has members\n"
334 "for each of the possible arguments for this method. For a detailed explanation\n"
335 "of these parameters see the member of the same same.\n"
336 "\n"
337 "If you do not submit a parameter the value saved in its member will be submitted.\n"
338 "If you do provide a parameter it will be used instead, without altering the member.";
340 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
341 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
342 "\n"
343 "Write this instance to a single plugin or all plugins if 'destination' is omitted.\n"
344 "This will bypass the main collectd process and all filtering and caching.\n"
345 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
346 "used instead of 'write'.\n";
348 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
350 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
351 Values *self;
353 self = (Values *) PluginData_new(type, args, kwds);
354 if (self == NULL)
355 return NULL;
357 self->values = PyList_New(0);
358 self->meta = PyDict_New();
359 self->interval = 0;
360 return (PyObject *) self;
361 }
363 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
364 Values *self = (Values *) s;
365 double interval = 0, time = 0;
366 PyObject *values = NULL, *meta = NULL, *tmp;
367 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
368 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
369 "plugin", "host", "time", "interval", "meta", NULL};
371 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
372 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
373 NULL, &plugin, NULL, &host, &time, &interval, &meta))
374 return -1;
376 if (type && plugin_get_ds(type) == NULL) {
377 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
378 FreeAll();
379 return -1;
380 }
382 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
383 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
384 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
385 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
386 sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
387 self->data.time = time;
389 FreeAll();
391 if (values == NULL) {
392 values = PyList_New(0);
393 PyErr_Clear();
394 } else {
395 Py_INCREF(values);
396 }
398 if (meta == NULL) {
399 meta = PyDict_New();
400 PyErr_Clear();
401 } else {
402 Py_INCREF(meta);
403 }
405 tmp = self->values;
406 self->values = values;
407 Py_XDECREF(tmp);
409 tmp = self->meta;
410 self->meta = meta;
411 Py_XDECREF(tmp);
413 self->interval = interval;
414 return 0;
415 }
417 static meta_data_t *cpy_build_meta(PyObject *meta) {
418 int i, s;
419 meta_data_t *m = NULL;
420 PyObject *l;
422 if ((meta == NULL) || (meta == Py_None))
423 return NULL;
425 l = PyDict_Items(meta); /* New reference. */
426 if (!l) {
427 cpy_log_exception("building meta data");
428 return NULL;
429 }
430 s = PyList_Size(l);
431 if (s <= 0) {
432 Py_XDECREF(l);
433 return NULL;
434 }
436 m = meta_data_create();
437 for (i = 0; i < s; ++i) {
438 const char *string, *keystring;
439 PyObject *key, *value, *item, *tmp;
441 item = PyList_GET_ITEM(l, i);
442 key = PyTuple_GET_ITEM(item, 0);
443 Py_INCREF(key);
444 keystring = cpy_unicode_or_bytes_to_string(&key);
445 if (!keystring) {
446 PyErr_Clear();
447 Py_XDECREF(key);
448 continue;
449 }
450 value = PyTuple_GET_ITEM(item, 1);
451 Py_INCREF(value);
452 if (value == Py_True) {
453 meta_data_add_boolean(m, keystring, 1);
454 } else if (value == Py_False) {
455 meta_data_add_boolean(m, keystring, 0);
456 } else if (PyFloat_Check(value)) {
457 meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
458 } else if (PyObject_TypeCheck(value, &SignedType)) {
459 long long int lli;
460 lli = PyLong_AsLongLong(value);
461 if (!PyErr_Occurred() && (lli == (int64_t) lli))
462 meta_data_add_signed_int(m, keystring, lli);
463 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
464 long long unsigned llu;
465 llu = PyLong_AsUnsignedLongLong(value);
466 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
467 meta_data_add_unsigned_int(m, keystring, llu);
468 } else if (PyNumber_Check(value)) {
469 long long int lli;
470 long long unsigned llu;
471 tmp = PyNumber_Long(value);
472 lli = PyLong_AsLongLong(tmp);
473 if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
474 meta_data_add_signed_int(m, keystring, lli);
475 } else {
476 PyErr_Clear();
477 llu = PyLong_AsUnsignedLongLong(tmp);
478 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
479 meta_data_add_unsigned_int(m, keystring, llu);
480 }
481 Py_XDECREF(tmp);
482 } else {
483 string = cpy_unicode_or_bytes_to_string(&value);
484 if (string) {
485 meta_data_add_string(m, keystring, string);
486 } else {
487 PyErr_Clear();
488 tmp = PyObject_Str(value);
489 string = cpy_unicode_or_bytes_to_string(&tmp);
490 if (string)
491 meta_data_add_string(m, keystring, string);
492 Py_XDECREF(tmp);
493 }
494 }
495 if (PyErr_Occurred())
496 cpy_log_exception("building meta data");
497 Py_XDECREF(value);
498 Py_DECREF(key);
499 }
500 Py_XDECREF(l);
501 return m;
502 }
504 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
505 int ret;
506 const data_set_t *ds;
507 size_t size, i;
508 value_t *value;
509 value_list_t value_list = VALUE_LIST_INIT;
510 PyObject *values = self->values, *meta = self->meta;
511 double time = self->data.time, interval = self->interval;
512 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
514 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
515 "plugin", "host", "time", "interval", "meta", NULL};
516 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
517 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
518 NULL, &plugin, NULL, &host, &time, &interval, &meta))
519 return NULL;
521 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
522 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
523 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
524 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
525 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
526 FreeAll();
527 if (value_list.type[0] == 0) {
528 PyErr_SetString(PyExc_RuntimeError, "type not set");
529 FreeAll();
530 return NULL;
531 }
532 ds = plugin_get_ds(value_list.type);
533 if (ds == NULL) {
534 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
535 return NULL;
536 }
537 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
538 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
539 return NULL;
540 }
541 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
542 PyErr_Format(PyExc_TypeError, "meta must be a dict");
543 return NULL;
544 }
545 size = (size_t) PySequence_Length(values);
546 if (size != ds->ds_num) {
547 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
548 return NULL;
549 }
550 value = calloc(size, sizeof(*value));
551 for (i = 0; i < size; ++i) {
552 PyObject *item, *num;
553 item = PySequence_Fast_GET_ITEM(values, (int) i); /* Borrowed reference. */
554 if (ds->ds->type == DS_TYPE_COUNTER) {
555 num = PyNumber_Long(item); /* New reference. */
556 if (num != NULL) {
557 value[i].counter = PyLong_AsUnsignedLongLong(num);
558 Py_XDECREF(num);
559 }
560 } else if (ds->ds->type == DS_TYPE_GAUGE) {
561 num = PyNumber_Float(item); /* New reference. */
562 if (num != NULL) {
563 value[i].gauge = PyFloat_AsDouble(num);
564 Py_XDECREF(num);
565 }
566 } else if (ds->ds->type == DS_TYPE_DERIVE) {
567 /* This might overflow without raising an exception.
568 * Not much we can do about it */
569 num = PyNumber_Long(item); /* New reference. */
570 if (num != NULL) {
571 value[i].derive = PyLong_AsLongLong(num);
572 Py_XDECREF(num);
573 }
574 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
575 /* This might overflow without raising an exception.
576 * Not much we can do about it */
577 num = PyNumber_Long(item); /* New reference. */
578 if (num != NULL) {
579 value[i].absolute = PyLong_AsUnsignedLongLong(num);
580 Py_XDECREF(num);
581 }
582 } else {
583 free(value);
584 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
585 return NULL;
586 }
587 if (PyErr_Occurred() != NULL) {
588 free(value);
589 return NULL;
590 }
591 }
592 value_list.values = value;
593 value_list.meta = cpy_build_meta(meta);
594 value_list.values_len = size;
595 value_list.time = DOUBLE_TO_CDTIME_T(time);
596 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
597 if (value_list.host[0] == 0)
598 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
599 if (value_list.plugin[0] == 0)
600 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
601 Py_BEGIN_ALLOW_THREADS;
602 ret = plugin_dispatch_values(&value_list);
603 Py_END_ALLOW_THREADS;
604 meta_data_destroy(value_list.meta);
605 free(value);
606 if (ret != 0) {
607 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
608 return NULL;
609 }
610 Py_RETURN_NONE;
611 }
613 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
614 int ret;
615 const data_set_t *ds;
616 size_t size, i;
617 value_t *value;
618 value_list_t value_list = VALUE_LIST_INIT;
619 PyObject *values = self->values, *meta = self->meta;
620 double time = self->data.time, interval = self->interval;
621 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
623 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
624 "plugin", "host", "time", "interval", "meta", NULL};
625 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
626 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
627 NULL, &plugin, NULL, &host, &time, &interval, &meta))
628 return NULL;
630 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
631 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
632 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
633 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
634 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
635 FreeAll();
636 if (value_list.type[0] == 0) {
637 PyErr_SetString(PyExc_RuntimeError, "type not set");
638 return NULL;
639 }
640 ds = plugin_get_ds(value_list.type);
641 if (ds == NULL) {
642 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
643 return NULL;
644 }
645 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
646 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
647 return NULL;
648 }
649 size = (size_t) PySequence_Length(values);
650 if (size != ds->ds_num) {
651 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
652 return NULL;
653 }
654 value = calloc(size, sizeof(*value));
655 for (i = 0; i < size; ++i) {
656 PyObject *item, *num;
657 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
658 if (ds->ds->type == DS_TYPE_COUNTER) {
659 num = PyNumber_Long(item); /* New reference. */
660 if (num != NULL) {
661 value[i].counter = PyLong_AsUnsignedLongLong(num);
662 Py_XDECREF(num);
663 }
664 } else if (ds->ds->type == DS_TYPE_GAUGE) {
665 num = PyNumber_Float(item); /* New reference. */
666 if (num != NULL) {
667 value[i].gauge = PyFloat_AsDouble(num);
668 Py_XDECREF(num);
669 }
670 } else if (ds->ds->type == DS_TYPE_DERIVE) {
671 /* This might overflow without raising an exception.
672 * Not much we can do about it */
673 num = PyNumber_Long(item); /* New reference. */
674 if (num != NULL) {
675 value[i].derive = PyLong_AsLongLong(num);
676 Py_XDECREF(num);
677 }
678 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
679 /* This might overflow without raising an exception.
680 * Not much we can do about it */
681 num = PyNumber_Long(item); /* New reference. */
682 if (num != NULL) {
683 value[i].absolute = PyLong_AsUnsignedLongLong(num);
684 Py_XDECREF(num);
685 }
686 } else {
687 free(value);
688 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
689 return NULL;
690 }
691 if (PyErr_Occurred() != NULL) {
692 free(value);
693 return NULL;
694 }
695 }
696 value_list.values = value;
697 value_list.values_len = size;
698 value_list.time = DOUBLE_TO_CDTIME_T(time);
699 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
700 value_list.meta = cpy_build_meta(meta);
701 if (value_list.host[0] == 0)
702 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
703 if (value_list.plugin[0] == 0)
704 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
705 Py_BEGIN_ALLOW_THREADS;
706 ret = plugin_write(dest, NULL, &value_list);
707 Py_END_ALLOW_THREADS;
708 meta_data_destroy(value_list.meta);
709 free(value);
710 if (ret != 0) {
711 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
712 return NULL;
713 }
714 Py_RETURN_NONE;
715 }
717 static PyObject *Values_repr(PyObject *s) {
718 PyObject *ret, *tmp;
719 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
720 Values *self = (Values *) s;
722 if (l_interval == NULL)
723 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
724 if (l_values == NULL)
725 l_values = cpy_string_to_unicode_or_bytes(",values=");
726 if (l_meta == NULL)
727 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
728 if (l_closing == NULL)
729 l_closing = cpy_string_to_unicode_or_bytes(")");
731 if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
732 return NULL;
734 ret = cpy_common_repr(s);
735 if (self->interval != 0) {
736 CPY_STRCAT(&ret, l_interval);
737 tmp = PyFloat_FromDouble(self->interval);
738 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
739 CPY_STRCAT_AND_DEL(&ret, tmp);
740 }
741 if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
742 CPY_STRCAT(&ret, l_values);
743 tmp = PyObject_Repr(self->values);
744 CPY_STRCAT_AND_DEL(&ret, tmp);
745 }
746 if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
747 CPY_STRCAT(&ret, l_meta);
748 tmp = PyObject_Repr(self->meta);
749 CPY_STRCAT_AND_DEL(&ret, tmp);
750 }
751 CPY_STRCAT(&ret, l_closing);
752 return ret;
753 }
755 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
756 Values *v = (Values *) self;
757 Py_VISIT(v->values);
758 Py_VISIT(v->meta);
759 return 0;
760 }
762 static int Values_clear(PyObject *self) {
763 Values *v = (Values *) self;
764 Py_CLEAR(v->values);
765 Py_CLEAR(v->meta);
766 return 0;
767 }
769 static void Values_dealloc(PyObject *self) {
770 Values_clear(self);
771 self->ob_type->tp_free(self);
772 }
774 static PyMemberDef Values_members[] = {
775 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
776 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
777 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
778 {NULL}
779 };
781 static PyMethodDef Values_methods[] = {
782 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
783 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
784 {NULL}
785 };
787 PyTypeObject ValuesType = {
788 CPY_INIT_TYPE
789 "collectd.Values", /* tp_name */
790 sizeof(Values), /* tp_basicsize */
791 0, /* Will be filled in later */
792 Values_dealloc, /* tp_dealloc */
793 0, /* tp_print */
794 0, /* tp_getattr */
795 0, /* tp_setattr */
796 0, /* tp_compare */
797 Values_repr, /* tp_repr */
798 0, /* tp_as_number */
799 0, /* tp_as_sequence */
800 0, /* tp_as_mapping */
801 0, /* tp_hash */
802 0, /* tp_call */
803 0, /* tp_str */
804 0, /* tp_getattro */
805 0, /* tp_setattro */
806 0, /* tp_as_buffer */
807 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
808 Values_doc, /* tp_doc */
809 Values_traverse, /* tp_traverse */
810 Values_clear, /* tp_clear */
811 0, /* tp_richcompare */
812 0, /* tp_weaklistoffset */
813 0, /* tp_iter */
814 0, /* tp_iternext */
815 Values_methods, /* tp_methods */
816 Values_members, /* tp_members */
817 0, /* tp_getset */
818 0, /* tp_base */
819 0, /* tp_dict */
820 0, /* tp_descr_get */
821 0, /* tp_descr_set */
822 0, /* tp_dictoffset */
823 Values_init, /* tp_init */
824 0, /* tp_alloc */
825 Values_new /* tp_new */
826 };
828 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
829 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
831 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
833 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
834 "It can be used to notify other plugins about bad stuff happening. It works\n"
835 "similar to Values but has a severity and a message instead of interval\n"
836 "and time.\n"
837 "Notifications can be dispatched at any time and can be received with register_notification.";
839 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
840 Notification *self = (Notification *) s;
841 int severity = 0;
842 double time = 0;
843 char *message = NULL;
844 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
845 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
846 "plugin", "host", "time", "severity", NULL};
848 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
849 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
850 NULL, &plugin, NULL, &host, &time, &severity))
851 return -1;
853 if (type && plugin_get_ds(type) == NULL) {
854 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
855 FreeAll();
856 PyMem_Free(message);
857 return -1;
858 }
860 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
861 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
862 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
863 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
864 sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
865 sstrncpy(self->message, message ? message : "", sizeof(self->message));
866 self->data.time = time;
867 self->severity = severity;
869 FreeAll();
870 PyMem_Free(message);
871 return 0;
872 }
874 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
875 int ret;
876 const data_set_t *ds;
877 notification_t notification;
878 double t = self->data.time;
879 int severity = self->severity;
880 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
881 char *message = NULL;
883 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
884 "plugin", "host", "time", "severity", NULL};
885 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
886 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
887 NULL, &plugin, NULL, &host, &t, &severity))
888 return NULL;
890 notification.time = DOUBLE_TO_CDTIME_T(t);
891 notification.severity = severity;
892 sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
893 sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
894 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
895 sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
896 sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
897 sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
898 notification.meta = NULL;
899 FreeAll();
900 PyMem_Free(message);
902 if (notification.type[0] == 0) {
903 PyErr_SetString(PyExc_RuntimeError, "type not set");
904 return NULL;
905 }
906 ds = plugin_get_ds(notification.type);
907 if (ds == NULL) {
908 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
909 return NULL;
910 }
912 if (notification.time == 0)
913 notification.time = cdtime();
914 if (notification.host[0] == 0)
915 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
916 if (notification.plugin[0] == 0)
917 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
918 Py_BEGIN_ALLOW_THREADS;
919 ret = plugin_dispatch_notification(¬ification);
920 Py_END_ALLOW_THREADS;
921 if (ret != 0) {
922 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
923 return NULL;
924 }
925 Py_RETURN_NONE;
926 }
928 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
929 Notification *self;
931 self = (Notification *) PluginData_new(type, args, kwds);
932 if (self == NULL)
933 return NULL;
935 self->message[0] = 0;
936 self->severity = 0;
937 return (PyObject *) self;
938 }
940 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
941 char *old;
942 const char *new;
944 if (value == NULL) {
945 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
946 return -1;
947 }
948 Py_INCREF(value);
949 new = cpy_unicode_or_bytes_to_string(&value);
950 if (new == NULL) {
951 Py_DECREF(value);
952 return -1;
953 }
954 old = ((char *) self) + (intptr_t) data;
955 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
956 Py_DECREF(value);
957 return 0;
958 }
960 static PyObject *Notification_repr(PyObject *s) {
961 PyObject *ret, *tmp;
962 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
963 Notification *self = (Notification *) s;
965 if (l_severity == NULL)
966 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
967 if (l_message == NULL)
968 l_message = cpy_string_to_unicode_or_bytes(",message=");
969 if (l_closing == NULL)
970 l_closing = cpy_string_to_unicode_or_bytes(")");
972 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
973 return NULL;
975 ret = cpy_common_repr(s);
976 if (self->severity != 0) {
977 CPY_STRCAT(&ret, l_severity);
978 tmp = PyInt_FromLong(self->severity);
979 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
980 CPY_STRCAT_AND_DEL(&ret, tmp);
981 }
982 if (self->message[0] != 0) {
983 CPY_STRCAT(&ret, l_message);
984 tmp = cpy_string_to_unicode_or_bytes(self->message);
985 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
986 CPY_STRCAT_AND_DEL(&ret, tmp);
987 }
988 CPY_STRCAT(&ret, l_closing);
989 return ret;
990 }
992 static PyMethodDef Notification_methods[] = {
993 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
994 {NULL}
995 };
997 static PyMemberDef Notification_members[] = {
998 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
999 {NULL}
1000 };
1002 static PyGetSetDef Notification_getseters[] = {
1003 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1004 {NULL}
1005 };
1007 PyTypeObject NotificationType = {
1008 CPY_INIT_TYPE
1009 "collectd.Notification", /* tp_name */
1010 sizeof(Notification), /* tp_basicsize */
1011 0, /* Will be filled in later */
1012 0, /* tp_dealloc */
1013 0, /* tp_print */
1014 0, /* tp_getattr */
1015 0, /* tp_setattr */
1016 0, /* tp_compare */
1017 Notification_repr, /* tp_repr */
1018 0, /* tp_as_number */
1019 0, /* tp_as_sequence */
1020 0, /* tp_as_mapping */
1021 0, /* tp_hash */
1022 0, /* tp_call */
1023 0, /* tp_str */
1024 0, /* tp_getattro */
1025 0, /* tp_setattro */
1026 0, /* tp_as_buffer */
1027 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1028 Notification_doc, /* tp_doc */
1029 0, /* tp_traverse */
1030 0, /* tp_clear */
1031 0, /* tp_richcompare */
1032 0, /* tp_weaklistoffset */
1033 0, /* tp_iter */
1034 0, /* tp_iternext */
1035 Notification_methods, /* tp_methods */
1036 Notification_members, /* tp_members */
1037 Notification_getseters, /* tp_getset */
1038 0, /* tp_base */
1039 0, /* tp_dict */
1040 0, /* tp_descr_get */
1041 0, /* tp_descr_set */
1042 0, /* tp_dictoffset */
1043 Notification_init, /* tp_init */
1044 0, /* tp_alloc */
1045 Notification_new /* tp_new */
1046 };
1048 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1049 "to choose the way it is stored in the meta data.";
1051 PyTypeObject SignedType = {
1052 CPY_INIT_TYPE
1053 "collectd.Signed", /* tp_name */
1054 sizeof(Signed), /* tp_basicsize */
1055 0, /* Will be filled in later */
1056 0, /* tp_dealloc */
1057 0, /* tp_print */
1058 0, /* tp_getattr */
1059 0, /* tp_setattr */
1060 0, /* tp_compare */
1061 0, /* tp_repr */
1062 0, /* tp_as_number */
1063 0, /* tp_as_sequence */
1064 0, /* tp_as_mapping */
1065 0, /* tp_hash */
1066 0, /* tp_call */
1067 0, /* tp_str */
1068 0, /* tp_getattro */
1069 0, /* tp_setattro */
1070 0, /* tp_as_buffer */
1071 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1072 Signed_doc /* tp_doc */
1073 };
1075 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1076 "to choose the way it is stored in the meta data.";
1078 PyTypeObject UnsignedType = {
1079 CPY_INIT_TYPE
1080 "collectd.Unsigned", /* tp_name */
1081 sizeof(Unsigned), /* tp_basicsize */
1082 0, /* Will be filled in later */
1083 0, /* tp_dealloc */
1084 0, /* tp_print */
1085 0, /* tp_getattr */
1086 0, /* tp_setattr */
1087 0, /* tp_compare */
1088 0, /* tp_repr */
1089 0, /* tp_as_number */
1090 0, /* tp_as_sequence */
1091 0, /* tp_as_mapping */
1092 0, /* tp_hash */
1093 0, /* tp_call */
1094 0, /* tp_str */
1095 0, /* tp_getattro */
1096 0, /* tp_setattro */
1097 0, /* tp_as_buffer */
1098 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1099 Unsigned_doc /* tp_doc */
1100 };