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, *l_type_instance, *l_plugin, *l_plugin_instance;
56 static PyObject *l_host, *l_time;
57 PluginData *self = (PluginData *)s;
60 l_type = cpy_string_to_unicode_or_bytes("(type=");
61 if (l_type_instance == NULL)
62 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
64 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
65 if (l_plugin_instance == NULL)
66 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
68 l_host = cpy_string_to_unicode_or_bytes(",host=");
70 l_time = cpy_string_to_unicode_or_bytes(",time=");
72 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance ||
76 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
78 CPY_STRCAT(&ret, l_type);
79 tmp = cpy_string_to_unicode_or_bytes(self->type);
80 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
81 CPY_STRCAT_AND_DEL(&ret, tmp);
83 if (self->type_instance[0] != 0) {
84 CPY_STRCAT(&ret, l_type_instance);
85 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
86 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
87 CPY_STRCAT_AND_DEL(&ret, tmp);
90 if (self->plugin[0] != 0) {
91 CPY_STRCAT(&ret, l_plugin);
92 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
93 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
94 CPY_STRCAT_AND_DEL(&ret, tmp);
97 if (self->plugin_instance[0] != 0) {
98 CPY_STRCAT(&ret, l_plugin_instance);
99 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
100 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
101 CPY_STRCAT_AND_DEL(&ret, tmp);
104 if (self->host[0] != 0) {
105 CPY_STRCAT(&ret, l_host);
106 tmp = cpy_string_to_unicode_or_bytes(self->host);
107 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
108 CPY_STRCAT_AND_DEL(&ret, tmp);
111 if (self->time != 0) {
112 CPY_STRCAT(&ret, l_time);
113 tmp = PyFloat_FromDouble(self->time);
114 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
115 CPY_STRCAT_AND_DEL(&ret, tmp);
120 static char time_doc[] =
121 "This is the Unix timestamp of the time this value was read.\n"
122 "For dispatching values this can be set to 0 which means \"now\".\n"
123 "This means the time the value is actually dispatched, not the time\n"
126 static char host_doc[] =
127 "The hostname of the host this value was read from.\n"
128 "For dispatching this can be set to an empty string which means\n"
129 "the local hostname as defined in collectd.conf.";
131 static char type_doc[] =
132 "The type of this value. This type has to be defined\n"
133 "in the types.db file. Attempting to set it to any other value\n"
134 "will raise a TypeError exception.\n"
135 "Assigning a type is mandatory, calling dispatch without doing\n"
136 "so will raise a RuntimeError exception.";
138 static char type_instance_doc[] = "";
140 static char plugin_doc[] =
141 "The name of the plugin that read the data. Setting this\n"
142 "member to an empty string will insert \"python\" upon dispatching.";
144 static char plugin_instance_doc[] = "";
146 static char PluginData_doc[] =
147 "This is an internal class that is the base for Values\n"
148 "and Notification. It is pretty useless by itself and is therefore not\n"
149 "exported to the collectd module.";
151 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args,
155 self = (PluginData *)type->tp_alloc(type, 0);
162 self->plugin_instance[0] = 0;
164 self->type_instance[0] = 0;
165 return (PyObject *)self;
168 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
169 PluginData *self = (PluginData *)s;
171 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
172 *plugin = NULL, *host = NULL;
173 static char *kwlist[] = {
174 "type", "plugin_instance", "type_instance", "plugin", "host", "time",
177 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL,
178 &type, NULL, &plugin_instance, NULL,
179 &type_instance, NULL, &plugin, NULL, &host,
183 if (type && plugin_get_ds(type) == NULL) {
184 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
189 sstrncpy(self->host, host ? host : "", sizeof(self->host));
190 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
191 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "",
192 sizeof(self->plugin_instance));
193 sstrncpy(self->type, type ? type : "", sizeof(self->type));
194 sstrncpy(self->type_instance, type_instance ? type_instance : "",
195 sizeof(self->type_instance));
203 static PyObject *PluginData_repr(PyObject *s) {
205 static PyObject *l_closing;
207 if (l_closing == NULL)
208 l_closing = cpy_string_to_unicode_or_bytes(")");
210 if (l_closing == NULL)
213 ret = cpy_common_repr(s);
214 CPY_STRCAT(&ret, l_closing);
218 static PyMemberDef PluginData_members[] = {
219 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc}, {NULL}};
221 static PyObject *PluginData_getstring(PyObject *self, void *data) {
222 const char *value = ((char *)self) + (intptr_t)data;
224 return cpy_string_to_unicode_or_bytes(value);
227 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
232 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
236 new = cpy_unicode_or_bytes_to_string(&value);
241 old = ((char *)self) + (intptr_t)data;
242 sstrncpy(old, new, DATA_MAX_NAME_LEN);
247 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
252 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
256 new = cpy_unicode_or_bytes_to_string(&value);
262 if (plugin_get_ds(new) == NULL) {
263 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
268 old = ((char *)self) + (intptr_t)data;
269 sstrncpy(old, new, DATA_MAX_NAME_LEN);
274 static PyGetSetDef PluginData_getseters[] = {
275 {"host", PluginData_getstring, PluginData_setstring, host_doc,
276 (void *)offsetof(PluginData, host)},
277 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc,
278 (void *)offsetof(PluginData, plugin)},
279 {"plugin_instance", PluginData_getstring, PluginData_setstring,
280 plugin_instance_doc, (void *)offsetof(PluginData, plugin_instance)},
281 {"type_instance", PluginData_getstring, PluginData_setstring,
282 type_instance_doc, (void *)offsetof(PluginData, type_instance)},
283 {"type", PluginData_getstring, PluginData_settype, type_doc,
284 (void *)offsetof(PluginData, type)},
287 PyTypeObject PluginDataType = {
288 CPY_INIT_TYPE "collectd.PluginData", /* tp_name */
289 sizeof(PluginData), /* tp_basicsize */
290 0, /* Will be filled in later */
296 PluginData_repr, /* tp_repr */
297 0, /* tp_as_number */
298 0, /* tp_as_sequence */
299 0, /* tp_as_mapping */
305 0, /* tp_as_buffer */
307 Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
308 PluginData_doc, /* tp_doc */
311 0, /* tp_richcompare */
312 0, /* tp_weaklistoffset */
316 PluginData_members, /* tp_members */
317 PluginData_getseters, /* tp_getset */
320 0, /* tp_descr_get */
321 0, /* tp_descr_set */
322 0, /* tp_dictoffset */
323 PluginData_init, /* tp_init */
325 PluginData_new /* tp_new */
328 static char interval_doc[] =
329 "The interval is the timespan in seconds between two submits for\n"
330 "the same data source. This value has to be a positive integer, so you "
332 "submit more than one value per second. If this member is set to a\n"
333 "non-positive value, the default value as specified in the config file "
335 "be used (default: 10).\n"
337 "If you submit values more often than the specified interval, the average\n"
338 "will be used. If you submit less values, your graphs will have gaps.";
340 static char values_doc[] =
341 "These are the actual values that get dispatched to collectd.\n"
342 "It has to be a sequence (a tuple or list) of numbers.\n"
343 "The size of the sequence and the type of its content depend on the type\n"
344 "member in the types.db file. For more information on this read the\n"
345 "types.db man page.\n"
347 "If the sequence does not have the correct size upon dispatch a "
349 "exception will be raised. If the content of the sequence is not a "
351 "a TypeError exception will be raised.";
353 static char meta_doc[] =
354 "These are the meta data for this Value object.\n"
355 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
356 "strings. int and long objects will be dispatched as signed integers "
358 "they are between 2**63 and 2**64-1, which will result in an unsigned "
360 "You can force one of these storage classes by using the classes\n"
361 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
362 "callback will always contain Signed or Unsigned objects.";
364 static char dispatch_doc[] =
365 "dispatch([type][, values][, plugin_instance][, type_instance]"
366 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
368 "Dispatch this instance to the collectd process. The object has members\n"
369 "for each of the possible arguments for this method. For a detailed "
371 "of these parameters see the member of the same same.\n"
373 "If you do not submit a parameter the value saved in its member will be "
375 "If you do provide a parameter it will be used instead, without altering "
378 static char write_doc[] =
379 "write([destination][, type][, values][, plugin_instance][, type_instance]"
380 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
382 "Write this instance to a single plugin or all plugins if 'destination' is "
384 "This will bypass the main collectd process and all filtering and "
386 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' "
388 "used instead of 'write'.\n";
390 static char Values_doc[] = "A Values object used for dispatching values to "
391 "collectd and receiving values from write "
394 static PyObject *Values_new(PyTypeObject *type, PyObject *args,
398 self = (Values *)PluginData_new(type, args, kwds);
402 self->values = PyList_New(0);
403 self->meta = PyDict_New();
405 return (PyObject *)self;
408 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
409 Values *self = (Values *)s;
410 double interval = 0, time = 0;
411 PyObject *values = NULL, *meta = NULL, *tmp;
412 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
413 *plugin = NULL, *host = NULL;
414 static char *kwlist[] = {
415 "type", "values", "plugin_instance", "type_instance", "plugin",
416 "host", "time", "interval", "meta", NULL};
418 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
419 &type, &values, NULL, &plugin_instance, NULL,
420 &type_instance, NULL, &plugin, NULL, &host,
421 &time, &interval, &meta))
424 if (type && plugin_get_ds(type) == NULL) {
425 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
430 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
431 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
432 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
433 sizeof(self->data.plugin_instance));
434 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
435 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
436 sizeof(self->data.type_instance));
437 self->data.time = time;
441 if (values == NULL) {
442 values = PyList_New(0);
456 self->values = values;
463 self->interval = interval;
467 static int cpy_build_meta_generic(PyObject *meta,
468 cpy_build_meta_handler_t *meta_func,
473 if ((meta == NULL) || (meta == Py_None))
476 l = PyDict_Items(meta); /* New reference. */
478 cpy_log_exception("building meta data");
487 for (int i = 0; i < s; ++i) {
488 const char *string, *keystring;
489 PyObject *key, *value, *item, *tmp;
491 item = PyList_GET_ITEM(l, i);
492 key = PyTuple_GET_ITEM(item, 0);
494 keystring = cpy_unicode_or_bytes_to_string(&key);
500 value = PyTuple_GET_ITEM(item, 1);
502 if (value == Py_True) {
503 meta_func->add_boolean(m, keystring, 1);
504 } else if (value == Py_False) {
505 meta_func->add_boolean(m, keystring, 0);
506 } else if (PyFloat_Check(value)) {
507 meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
508 } else if (PyObject_TypeCheck(value, &SignedType)) {
510 lli = PyLong_AsLongLong(value);
511 if (!PyErr_Occurred() && (lli == (int64_t)lli))
512 meta_func->add_signed_int(m, keystring, lli);
513 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
514 long long unsigned llu;
515 llu = PyLong_AsUnsignedLongLong(value);
516 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
517 meta_func->add_unsigned_int(m, keystring, llu);
518 } else if (PyNumber_Check(value)) {
520 long long unsigned llu;
521 tmp = PyNumber_Long(value);
522 lli = PyLong_AsLongLong(tmp);
523 if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
524 meta_func->add_signed_int(m, keystring, lli);
527 llu = PyLong_AsUnsignedLongLong(tmp);
528 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
529 meta_func->add_unsigned_int(m, keystring, llu);
533 string = cpy_unicode_or_bytes_to_string(&value);
535 meta_func->add_string(m, keystring, string);
538 tmp = PyObject_Str(value);
539 string = cpy_unicode_or_bytes_to_string(&tmp);
541 meta_func->add_string(m, keystring, string);
545 if (PyErr_Occurred())
546 cpy_log_exception("building meta data");
554 #define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
555 static int cpy_##func(void *meta, const char *key, val_type val) { \
556 return func((meta_type *)meta, key, val); \
559 #define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
560 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *) \
561 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t) \
562 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t) \
563 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double) \
564 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, bool) \
566 static cpy_build_meta_handler_t cpy_##func_prefix = { \
567 .add_string = cpy_##func_prefix##_add_string, \
568 .add_signed_int = cpy_##func_prefix##_add_signed_int, \
569 .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int, \
570 .add_double = cpy_##func_prefix##_add_double, \
571 .add_boolean = cpy_##func_prefix##_add_boolean}
573 CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
574 CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
576 static meta_data_t *cpy_build_meta(PyObject *meta) {
577 meta_data_t *m = meta_data_create();
578 if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
579 meta_data_destroy(m);
585 static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
586 cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
589 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
591 const data_set_t *ds;
594 value_list_t value_list = VALUE_LIST_INIT;
595 PyObject *values = self->values, *meta = self->meta;
596 double time = self->data.time, interval = self->interval;
597 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
598 *type_instance = NULL;
600 static char *kwlist[] = {
601 "type", "values", "plugin_instance", "type_instance", "plugin",
602 "host", "time", "interval", "meta", NULL};
603 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
604 &type, &values, NULL, &plugin_instance, NULL,
605 &type_instance, NULL, &plugin, NULL, &host,
606 &time, &interval, &meta))
609 sstrncpy(value_list.host, host ? host : self->data.host,
610 sizeof(value_list.host));
611 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
612 sizeof(value_list.plugin));
613 sstrncpy(value_list.plugin_instance,
614 plugin_instance ? plugin_instance : self->data.plugin_instance,
615 sizeof(value_list.plugin_instance));
616 sstrncpy(value_list.type, type ? type : self->data.type,
617 sizeof(value_list.type));
618 sstrncpy(value_list.type_instance,
619 type_instance ? type_instance : self->data.type_instance,
620 sizeof(value_list.type_instance));
622 if (value_list.type[0] == 0) {
623 PyErr_SetString(PyExc_RuntimeError, "type not set");
627 ds = plugin_get_ds(value_list.type);
629 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
632 if (values == NULL ||
633 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
634 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
637 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
638 PyErr_Format(PyExc_TypeError, "meta must be a dict");
641 size = (size_t)PySequence_Length(values);
642 if (size != ds->ds_num) {
643 PyErr_Format(PyExc_RuntimeError,
644 "type %s needs %" PRIsz " values, got %" PRIsz,
645 value_list.type, ds->ds_num, size);
648 value = calloc(size, sizeof(*value));
649 for (size_t i = 0; i < size; ++i) {
650 PyObject *item, *num;
651 item = PySequence_Fast_GET_ITEM(values, (int)i); /* Borrowed reference. */
652 switch (ds->ds[i].type) {
653 case DS_TYPE_COUNTER:
654 num = PyNumber_Long(item); /* New reference. */
656 value[i].counter = PyLong_AsUnsignedLongLong(num);
661 num = PyNumber_Float(item); /* New reference. */
663 value[i].gauge = PyFloat_AsDouble(num);
668 /* This might overflow without raising an exception.
669 * Not much we can do about it */
670 num = PyNumber_Long(item); /* New reference. */
672 value[i].derive = PyLong_AsLongLong(num);
676 case DS_TYPE_ABSOLUTE:
677 /* This might overflow without raising an exception.
678 * Not much we can do about it */
679 num = PyNumber_Long(item); /* New reference. */
681 value[i].absolute = PyLong_AsUnsignedLongLong(num);
687 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
688 ds->ds[i].type, value_list.type);
691 if (PyErr_Occurred() != NULL) {
696 value_list.values = value;
697 value_list.meta = cpy_build_meta(meta);
698 value_list.values_len = size;
699 value_list.time = DOUBLE_TO_CDTIME_T(time);
700 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
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_dispatch_values(&value_list);
707 Py_END_ALLOW_THREADS;
708 meta_data_destroy(value_list.meta);
711 PyErr_SetString(PyExc_RuntimeError,
712 "error dispatching values, read the logs");
718 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
720 const data_set_t *ds;
723 value_list_t value_list = VALUE_LIST_INIT;
724 PyObject *values = self->values, *meta = self->meta;
725 double time = self->data.time, interval = self->interval;
726 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
727 *type_instance = NULL, *dest = NULL;
729 static char *kwlist[] = {
730 "destination", "type", "values", "plugin_instance",
731 "type_instance", "plugin", "host", "time",
732 "interval", "meta", NULL};
733 if (!PyArg_ParseTupleAndKeywords(
734 args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest, NULL, &type,
735 &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin,
736 NULL, &host, &time, &interval, &meta))
739 sstrncpy(value_list.host, host ? host : self->data.host,
740 sizeof(value_list.host));
741 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
742 sizeof(value_list.plugin));
743 sstrncpy(value_list.plugin_instance,
744 plugin_instance ? plugin_instance : self->data.plugin_instance,
745 sizeof(value_list.plugin_instance));
746 sstrncpy(value_list.type, type ? type : self->data.type,
747 sizeof(value_list.type));
748 sstrncpy(value_list.type_instance,
749 type_instance ? type_instance : self->data.type_instance,
750 sizeof(value_list.type_instance));
752 if (value_list.type[0] == 0) {
753 PyErr_SetString(PyExc_RuntimeError, "type not set");
756 ds = plugin_get_ds(value_list.type);
758 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
761 if (values == NULL ||
762 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
763 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
766 size = (size_t)PySequence_Length(values);
767 if (size != ds->ds_num) {
768 PyErr_Format(PyExc_RuntimeError,
769 "type %s needs %" PRIsz " values, got %" PRIsz,
770 value_list.type, ds->ds_num, size);
773 value = calloc(size, sizeof(*value));
774 for (size_t i = 0; i < size; ++i) {
775 PyObject *item, *num;
776 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
777 switch (ds->ds[i].type) {
778 case DS_TYPE_COUNTER:
779 num = PyNumber_Long(item); /* New reference. */
781 value[i].counter = PyLong_AsUnsignedLongLong(num);
786 num = PyNumber_Float(item); /* New reference. */
788 value[i].gauge = PyFloat_AsDouble(num);
793 /* This might overflow without raising an exception.
794 * Not much we can do about it */
795 num = PyNumber_Long(item); /* New reference. */
797 value[i].derive = PyLong_AsLongLong(num);
801 case DS_TYPE_ABSOLUTE:
802 /* This might overflow without raising an exception.
803 * Not much we can do about it */
804 num = PyNumber_Long(item); /* New reference. */
806 value[i].absolute = PyLong_AsUnsignedLongLong(num);
812 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
813 ds->ds[i].type, value_list.type);
816 if (PyErr_Occurred() != NULL) {
821 value_list.values = value;
822 value_list.values_len = size;
823 value_list.time = DOUBLE_TO_CDTIME_T(time);
824 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
825 value_list.meta = cpy_build_meta(meta);
826 if (value_list.host[0] == 0)
827 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
828 if (value_list.plugin[0] == 0)
829 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
830 Py_BEGIN_ALLOW_THREADS;
831 ret = plugin_write(dest, NULL, &value_list);
832 Py_END_ALLOW_THREADS;
833 meta_data_destroy(value_list.meta);
836 PyErr_SetString(PyExc_RuntimeError,
837 "error dispatching values, read the logs");
843 static PyObject *Values_repr(PyObject *s) {
845 static PyObject *l_interval, *l_values, *l_meta, *l_closing;
846 Values *self = (Values *)s;
848 if (l_interval == NULL)
849 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
850 if (l_values == NULL)
851 l_values = cpy_string_to_unicode_or_bytes(",values=");
853 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
854 if (l_closing == NULL)
855 l_closing = cpy_string_to_unicode_or_bytes(")");
857 if (l_interval == NULL || l_values == NULL || l_meta == NULL ||
861 ret = cpy_common_repr(s);
862 if (self->interval != 0) {
863 CPY_STRCAT(&ret, l_interval);
864 tmp = PyFloat_FromDouble(self->interval);
865 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
866 CPY_STRCAT_AND_DEL(&ret, tmp);
869 (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
870 CPY_STRCAT(&ret, l_values);
871 tmp = PyObject_Repr(self->values);
872 CPY_STRCAT_AND_DEL(&ret, tmp);
875 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
876 CPY_STRCAT(&ret, l_meta);
877 tmp = PyObject_Repr(self->meta);
878 CPY_STRCAT_AND_DEL(&ret, tmp);
880 CPY_STRCAT(&ret, l_closing);
884 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
885 Values *v = (Values *)self;
891 static int Values_clear(PyObject *self) {
892 Values *v = (Values *)self;
898 static void Values_dealloc(PyObject *self) {
900 self->ob_type->tp_free(self);
903 static PyMemberDef Values_members[] = {
904 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
905 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
906 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
909 static PyMethodDef Values_methods[] = {
910 {"dispatch", (PyCFunction)Values_dispatch, METH_VARARGS | METH_KEYWORDS,
912 {"write", (PyCFunction)Values_write, METH_VARARGS | METH_KEYWORDS,
916 PyTypeObject ValuesType = {
917 CPY_INIT_TYPE "collectd.Values", /* tp_name */
918 sizeof(Values), /* tp_basicsize */
919 0, /* Will be filled in later */
920 Values_dealloc, /* tp_dealloc */
925 Values_repr, /* tp_repr */
926 0, /* tp_as_number */
927 0, /* tp_as_sequence */
928 0, /* tp_as_mapping */
934 0, /* tp_as_buffer */
935 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
936 Values_doc, /* tp_doc */
937 Values_traverse, /* tp_traverse */
938 Values_clear, /* tp_clear */
939 0, /* tp_richcompare */
940 0, /* tp_weaklistoffset */
943 Values_methods, /* tp_methods */
944 Values_members, /* tp_members */
948 0, /* tp_descr_get */
949 0, /* tp_descr_set */
950 0, /* tp_dictoffset */
951 Values_init, /* tp_init */
953 Values_new /* tp_new */
956 static char notification_meta_doc[] =
957 "These are the meta data for the Notification object.\n"
958 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
959 "strings. int and long objects will be dispatched as signed integers "
961 "they are between 2**63 and 2**64-1, which will result in an unsigned "
963 "One of these storage classes can be forced by using the classes\n"
964 "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
965 "notification callback will always contain Signed or Unsigned objects.";
967 static char severity_doc[] =
968 "The severity of this notification. Assign or compare to\n"
969 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
971 static char message_doc[] = "Some kind of description what's going on and why "
972 "this Notification was generated.";
974 static char Notification_doc[] =
975 "The Notification class is a wrapper around the collectd notification.\n"
976 "It can be used to notify other plugins about bad stuff happening. It "
978 "similar to Values but has a severity and a message instead of interval\n"
980 "Notifications can be dispatched at any time and can be received with "
981 "register_notification.";
983 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
984 Notification *self = (Notification *)s;
987 char *message = NULL;
988 PyObject *meta = NULL;
989 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
990 *plugin = NULL, *host = NULL;
991 static char *kwlist[] = {
992 "type", "message", "plugin_instance", "type_instance", "plugin",
993 "host", "time", "severity", "meta", NULL};
995 if (!PyArg_ParseTupleAndKeywords(
996 args, kwds, "|etetetetetetdiO", kwlist, NULL, &type, NULL, &message,
997 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL,
998 &host, &time, &severity, &meta))
1001 if (type && plugin_get_ds(type) == NULL) {
1002 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
1004 PyMem_Free(message);
1008 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
1009 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
1010 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
1011 sizeof(self->data.plugin_instance));
1012 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
1013 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
1014 sizeof(self->data.type_instance));
1015 sstrncpy(self->message, message ? message : "", sizeof(self->message));
1016 self->data.time = time;
1017 self->severity = severity;
1020 PyMem_Free(message);
1023 meta = PyDict_New();
1029 PyObject *tmp = self->meta;
1036 static PyObject *Notification_dispatch(Notification *self, PyObject *args,
1039 const data_set_t *ds;
1040 notification_t notification;
1041 double t = self->data.time;
1042 PyObject *meta = self->meta;
1043 int severity = self->severity;
1044 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
1045 *type_instance = NULL;
1046 char *message = NULL;
1048 static char *kwlist[] = {
1049 "type", "message", "plugin_instance", "type_instance", "plugin",
1050 "host", "time", "severity", "meta", NULL};
1051 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
1052 &type, NULL, &message, NULL,
1053 &plugin_instance, NULL, &type_instance, NULL,
1054 &plugin, NULL, &host, &t, &severity, &meta))
1057 notification.time = DOUBLE_TO_CDTIME_T(t);
1058 notification.severity = severity;
1059 sstrncpy(notification.message, message ? message : self->message,
1060 sizeof(notification.message));
1061 sstrncpy(notification.host, host ? host : self->data.host,
1062 sizeof(notification.host));
1063 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin,
1064 sizeof(notification.plugin));
1065 sstrncpy(notification.plugin_instance,
1066 plugin_instance ? plugin_instance : self->data.plugin_instance,
1067 sizeof(notification.plugin_instance));
1068 sstrncpy(notification.type, type ? type : self->data.type,
1069 sizeof(notification.type));
1070 sstrncpy(notification.type_instance,
1071 type_instance ? type_instance : self->data.type_instance,
1072 sizeof(notification.type_instance));
1073 notification.meta = NULL;
1075 PyMem_Free(message);
1077 if (notification.type[0] == 0) {
1078 PyErr_SetString(PyExc_RuntimeError, "type not set");
1081 ds = plugin_get_ds(notification.type);
1083 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
1086 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
1087 PyErr_Format(PyExc_TypeError, "meta must be a dict");
1090 cpy_build_notification_meta(¬ification, meta);
1092 if (notification.time == 0)
1093 notification.time = cdtime();
1094 if (notification.host[0] == 0)
1095 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
1096 if (notification.plugin[0] == 0)
1097 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
1098 Py_BEGIN_ALLOW_THREADS;
1099 ret = plugin_dispatch_notification(¬ification);
1100 if (notification.meta)
1101 plugin_notification_meta_free(notification.meta);
1102 Py_END_ALLOW_THREADS;
1104 PyErr_SetString(PyExc_RuntimeError,
1105 "error dispatching notification, read the logs");
1111 static PyObject *Notification_new(PyTypeObject *type, PyObject *args,
1115 self = (Notification *)PluginData_new(type, args, kwds);
1119 self->meta = PyDict_New();
1120 self->message[0] = 0;
1122 return (PyObject *)self;
1125 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
1129 if (value == NULL) {
1130 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
1134 new = cpy_unicode_or_bytes_to_string(&value);
1139 old = ((char *)self) + (intptr_t)data;
1140 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
1145 static PyObject *Notification_repr(PyObject *s) {
1146 PyObject *ret, *tmp;
1147 static PyObject *l_severity, *l_message, *l_meta, *l_closing;
1148 Notification *self = (Notification *)s;
1150 if (l_severity == NULL)
1151 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
1152 if (l_message == NULL)
1153 l_message = cpy_string_to_unicode_or_bytes(",message=");
1155 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
1156 if (l_closing == NULL)
1157 l_closing = cpy_string_to_unicode_or_bytes(")");
1159 if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
1163 ret = cpy_common_repr(s);
1164 if (self->severity != 0) {
1165 CPY_STRCAT(&ret, l_severity);
1166 tmp = PyInt_FromLong(self->severity);
1167 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1168 CPY_STRCAT_AND_DEL(&ret, tmp);
1170 if (self->message[0] != 0) {
1171 CPY_STRCAT(&ret, l_message);
1172 tmp = cpy_string_to_unicode_or_bytes(self->message);
1173 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1174 CPY_STRCAT_AND_DEL(&ret, tmp);
1177 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
1178 CPY_STRCAT(&ret, l_meta);
1179 tmp = PyObject_Repr(self->meta);
1180 CPY_STRCAT_AND_DEL(&ret, tmp);
1182 CPY_STRCAT(&ret, l_closing);
1186 static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
1187 Notification *n = (Notification *)self;
1192 static int Notification_clear(PyObject *self) {
1193 Notification *n = (Notification *)self;
1198 static void Notification_dealloc(PyObject *self) {
1199 Notification_clear(self);
1200 self->ob_type->tp_free(self);
1203 static PyMethodDef Notification_methods[] = {
1204 {"dispatch", (PyCFunction)Notification_dispatch,
1205 METH_VARARGS | METH_KEYWORDS, dispatch_doc},
1208 static PyMemberDef Notification_members[] = {
1209 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1210 {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
1211 notification_meta_doc},
1214 static PyGetSetDef Notification_getseters[] = {
1215 {"message", PluginData_getstring, Notification_setstring, message_doc,
1216 (void *)offsetof(Notification, message)},
1219 PyTypeObject NotificationType = {
1220 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1221 sizeof(Notification), /* tp_basicsize */
1222 0, /* Will be filled in later */
1223 Notification_dealloc, /* tp_dealloc */
1228 Notification_repr, /* tp_repr */
1229 0, /* tp_as_number */
1230 0, /* tp_as_sequence */
1231 0, /* tp_as_mapping */
1235 0, /* tp_getattro */
1236 0, /* tp_setattro */
1237 0, /* tp_as_buffer */
1238 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
1239 Notification_doc, /* tp_doc */
1240 Notification_traverse, /* tp_traverse */
1241 Notification_clear, /* tp_clear */
1242 0, /* tp_richcompare */
1243 0, /* tp_weaklistoffset */
1245 0, /* tp_iternext */
1246 Notification_methods, /* tp_methods */
1247 Notification_members, /* tp_members */
1248 Notification_getseters, /* tp_getset */
1251 0, /* tp_descr_get */
1252 0, /* tp_descr_set */
1253 0, /* tp_dictoffset */
1254 Notification_init, /* tp_init */
1256 Notification_new /* tp_new */
1259 static char Signed_doc[] =
1260 "This is a long by another name. Use it in meta data dicts\n"
1261 "to choose the way it is stored in the meta data.";
1263 PyTypeObject SignedType = {
1264 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1265 sizeof(Signed), /* tp_basicsize */
1266 0, /* Will be filled in later */
1273 0, /* tp_as_number */
1274 0, /* tp_as_sequence */
1275 0, /* tp_as_mapping */
1279 0, /* tp_getattro */
1280 0, /* tp_setattro */
1281 0, /* tp_as_buffer */
1282 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1283 Signed_doc /* tp_doc */
1286 static char Unsigned_doc[] =
1287 "This is a long by another name. Use it in meta data dicts\n"
1288 "to choose the way it is stored in the meta data.";
1290 PyTypeObject UnsignedType = {
1291 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1292 sizeof(Unsigned), /* tp_basicsize */
1293 0, /* Will be filled in later */
1300 0, /* tp_as_number */
1301 0, /* tp_as_sequence */
1302 0, /* tp_as_mapping */
1306 0, /* tp_getattro */
1307 0, /* tp_setattro */
1308 0, /* tp_as_buffer */
1309 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1310 Unsigned_doc /* tp_doc */