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