2 * collectd - src/pyvalues.c
3 * Copyright (C) 2009 Sven Trenkel
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 * Sven Trenkel <collectd at semidefinite.de>
28 #include <structmember.h>
37 int (*add_string)(void *, const char *, const char *);
38 int (*add_signed_int)(void *, const char *, int64_t);
39 int (*add_unsigned_int)(void *, const char *, uint64_t);
40 int (*add_double)(void *, const char *, double);
41 int (*add_boolean)(void *, const char *, _Bool);
42 } cpy_build_meta_handler_t;
47 PyMem_Free(plugin_instance); \
48 PyMem_Free(type_instance); \
53 static PyObject *cpy_common_repr(PyObject *s) {
55 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL,
56 *l_plugin_instance = NULL;
57 static PyObject *l_host = NULL, *l_time = NULL;
58 PluginData *self = (PluginData *)s;
61 l_type = cpy_string_to_unicode_or_bytes("(type=");
62 if (l_type_instance == NULL)
63 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
65 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
66 if (l_plugin_instance == NULL)
67 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
69 l_host = cpy_string_to_unicode_or_bytes(",host=");
71 l_time = cpy_string_to_unicode_or_bytes(",time=");
73 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance ||
77 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
79 CPY_STRCAT(&ret, l_type);
80 tmp = cpy_string_to_unicode_or_bytes(self->type);
81 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
82 CPY_STRCAT_AND_DEL(&ret, tmp);
84 if (self->type_instance[0] != 0) {
85 CPY_STRCAT(&ret, l_type_instance);
86 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
87 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
88 CPY_STRCAT_AND_DEL(&ret, tmp);
91 if (self->plugin[0] != 0) {
92 CPY_STRCAT(&ret, l_plugin);
93 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
94 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
95 CPY_STRCAT_AND_DEL(&ret, tmp);
98 if (self->plugin_instance[0] != 0) {
99 CPY_STRCAT(&ret, l_plugin_instance);
100 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
101 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
102 CPY_STRCAT_AND_DEL(&ret, tmp);
105 if (self->host[0] != 0) {
106 CPY_STRCAT(&ret, l_host);
107 tmp = cpy_string_to_unicode_or_bytes(self->host);
108 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
109 CPY_STRCAT_AND_DEL(&ret, tmp);
112 if (self->time != 0) {
113 CPY_STRCAT(&ret, l_time);
114 tmp = PyFloat_FromDouble(self->time);
115 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
116 CPY_STRCAT_AND_DEL(&ret, tmp);
121 static char time_doc[] =
122 "This is the Unix timestamp of the time this value was read.\n"
123 "For dispatching values this can be set to 0 which means \"now\".\n"
124 "This means the time the value is actually dispatched, not the time\n"
127 static char host_doc[] =
128 "The hostname of the host this value was read from.\n"
129 "For dispatching this can be set to an empty string which means\n"
130 "the local hostname as defined in collectd.conf.";
132 static char type_doc[] =
133 "The type of this value. This type has to be defined\n"
134 "in the types.db file. Attempting to set it to any other value\n"
135 "will raise a TypeError exception.\n"
136 "Assigning a type is mandatory, calling dispatch without doing\n"
137 "so will raise a RuntimeError exception.";
139 static char type_instance_doc[] = "";
141 static char plugin_doc[] =
142 "The name of the plugin that read the data. Setting this\n"
143 "member to an empty string will insert \"python\" upon dispatching.";
145 static char plugin_instance_doc[] = "";
147 static char PluginData_doc[] =
148 "This is an internal class that is the base for Values\n"
149 "and Notification. It is pretty useless by itself and is therefore not\n"
150 "exported to the collectd module.";
152 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args,
156 self = (PluginData *)type->tp_alloc(type, 0);
163 self->plugin_instance[0] = 0;
165 self->type_instance[0] = 0;
166 return (PyObject *)self;
169 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
170 PluginData *self = (PluginData *)s;
172 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
173 *plugin = NULL, *host = NULL;
174 static char *kwlist[] = {
175 "type", "plugin_instance", "type_instance", "plugin", "host", "time",
178 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL,
179 &type, NULL, &plugin_instance, NULL,
180 &type_instance, NULL, &plugin, NULL, &host,
184 if (type && plugin_get_ds(type) == NULL) {
185 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
190 sstrncpy(self->host, host ? host : "", sizeof(self->host));
191 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
192 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "",
193 sizeof(self->plugin_instance));
194 sstrncpy(self->type, type ? type : "", sizeof(self->type));
195 sstrncpy(self->type_instance, type_instance ? type_instance : "",
196 sizeof(self->type_instance));
204 static PyObject *PluginData_repr(PyObject *s) {
206 static PyObject *l_closing = NULL;
208 if (l_closing == NULL)
209 l_closing = cpy_string_to_unicode_or_bytes(")");
211 if (l_closing == NULL)
214 ret = cpy_common_repr(s);
215 CPY_STRCAT(&ret, l_closing);
219 static PyMemberDef PluginData_members[] = {
220 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc}, {NULL}};
222 static PyObject *PluginData_getstring(PyObject *self, void *data) {
223 const char *value = ((char *)self) + (intptr_t)data;
225 return cpy_string_to_unicode_or_bytes(value);
228 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
233 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
237 new = cpy_unicode_or_bytes_to_string(&value);
242 old = ((char *)self) + (intptr_t)data;
243 sstrncpy(old, new, DATA_MAX_NAME_LEN);
248 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
253 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
257 new = cpy_unicode_or_bytes_to_string(&value);
263 if (plugin_get_ds(new) == NULL) {
264 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
269 old = ((char *)self) + (intptr_t)data;
270 sstrncpy(old, new, DATA_MAX_NAME_LEN);
275 static PyGetSetDef PluginData_getseters[] = {
276 {"host", PluginData_getstring, PluginData_setstring, host_doc,
277 (void *)offsetof(PluginData, host)},
278 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc,
279 (void *)offsetof(PluginData, plugin)},
280 {"plugin_instance", PluginData_getstring, PluginData_setstring,
281 plugin_instance_doc, (void *)offsetof(PluginData, plugin_instance)},
282 {"type_instance", PluginData_getstring, PluginData_setstring,
283 type_instance_doc, (void *)offsetof(PluginData, type_instance)},
284 {"type", PluginData_getstring, PluginData_settype, type_doc,
285 (void *)offsetof(PluginData, type)},
288 PyTypeObject PluginDataType = {
289 CPY_INIT_TYPE "collectd.PluginData", /* tp_name */
290 sizeof(PluginData), /* tp_basicsize */
291 0, /* Will be filled in later */
297 PluginData_repr, /* tp_repr */
298 0, /* tp_as_number */
299 0, /* tp_as_sequence */
300 0, /* tp_as_mapping */
306 0, /* tp_as_buffer */
308 Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
309 PluginData_doc, /* tp_doc */
312 0, /* tp_richcompare */
313 0, /* tp_weaklistoffset */
317 PluginData_members, /* tp_members */
318 PluginData_getseters, /* tp_getset */
321 0, /* tp_descr_get */
322 0, /* tp_descr_set */
323 0, /* tp_dictoffset */
324 PluginData_init, /* tp_init */
326 PluginData_new /* tp_new */
329 static char interval_doc[] =
330 "The interval is the timespan in seconds between two submits for\n"
331 "the same data source. This value has to be a positive integer, so you "
333 "submit more than one value per second. If this member is set to a\n"
334 "non-positive value, the default value as specified in the config file "
336 "be used (default: 10).\n"
338 "If you submit values more often than the specified interval, the average\n"
339 "will be used. If you submit less values, your graphs will have gaps.";
341 static char values_doc[] =
342 "These are the actual values that get dispatched to collectd.\n"
343 "It has to be a sequence (a tuple or list) of numbers.\n"
344 "The size of the sequence and the type of its content depend on the type\n"
345 "member in the types.db file. For more information on this read the\n"
346 "types.db man page.\n"
348 "If the sequence does not have the correct size upon dispatch a "
350 "exception will be raised. If the content of the sequence is not a "
352 "a TypeError exception will be raised.";
354 static char meta_doc[] =
355 "These are the meta data for this Value object.\n"
356 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
357 "strings. int and long objects will be dispatched as signed integers "
359 "they are between 2**63 and 2**64-1, which will result in an unsigned "
361 "You can force one of these storage classes by using the classes\n"
362 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
363 "callback will always contain Signed or Unsigned objects.";
365 static char dispatch_doc[] =
366 "dispatch([type][, values][, plugin_instance][, type_instance]"
367 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
369 "Dispatch this instance to the collectd process. The object has members\n"
370 "for each of the possible arguments for this method. For a detailed "
372 "of these parameters see the member of the same same.\n"
374 "If you do not submit a parameter the value saved in its member will be "
376 "If you do provide a parameter it will be used instead, without altering "
379 static char write_doc[] =
380 "write([destination][, type][, values][, plugin_instance][, type_instance]"
381 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
383 "Write this instance to a single plugin or all plugins if 'destination' is "
385 "This will bypass the main collectd process and all filtering and "
387 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' "
389 "used instead of 'write'.\n";
391 static char Values_doc[] = "A Values object used for dispatching values to "
392 "collectd and receiving values from write "
395 static PyObject *Values_new(PyTypeObject *type, PyObject *args,
399 self = (Values *)PluginData_new(type, args, kwds);
403 self->values = PyList_New(0);
404 self->meta = PyDict_New();
406 return (PyObject *)self;
409 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
410 Values *self = (Values *)s;
411 double interval = 0, time = 0;
412 PyObject *values = NULL, *meta = NULL, *tmp;
413 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
414 *plugin = NULL, *host = NULL;
415 static char *kwlist[] = {
416 "type", "values", "plugin_instance", "type_instance", "plugin",
417 "host", "time", "interval", "meta", NULL};
419 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
420 &type, &values, NULL, &plugin_instance, NULL,
421 &type_instance, NULL, &plugin, NULL, &host,
422 &time, &interval, &meta))
425 if (type && plugin_get_ds(type) == NULL) {
426 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
431 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
432 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
433 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
434 sizeof(self->data.plugin_instance));
435 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
436 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
437 sizeof(self->data.type_instance));
438 self->data.time = time;
442 if (values == NULL) {
443 values = PyList_New(0);
457 self->values = values;
464 self->interval = interval;
468 static int cpy_build_meta_generic(PyObject *meta,
469 cpy_build_meta_handler_t *meta_func,
474 if ((meta == NULL) || (meta == Py_None))
477 l = PyDict_Items(meta); /* New reference. */
479 cpy_log_exception("building meta data");
488 for (int i = 0; i < s; ++i) {
489 const char *string, *keystring;
490 PyObject *key, *value, *item, *tmp;
492 item = PyList_GET_ITEM(l, i);
493 key = PyTuple_GET_ITEM(item, 0);
495 keystring = cpy_unicode_or_bytes_to_string(&key);
501 value = PyTuple_GET_ITEM(item, 1);
503 if (value == Py_True) {
504 meta_func->add_boolean(m, keystring, 1);
505 } else if (value == Py_False) {
506 meta_func->add_boolean(m, keystring, 0);
507 } else if (PyFloat_Check(value)) {
508 meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
509 } else if (PyObject_TypeCheck(value, &SignedType)) {
511 lli = PyLong_AsLongLong(value);
512 if (!PyErr_Occurred() && (lli == (int64_t)lli))
513 meta_func->add_signed_int(m, keystring, lli);
514 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
515 long long unsigned llu;
516 llu = PyLong_AsUnsignedLongLong(value);
517 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
518 meta_func->add_unsigned_int(m, keystring, llu);
519 } else if (PyNumber_Check(value)) {
521 long long unsigned llu;
522 tmp = PyNumber_Long(value);
523 lli = PyLong_AsLongLong(tmp);
524 if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
525 meta_func->add_signed_int(m, keystring, lli);
528 llu = PyLong_AsUnsignedLongLong(tmp);
529 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
530 meta_func->add_unsigned_int(m, keystring, llu);
534 string = cpy_unicode_or_bytes_to_string(&value);
536 meta_func->add_string(m, keystring, string);
539 tmp = PyObject_Str(value);
540 string = cpy_unicode_or_bytes_to_string(&tmp);
542 meta_func->add_string(m, keystring, string);
546 if (PyErr_Occurred())
547 cpy_log_exception("building meta data");
555 #define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
556 static int cpy_##func(void *meta, const char *key, val_type val) { \
557 return func((meta_type *)meta, key, val); \
560 #define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
561 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *) \
562 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t) \
563 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t) \
564 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double) \
565 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool) \
567 static cpy_build_meta_handler_t cpy_##func_prefix = { \
568 .add_string = cpy_##func_prefix##_add_string, \
569 .add_signed_int = cpy_##func_prefix##_add_signed_int, \
570 .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int, \
571 .add_double = cpy_##func_prefix##_add_double, \
572 .add_boolean = cpy_##func_prefix##_add_boolean}
574 CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
575 CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
577 static meta_data_t *cpy_build_meta(PyObject *meta) {
578 meta_data_t *m = meta_data_create();
579 if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
580 meta_data_destroy(m);
586 static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
587 cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
590 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
592 const data_set_t *ds;
595 value_list_t value_list = VALUE_LIST_INIT;
596 PyObject *values = self->values, *meta = self->meta;
597 double time = self->data.time, interval = self->interval;
598 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
599 *type_instance = NULL;
601 static char *kwlist[] = {
602 "type", "values", "plugin_instance", "type_instance", "plugin",
603 "host", "time", "interval", "meta", NULL};
604 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
605 &type, &values, NULL, &plugin_instance, NULL,
606 &type_instance, NULL, &plugin, NULL, &host,
607 &time, &interval, &meta))
610 sstrncpy(value_list.host, host ? host : self->data.host,
611 sizeof(value_list.host));
612 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
613 sizeof(value_list.plugin));
614 sstrncpy(value_list.plugin_instance,
615 plugin_instance ? plugin_instance : self->data.plugin_instance,
616 sizeof(value_list.plugin_instance));
617 sstrncpy(value_list.type, type ? type : self->data.type,
618 sizeof(value_list.type));
619 sstrncpy(value_list.type_instance,
620 type_instance ? type_instance : self->data.type_instance,
621 sizeof(value_list.type_instance));
623 if (value_list.type[0] == 0) {
624 PyErr_SetString(PyExc_RuntimeError, "type not set");
628 ds = plugin_get_ds(value_list.type);
630 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
633 if (values == NULL ||
634 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
635 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
638 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
639 PyErr_Format(PyExc_TypeError, "meta must be a dict");
642 size = (size_t)PySequence_Length(values);
643 if (size != ds->ds_num) {
644 PyErr_Format(PyExc_RuntimeError,
645 "type %s needs %" PRIsz " values, got %" PRIsz,
646 value_list.type, ds->ds_num, size);
649 value = calloc(size, sizeof(*value));
650 for (size_t i = 0; i < size; ++i) {
651 PyObject *item, *num;
652 item = PySequence_Fast_GET_ITEM(values, (int)i); /* Borrowed reference. */
653 switch (ds->ds[i].type) {
654 case DS_TYPE_COUNTER:
655 num = PyNumber_Long(item); /* New reference. */
657 value[i].counter = PyLong_AsUnsignedLongLong(num);
662 num = PyNumber_Float(item); /* New reference. */
664 value[i].gauge = PyFloat_AsDouble(num);
669 /* This might overflow without raising an exception.
670 * Not much we can do about it */
671 num = PyNumber_Long(item); /* New reference. */
673 value[i].derive = PyLong_AsLongLong(num);
677 case DS_TYPE_ABSOLUTE:
678 /* This might overflow without raising an exception.
679 * Not much we can do about it */
680 num = PyNumber_Long(item); /* New reference. */
682 value[i].absolute = PyLong_AsUnsignedLongLong(num);
688 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
689 ds->ds[i].type, value_list.type);
692 if (PyErr_Occurred() != NULL) {
697 value_list.values = value;
698 value_list.meta = cpy_build_meta(meta);
699 value_list.values_len = size;
700 value_list.time = DOUBLE_TO_CDTIME_T(time);
701 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
702 if (value_list.host[0] == 0)
703 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
704 if (value_list.plugin[0] == 0)
705 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
706 Py_BEGIN_ALLOW_THREADS;
707 ret = plugin_dispatch_values(&value_list);
708 Py_END_ALLOW_THREADS;
709 meta_data_destroy(value_list.meta);
712 PyErr_SetString(PyExc_RuntimeError,
713 "error dispatching values, read the logs");
719 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
721 const data_set_t *ds;
724 value_list_t value_list = VALUE_LIST_INIT;
725 PyObject *values = self->values, *meta = self->meta;
726 double time = self->data.time, interval = self->interval;
727 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
728 *type_instance = NULL, *dest = NULL;
730 static char *kwlist[] = {
731 "destination", "type", "values", "plugin_instance",
732 "type_instance", "plugin", "host", "time",
733 "interval", "meta", NULL};
734 if (!PyArg_ParseTupleAndKeywords(
735 args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest, NULL, &type,
736 &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin,
737 NULL, &host, &time, &interval, &meta))
740 sstrncpy(value_list.host, host ? host : self->data.host,
741 sizeof(value_list.host));
742 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
743 sizeof(value_list.plugin));
744 sstrncpy(value_list.plugin_instance,
745 plugin_instance ? plugin_instance : self->data.plugin_instance,
746 sizeof(value_list.plugin_instance));
747 sstrncpy(value_list.type, type ? type : self->data.type,
748 sizeof(value_list.type));
749 sstrncpy(value_list.type_instance,
750 type_instance ? type_instance : self->data.type_instance,
751 sizeof(value_list.type_instance));
753 if (value_list.type[0] == 0) {
754 PyErr_SetString(PyExc_RuntimeError, "type not set");
757 ds = plugin_get_ds(value_list.type);
759 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
762 if (values == NULL ||
763 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
764 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
767 size = (size_t)PySequence_Length(values);
768 if (size != ds->ds_num) {
769 PyErr_Format(PyExc_RuntimeError,
770 "type %s needs %" PRIsz " values, got %" PRIsz,
771 value_list.type, ds->ds_num, size);
774 value = calloc(size, sizeof(*value));
775 for (size_t i = 0; i < size; ++i) {
776 PyObject *item, *num;
777 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
778 switch (ds->ds[i].type) {
779 case DS_TYPE_COUNTER:
780 num = PyNumber_Long(item); /* New reference. */
782 value[i].counter = PyLong_AsUnsignedLongLong(num);
787 num = PyNumber_Float(item); /* New reference. */
789 value[i].gauge = PyFloat_AsDouble(num);
794 /* This might overflow without raising an exception.
795 * Not much we can do about it */
796 num = PyNumber_Long(item); /* New reference. */
798 value[i].derive = PyLong_AsLongLong(num);
802 case DS_TYPE_ABSOLUTE:
803 /* This might overflow without raising an exception.
804 * Not much we can do about it */
805 num = PyNumber_Long(item); /* New reference. */
807 value[i].absolute = PyLong_AsUnsignedLongLong(num);
813 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
814 ds->ds[i].type, value_list.type);
817 if (PyErr_Occurred() != NULL) {
822 value_list.values = value;
823 value_list.values_len = size;
824 value_list.time = DOUBLE_TO_CDTIME_T(time);
825 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
826 value_list.meta = cpy_build_meta(meta);
827 if (value_list.host[0] == 0)
828 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
829 if (value_list.plugin[0] == 0)
830 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
831 Py_BEGIN_ALLOW_THREADS;
832 ret = plugin_write(dest, NULL, &value_list);
833 Py_END_ALLOW_THREADS;
834 meta_data_destroy(value_list.meta);
837 PyErr_SetString(PyExc_RuntimeError,
838 "error dispatching values, read the logs");
844 static PyObject *Values_repr(PyObject *s) {
846 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL,
848 Values *self = (Values *)s;
850 if (l_interval == NULL)
851 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
852 if (l_values == NULL)
853 l_values = cpy_string_to_unicode_or_bytes(",values=");
855 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
856 if (l_closing == NULL)
857 l_closing = cpy_string_to_unicode_or_bytes(")");
859 if (l_interval == NULL || l_values == NULL || l_meta == NULL ||
863 ret = cpy_common_repr(s);
864 if (self->interval != 0) {
865 CPY_STRCAT(&ret, l_interval);
866 tmp = PyFloat_FromDouble(self->interval);
867 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
868 CPY_STRCAT_AND_DEL(&ret, tmp);
871 (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
872 CPY_STRCAT(&ret, l_values);
873 tmp = PyObject_Repr(self->values);
874 CPY_STRCAT_AND_DEL(&ret, tmp);
877 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
878 CPY_STRCAT(&ret, l_meta);
879 tmp = PyObject_Repr(self->meta);
880 CPY_STRCAT_AND_DEL(&ret, tmp);
882 CPY_STRCAT(&ret, l_closing);
886 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
887 Values *v = (Values *)self;
893 static int Values_clear(PyObject *self) {
894 Values *v = (Values *)self;
900 static void Values_dealloc(PyObject *self) {
902 self->ob_type->tp_free(self);
905 static PyMemberDef Values_members[] = {
906 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
907 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
908 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
911 static PyMethodDef Values_methods[] = {
912 {"dispatch", (PyCFunction)Values_dispatch, METH_VARARGS | METH_KEYWORDS,
914 {"write", (PyCFunction)Values_write, METH_VARARGS | METH_KEYWORDS,
918 PyTypeObject ValuesType = {
919 CPY_INIT_TYPE "collectd.Values", /* tp_name */
920 sizeof(Values), /* tp_basicsize */
921 0, /* Will be filled in later */
922 Values_dealloc, /* tp_dealloc */
927 Values_repr, /* tp_repr */
928 0, /* tp_as_number */
929 0, /* tp_as_sequence */
930 0, /* tp_as_mapping */
936 0, /* tp_as_buffer */
937 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
938 Values_doc, /* tp_doc */
939 Values_traverse, /* tp_traverse */
940 Values_clear, /* tp_clear */
941 0, /* tp_richcompare */
942 0, /* tp_weaklistoffset */
945 Values_methods, /* tp_methods */
946 Values_members, /* tp_members */
950 0, /* tp_descr_get */
951 0, /* tp_descr_set */
952 0, /* tp_dictoffset */
953 Values_init, /* tp_init */
955 Values_new /* tp_new */
958 static char notification_meta_doc[] =
959 "These are the meta data for the Notification object.\n"
960 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
961 "strings. int and long objects will be dispatched as signed integers "
963 "they are between 2**63 and 2**64-1, which will result in an unsigned "
965 "One of these storage classes can be forced by using the classes\n"
966 "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
967 "notification callback will always contain Signed or Unsigned objects.";
969 static char severity_doc[] =
970 "The severity of this notification. Assign or compare to\n"
971 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
973 static char message_doc[] = "Some kind of description what's going on and why "
974 "this Notification was generated.";
976 static char Notification_doc[] =
977 "The Notification class is a wrapper around the collectd notification.\n"
978 "It can be used to notify other plugins about bad stuff happening. It "
980 "similar to Values but has a severity and a message instead of interval\n"
982 "Notifications can be dispatched at any time and can be received with "
983 "register_notification.";
985 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
986 Notification *self = (Notification *)s;
989 char *message = NULL;
990 PyObject *meta = NULL;
991 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
992 *plugin = NULL, *host = NULL;
993 static char *kwlist[] = {
994 "type", "message", "plugin_instance", "type_instance", "plugin",
995 "host", "time", "severity", "meta", NULL};
997 if (!PyArg_ParseTupleAndKeywords(
998 args, kwds, "|etetetetetetdiO", kwlist, NULL, &type, NULL, &message,
999 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL,
1000 &host, &time, &severity, &meta))
1003 if (type && plugin_get_ds(type) == NULL) {
1004 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
1006 PyMem_Free(message);
1010 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
1011 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
1012 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
1013 sizeof(self->data.plugin_instance));
1014 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
1015 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
1016 sizeof(self->data.type_instance));
1017 sstrncpy(self->message, message ? message : "", sizeof(self->message));
1018 self->data.time = time;
1019 self->severity = severity;
1022 PyMem_Free(message);
1025 meta = PyDict_New();
1031 PyObject *tmp = self->meta;
1038 static PyObject *Notification_dispatch(Notification *self, PyObject *args,
1041 const data_set_t *ds;
1042 notification_t notification;
1043 double t = self->data.time;
1044 PyObject *meta = self->meta;
1045 int severity = self->severity;
1046 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
1047 *type_instance = NULL;
1048 char *message = NULL;
1050 static char *kwlist[] = {
1051 "type", "message", "plugin_instance", "type_instance", "plugin",
1052 "host", "time", "severity", "meta", NULL};
1053 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
1054 &type, NULL, &message, NULL,
1055 &plugin_instance, NULL, &type_instance, NULL,
1056 &plugin, NULL, &host, &t, &severity, &meta))
1059 notification.time = DOUBLE_TO_CDTIME_T(t);
1060 notification.severity = severity;
1061 sstrncpy(notification.message, message ? message : self->message,
1062 sizeof(notification.message));
1063 sstrncpy(notification.host, host ? host : self->data.host,
1064 sizeof(notification.host));
1065 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin,
1066 sizeof(notification.plugin));
1067 sstrncpy(notification.plugin_instance,
1068 plugin_instance ? plugin_instance : self->data.plugin_instance,
1069 sizeof(notification.plugin_instance));
1070 sstrncpy(notification.type, type ? type : self->data.type,
1071 sizeof(notification.type));
1072 sstrncpy(notification.type_instance,
1073 type_instance ? type_instance : self->data.type_instance,
1074 sizeof(notification.type_instance));
1075 notification.meta = NULL;
1077 PyMem_Free(message);
1079 if (notification.type[0] == 0) {
1080 PyErr_SetString(PyExc_RuntimeError, "type not set");
1083 ds = plugin_get_ds(notification.type);
1085 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
1088 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
1089 PyErr_Format(PyExc_TypeError, "meta must be a dict");
1092 cpy_build_notification_meta(¬ification, meta);
1094 if (notification.time == 0)
1095 notification.time = cdtime();
1096 if (notification.host[0] == 0)
1097 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
1098 if (notification.plugin[0] == 0)
1099 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
1100 Py_BEGIN_ALLOW_THREADS;
1101 ret = plugin_dispatch_notification(¬ification);
1102 if (notification.meta)
1103 plugin_notification_meta_free(notification.meta);
1104 Py_END_ALLOW_THREADS;
1106 PyErr_SetString(PyExc_RuntimeError,
1107 "error dispatching notification, read the logs");
1113 static PyObject *Notification_new(PyTypeObject *type, PyObject *args,
1117 self = (Notification *)PluginData_new(type, args, kwds);
1121 self->meta = PyDict_New();
1122 self->message[0] = 0;
1124 return (PyObject *)self;
1127 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
1131 if (value == NULL) {
1132 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
1136 new = cpy_unicode_or_bytes_to_string(&value);
1141 old = ((char *)self) + (intptr_t)data;
1142 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
1147 static PyObject *Notification_repr(PyObject *s) {
1148 PyObject *ret, *tmp;
1149 static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
1151 Notification *self = (Notification *)s;
1153 if (l_severity == NULL)
1154 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
1155 if (l_message == NULL)
1156 l_message = cpy_string_to_unicode_or_bytes(",message=");
1158 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
1159 if (l_closing == NULL)
1160 l_closing = cpy_string_to_unicode_or_bytes(")");
1162 if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
1166 ret = cpy_common_repr(s);
1167 if (self->severity != 0) {
1168 CPY_STRCAT(&ret, l_severity);
1169 tmp = PyInt_FromLong(self->severity);
1170 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1171 CPY_STRCAT_AND_DEL(&ret, tmp);
1173 if (self->message[0] != 0) {
1174 CPY_STRCAT(&ret, l_message);
1175 tmp = cpy_string_to_unicode_or_bytes(self->message);
1176 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1177 CPY_STRCAT_AND_DEL(&ret, tmp);
1180 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
1181 CPY_STRCAT(&ret, l_meta);
1182 tmp = PyObject_Repr(self->meta);
1183 CPY_STRCAT_AND_DEL(&ret, tmp);
1185 CPY_STRCAT(&ret, l_closing);
1189 static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
1190 Notification *n = (Notification *)self;
1195 static int Notification_clear(PyObject *self) {
1196 Notification *n = (Notification *)self;
1201 static void Notification_dealloc(PyObject *self) {
1202 Notification_clear(self);
1203 self->ob_type->tp_free(self);
1206 static PyMethodDef Notification_methods[] = {
1207 {"dispatch", (PyCFunction)Notification_dispatch,
1208 METH_VARARGS | METH_KEYWORDS, dispatch_doc},
1211 static PyMemberDef Notification_members[] = {
1212 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1213 {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
1214 notification_meta_doc},
1217 static PyGetSetDef Notification_getseters[] = {
1218 {"message", PluginData_getstring, Notification_setstring, message_doc,
1219 (void *)offsetof(Notification, message)},
1222 PyTypeObject NotificationType = {
1223 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1224 sizeof(Notification), /* tp_basicsize */
1225 0, /* Will be filled in later */
1226 Notification_dealloc, /* tp_dealloc */
1231 Notification_repr, /* tp_repr */
1232 0, /* tp_as_number */
1233 0, /* tp_as_sequence */
1234 0, /* tp_as_mapping */
1238 0, /* tp_getattro */
1239 0, /* tp_setattro */
1240 0, /* tp_as_buffer */
1241 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
1242 Notification_doc, /* tp_doc */
1243 Notification_traverse, /* tp_traverse */
1244 Notification_clear, /* tp_clear */
1245 0, /* tp_richcompare */
1246 0, /* tp_weaklistoffset */
1248 0, /* tp_iternext */
1249 Notification_methods, /* tp_methods */
1250 Notification_members, /* tp_members */
1251 Notification_getseters, /* tp_getset */
1254 0, /* tp_descr_get */
1255 0, /* tp_descr_set */
1256 0, /* tp_dictoffset */
1257 Notification_init, /* tp_init */
1259 Notification_new /* tp_new */
1262 static char Signed_doc[] =
1263 "This is a long by another name. Use it in meta data dicts\n"
1264 "to choose the way it is stored in the meta data.";
1266 PyTypeObject SignedType = {
1267 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1268 sizeof(Signed), /* tp_basicsize */
1269 0, /* Will be filled in later */
1276 0, /* tp_as_number */
1277 0, /* tp_as_sequence */
1278 0, /* tp_as_mapping */
1282 0, /* tp_getattro */
1283 0, /* tp_setattro */
1284 0, /* tp_as_buffer */
1285 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1286 Signed_doc /* tp_doc */
1289 static char Unsigned_doc[] =
1290 "This is a long by another name. Use it in meta data dicts\n"
1291 "to choose the way it is stored in the meta data.";
1293 PyTypeObject UnsignedType = {
1294 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1295 sizeof(Unsigned), /* tp_basicsize */
1296 0, /* Will be filled in later */
1303 0, /* tp_as_number */
1304 0, /* tp_as_sequence */
1305 0, /* tp_as_mapping */
1309 0, /* tp_getattro */
1310 0, /* tp_setattro */
1311 0, /* tp_as_buffer */
1312 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1313 Unsigned_doc /* tp_doc */