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 #define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
54 static int cpy_ ## func(void *meta, const char *key, val_type val) { \
55 return func((meta_type *)meta, key, val); \
58 #define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
59 CPY_BUILD_META_FUNC(meta_type, func_prefix ## _add_string, const char *) \
60 CPY_BUILD_META_FUNC(meta_type, func_prefix ## _add_signed_int, int64_t) \
61 CPY_BUILD_META_FUNC(meta_type, func_prefix ## _add_unsigned_int, uint64_t) \
62 CPY_BUILD_META_FUNC(meta_type, func_prefix ## _add_double, double) \
63 CPY_BUILD_META_FUNC(meta_type, func_prefix ## _add_boolean, _Bool) \
65 static cpy_build_meta_handler_t cpy_ ## func_prefix = { \
66 .add_string = cpy_ ## func_prefix ## _add_string, \
67 .add_signed_int = cpy_ ## func_prefix ## _add_signed_int, \
68 .add_unsigned_int = cpy_ ## func_prefix ## _add_unsigned_int, \
69 .add_double = cpy_ ## func_prefix ## _add_double, \
70 .add_boolean = cpy_ ## func_prefix ## _add_boolean \
73 static PyObject *cpy_common_repr(PyObject *s) {
75 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL,
76 *l_plugin_instance = NULL;
77 static PyObject *l_host = NULL, *l_time = NULL;
78 PluginData *self = (PluginData *)s;
81 l_type = cpy_string_to_unicode_or_bytes("(type=");
82 if (l_type_instance == NULL)
83 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
85 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
86 if (l_plugin_instance == NULL)
87 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
89 l_host = cpy_string_to_unicode_or_bytes(",host=");
91 l_time = cpy_string_to_unicode_or_bytes(",time=");
93 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance ||
97 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
99 CPY_STRCAT(&ret, l_type);
100 tmp = cpy_string_to_unicode_or_bytes(self->type);
101 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
102 CPY_STRCAT_AND_DEL(&ret, tmp);
104 if (self->type_instance[0] != 0) {
105 CPY_STRCAT(&ret, l_type_instance);
106 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
107 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
108 CPY_STRCAT_AND_DEL(&ret, tmp);
111 if (self->plugin[0] != 0) {
112 CPY_STRCAT(&ret, l_plugin);
113 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
114 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
115 CPY_STRCAT_AND_DEL(&ret, tmp);
118 if (self->plugin_instance[0] != 0) {
119 CPY_STRCAT(&ret, l_plugin_instance);
120 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
121 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
122 CPY_STRCAT_AND_DEL(&ret, tmp);
125 if (self->host[0] != 0) {
126 CPY_STRCAT(&ret, l_host);
127 tmp = cpy_string_to_unicode_or_bytes(self->host);
128 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
129 CPY_STRCAT_AND_DEL(&ret, tmp);
132 if (self->time != 0) {
133 CPY_STRCAT(&ret, l_time);
134 tmp = PyFloat_FromDouble(self->time);
135 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
136 CPY_STRCAT_AND_DEL(&ret, tmp);
141 static char time_doc[] =
142 "This is the Unix timestamp of the time this value was read.\n"
143 "For dispatching values this can be set to 0 which means \"now\".\n"
144 "This means the time the value is actually dispatched, not the time\n"
147 static char host_doc[] =
148 "The hostname of the host this value was read from.\n"
149 "For dispatching this can be set to an empty string which means\n"
150 "the local hostname as defined in collectd.conf.";
152 static char type_doc[] =
153 "The type of this value. This type has to be defined\n"
154 "in the types.db file. Attempting to set it to any other value\n"
155 "will raise a TypeError exception.\n"
156 "Assigning a type is mandatory, calling dispatch without doing\n"
157 "so will raise a RuntimeError exception.";
159 static char type_instance_doc[] = "";
161 static char plugin_doc[] =
162 "The name of the plugin that read the data. Setting this\n"
163 "member to an empty string will insert \"python\" upon dispatching.";
165 static char plugin_instance_doc[] = "";
167 static char PluginData_doc[] =
168 "This is an internal class that is the base for Values\n"
169 "and Notification. It is pretty useless by itself and is therefore not\n"
170 "exported to the collectd module.";
172 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args,
176 self = (PluginData *)type->tp_alloc(type, 0);
183 self->plugin_instance[0] = 0;
185 self->type_instance[0] = 0;
186 return (PyObject *)self;
189 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
190 PluginData *self = (PluginData *)s;
192 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
193 *plugin = NULL, *host = NULL;
194 static char *kwlist[] = {
195 "type", "plugin_instance", "type_instance", "plugin", "host", "time",
198 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL,
199 &type, NULL, &plugin_instance, NULL,
200 &type_instance, NULL, &plugin, NULL, &host,
204 if (type && plugin_get_ds(type) == NULL) {
205 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
210 sstrncpy(self->host, host ? host : "", sizeof(self->host));
211 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
212 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "",
213 sizeof(self->plugin_instance));
214 sstrncpy(self->type, type ? type : "", sizeof(self->type));
215 sstrncpy(self->type_instance, type_instance ? type_instance : "",
216 sizeof(self->type_instance));
224 static PyObject *PluginData_repr(PyObject *s) {
226 static PyObject *l_closing = NULL;
228 if (l_closing == NULL)
229 l_closing = cpy_string_to_unicode_or_bytes(")");
231 if (l_closing == NULL)
234 ret = cpy_common_repr(s);
235 CPY_STRCAT(&ret, l_closing);
239 static PyMemberDef PluginData_members[] = {
240 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc}, {NULL}};
242 static PyObject *PluginData_getstring(PyObject *self, void *data) {
243 const char *value = ((char *)self) + (intptr_t)data;
245 return cpy_string_to_unicode_or_bytes(value);
248 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
253 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
257 new = cpy_unicode_or_bytes_to_string(&value);
262 old = ((char *)self) + (intptr_t)data;
263 sstrncpy(old, new, DATA_MAX_NAME_LEN);
268 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
273 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
277 new = cpy_unicode_or_bytes_to_string(&value);
283 if (plugin_get_ds(new) == NULL) {
284 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
289 old = ((char *)self) + (intptr_t)data;
290 sstrncpy(old, new, DATA_MAX_NAME_LEN);
295 static PyGetSetDef PluginData_getseters[] = {
296 {"host", PluginData_getstring, PluginData_setstring, host_doc,
297 (void *)offsetof(PluginData, host)},
298 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc,
299 (void *)offsetof(PluginData, plugin)},
300 {"plugin_instance", PluginData_getstring, PluginData_setstring,
301 plugin_instance_doc, (void *)offsetof(PluginData, plugin_instance)},
302 {"type_instance", PluginData_getstring, PluginData_setstring,
303 type_instance_doc, (void *)offsetof(PluginData, type_instance)},
304 {"type", PluginData_getstring, PluginData_settype, type_doc,
305 (void *)offsetof(PluginData, type)},
308 PyTypeObject PluginDataType = {
309 CPY_INIT_TYPE "collectd.PluginData", /* tp_name */
310 sizeof(PluginData), /* tp_basicsize */
311 0, /* Will be filled in later */
317 PluginData_repr, /* tp_repr */
318 0, /* tp_as_number */
319 0, /* tp_as_sequence */
320 0, /* tp_as_mapping */
326 0, /* tp_as_buffer */
328 Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
329 PluginData_doc, /* tp_doc */
332 0, /* tp_richcompare */
333 0, /* tp_weaklistoffset */
337 PluginData_members, /* tp_members */
338 PluginData_getseters, /* tp_getset */
341 0, /* tp_descr_get */
342 0, /* tp_descr_set */
343 0, /* tp_dictoffset */
344 PluginData_init, /* tp_init */
346 PluginData_new /* tp_new */
349 static char interval_doc[] =
350 "The interval is the timespan in seconds between two submits for\n"
351 "the same data source. This value has to be a positive integer, so you "
353 "submit more than one value per second. If this member is set to a\n"
354 "non-positive value, the default value as specified in the config file "
356 "be used (default: 10).\n"
358 "If you submit values more often than the specified interval, the average\n"
359 "will be used. If you submit less values, your graphs will have gaps.";
361 static char values_doc[] =
362 "These are the actual values that get dispatched to collectd.\n"
363 "It has to be a sequence (a tuple or list) of numbers.\n"
364 "The size of the sequence and the type of its content depend on the type\n"
365 "member in the types.db file. For more information on this read the\n"
366 "types.db man page.\n"
368 "If the sequence does not have the correct size upon dispatch a "
370 "exception will be raised. If the content of the sequence is not a "
372 "a TypeError exception will be raised.";
374 static char meta_doc[] =
375 "These are the meta data for this Value object.\n"
376 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
377 "strings. int and long objects will be dispatched as signed integers "
379 "they are between 2**63 and 2**64-1, which will result in an unsigned "
381 "You can force one of these storage classes by using the classes\n"
382 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
383 "callback will always contain Signed or Unsigned objects.";
385 static char dispatch_doc[] =
386 "dispatch([type][, values][, plugin_instance][, type_instance]"
387 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
389 "Dispatch this instance to the collectd process. The object has members\n"
390 "for each of the possible arguments for this method. For a detailed "
392 "of these parameters see the member of the same same.\n"
394 "If you do not submit a parameter the value saved in its member will be "
396 "If you do provide a parameter it will be used instead, without altering "
399 static char write_doc[] =
400 "write([destination][, type][, values][, plugin_instance][, type_instance]"
401 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
403 "Write this instance to a single plugin or all plugins if 'destination' is "
405 "This will bypass the main collectd process and all filtering and "
407 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' "
409 "used instead of 'write'.\n";
411 static char Values_doc[] = "A Values object used for dispatching values to "
412 "collectd and receiving values from write "
415 static PyObject *Values_new(PyTypeObject *type, PyObject *args,
419 self = (Values *)PluginData_new(type, args, kwds);
423 self->values = PyList_New(0);
424 self->meta = PyDict_New();
426 return (PyObject *)self;
429 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
430 Values *self = (Values *)s;
431 double interval = 0, time = 0;
432 PyObject *values = NULL, *meta = NULL, *tmp;
433 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
434 *plugin = NULL, *host = NULL;
435 static char *kwlist[] = {
436 "type", "values", "plugin_instance", "type_instance", "plugin",
437 "host", "time", "interval", "meta", NULL};
439 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
440 &type, &values, NULL, &plugin_instance, NULL,
441 &type_instance, NULL, &plugin, NULL, &host,
442 &time, &interval, &meta))
445 if (type && plugin_get_ds(type) == NULL) {
446 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
451 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
452 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
453 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
454 sizeof(self->data.plugin_instance));
455 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
456 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
457 sizeof(self->data.type_instance));
458 self->data.time = time;
462 if (values == NULL) {
463 values = PyList_New(0);
477 self->values = values;
484 self->interval = interval;
488 static int cpy_build_meta(PyObject *meta, cpy_build_meta_handler_t *meta_func,
493 if ((meta == NULL) || (meta == Py_None))
496 l = PyDict_Items(meta); /* New reference. */
498 cpy_log_exception("building meta data");
507 for (int i = 0; i < s; ++i) {
508 const char *string, *keystring;
509 PyObject *key, *value, *item, *tmp;
511 item = PyList_GET_ITEM(l, i);
512 key = PyTuple_GET_ITEM(item, 0);
514 keystring = cpy_unicode_or_bytes_to_string(&key);
520 value = PyTuple_GET_ITEM(item, 1);
522 if (value == Py_True) {
523 meta_func->add_boolean(m, keystring, 1);
524 } else if (value == Py_False) {
525 meta_func->add_boolean(m, keystring, 0);
526 } else if (PyFloat_Check(value)) {
527 meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
528 } else if (PyObject_TypeCheck(value, &SignedType)) {
530 lli = PyLong_AsLongLong(value);
531 if (!PyErr_Occurred() && (lli == (int64_t)lli))
532 meta_func->add_signed_int(m, keystring, lli);
533 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
534 long long unsigned llu;
535 llu = PyLong_AsUnsignedLongLong(value);
536 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
537 meta_func->add_unsigned_int(m, keystring, llu);
538 } else if (PyNumber_Check(value)) {
540 long long unsigned llu;
541 tmp = PyNumber_Long(value);
542 lli = PyLong_AsLongLong(tmp);
543 if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
544 meta_func->add_signed_int(m, keystring, lli);
547 llu = PyLong_AsUnsignedLongLong(tmp);
548 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
549 meta_func->add_unsigned_int(m, keystring, llu);
553 string = cpy_unicode_or_bytes_to_string(&value);
555 meta_func->add_string(m, keystring, string);
558 tmp = PyObject_Str(value);
559 string = cpy_unicode_or_bytes_to_string(&tmp);
561 meta_func->add_string(m, keystring, string);
565 if (PyErr_Occurred())
566 cpy_log_exception("building meta data");
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_value_meta(PyObject *meta) {
578 meta_data_t *m = meta_data_create();
579 cpy_build_meta(meta, &cpy_meta_data, (void *)m);
583 static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
584 cpy_build_meta(meta, &cpy_plugin_notification_meta, (void *)n);
587 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
589 const data_set_t *ds;
592 value_list_t value_list = VALUE_LIST_INIT;
593 PyObject *values = self->values, *meta = self->meta;
594 double time = self->data.time, interval = self->interval;
595 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
596 *type_instance = NULL;
598 static char *kwlist[] = {
599 "type", "values", "plugin_instance", "type_instance", "plugin",
600 "host", "time", "interval", "meta", NULL};
601 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
602 &type, &values, NULL, &plugin_instance, NULL,
603 &type_instance, NULL, &plugin, NULL, &host,
604 &time, &interval, &meta))
607 sstrncpy(value_list.host, host ? host : self->data.host,
608 sizeof(value_list.host));
609 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
610 sizeof(value_list.plugin));
611 sstrncpy(value_list.plugin_instance,
612 plugin_instance ? plugin_instance : self->data.plugin_instance,
613 sizeof(value_list.plugin_instance));
614 sstrncpy(value_list.type, type ? type : self->data.type,
615 sizeof(value_list.type));
616 sstrncpy(value_list.type_instance,
617 type_instance ? type_instance : self->data.type_instance,
618 sizeof(value_list.type_instance));
620 if (value_list.type[0] == 0) {
621 PyErr_SetString(PyExc_RuntimeError, "type not set");
625 ds = plugin_get_ds(value_list.type);
627 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
630 if (values == NULL ||
631 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
632 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
635 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
636 PyErr_Format(PyExc_TypeError, "meta must be a dict");
639 size = (size_t)PySequence_Length(values);
640 if (size != ds->ds_num) {
641 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
642 value_list.type, ds->ds_num, size);
645 value = calloc(size, sizeof(*value));
646 for (size_t i = 0; i < size; ++i) {
647 PyObject *item, *num;
648 item = PySequence_Fast_GET_ITEM(values, (int)i); /* Borrowed reference. */
649 switch (ds->ds[i].type) {
650 case DS_TYPE_COUNTER:
651 num = PyNumber_Long(item); /* New reference. */
653 value[i].counter = PyLong_AsUnsignedLongLong(num);
658 num = PyNumber_Float(item); /* New reference. */
660 value[i].gauge = PyFloat_AsDouble(num);
665 /* This might overflow without raising an exception.
666 * Not much we can do about it */
667 num = PyNumber_Long(item); /* New reference. */
669 value[i].derive = PyLong_AsLongLong(num);
673 case DS_TYPE_ABSOLUTE:
674 /* This might overflow without raising an exception.
675 * Not much we can do about it */
676 num = PyNumber_Long(item); /* New reference. */
678 value[i].absolute = PyLong_AsUnsignedLongLong(num);
684 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
685 ds->ds[i].type, value_list.type);
688 if (PyErr_Occurred() != NULL) {
693 value_list.values = value;
694 value_list.meta = cpy_build_value_meta(meta);
695 value_list.values_len = size;
696 value_list.time = DOUBLE_TO_CDTIME_T(time);
697 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
698 if (value_list.host[0] == 0)
699 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
700 if (value_list.plugin[0] == 0)
701 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
702 Py_BEGIN_ALLOW_THREADS;
703 ret = plugin_dispatch_values(&value_list);
704 Py_END_ALLOW_THREADS;
705 meta_data_destroy(value_list.meta);
708 PyErr_SetString(PyExc_RuntimeError,
709 "error dispatching values, read the logs");
715 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
717 const data_set_t *ds;
720 value_list_t value_list = VALUE_LIST_INIT;
721 PyObject *values = self->values, *meta = self->meta;
722 double time = self->data.time, interval = self->interval;
723 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
724 *type_instance = NULL, *dest = NULL;
726 static char *kwlist[] = {
727 "destination", "type", "values", "plugin_instance",
728 "type_instance", "plugin", "host", "time",
729 "interval", "meta", NULL};
730 if (!PyArg_ParseTupleAndKeywords(
731 args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest, NULL, &type,
732 &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin,
733 NULL, &host, &time, &interval, &meta))
736 sstrncpy(value_list.host, host ? host : self->data.host,
737 sizeof(value_list.host));
738 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
739 sizeof(value_list.plugin));
740 sstrncpy(value_list.plugin_instance,
741 plugin_instance ? plugin_instance : self->data.plugin_instance,
742 sizeof(value_list.plugin_instance));
743 sstrncpy(value_list.type, type ? type : self->data.type,
744 sizeof(value_list.type));
745 sstrncpy(value_list.type_instance,
746 type_instance ? type_instance : self->data.type_instance,
747 sizeof(value_list.type_instance));
749 if (value_list.type[0] == 0) {
750 PyErr_SetString(PyExc_RuntimeError, "type not set");
753 ds = plugin_get_ds(value_list.type);
755 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
758 if (values == NULL ||
759 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
760 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
763 size = (size_t)PySequence_Length(values);
764 if (size != ds->ds_num) {
765 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
766 value_list.type, ds->ds_num, size);
769 value = calloc(size, sizeof(*value));
770 for (size_t i = 0; i < size; ++i) {
771 PyObject *item, *num;
772 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
773 switch (ds->ds[i].type) {
774 case DS_TYPE_COUNTER:
775 num = PyNumber_Long(item); /* New reference. */
777 value[i].counter = PyLong_AsUnsignedLongLong(num);
782 num = PyNumber_Float(item); /* New reference. */
784 value[i].gauge = PyFloat_AsDouble(num);
789 /* This might overflow without raising an exception.
790 * Not much we can do about it */
791 num = PyNumber_Long(item); /* New reference. */
793 value[i].derive = PyLong_AsLongLong(num);
797 case DS_TYPE_ABSOLUTE:
798 /* This might overflow without raising an exception.
799 * Not much we can do about it */
800 num = PyNumber_Long(item); /* New reference. */
802 value[i].absolute = PyLong_AsUnsignedLongLong(num);
808 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
809 ds->ds[i].type, value_list.type);
812 if (PyErr_Occurred() != NULL) {
817 value_list.values = value;
818 value_list.values_len = size;
819 value_list.time = DOUBLE_TO_CDTIME_T(time);
820 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
821 value_list.meta = cpy_build_value_meta(meta);
822 if (value_list.host[0] == 0)
823 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
824 if (value_list.plugin[0] == 0)
825 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
826 Py_BEGIN_ALLOW_THREADS;
827 ret = plugin_write(dest, NULL, &value_list);
828 Py_END_ALLOW_THREADS;
829 meta_data_destroy(value_list.meta);
832 PyErr_SetString(PyExc_RuntimeError,
833 "error dispatching values, read the logs");
839 static PyObject *Values_repr(PyObject *s) {
841 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL,
843 Values *self = (Values *)s;
845 if (l_interval == NULL)
846 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
847 if (l_values == NULL)
848 l_values = cpy_string_to_unicode_or_bytes(",values=");
850 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
851 if (l_closing == NULL)
852 l_closing = cpy_string_to_unicode_or_bytes(")");
854 if (l_interval == NULL || l_values == NULL || l_meta == NULL ||
858 ret = cpy_common_repr(s);
859 if (self->interval != 0) {
860 CPY_STRCAT(&ret, l_interval);
861 tmp = PyFloat_FromDouble(self->interval);
862 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
863 CPY_STRCAT_AND_DEL(&ret, tmp);
866 (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
867 CPY_STRCAT(&ret, l_values);
868 tmp = PyObject_Repr(self->values);
869 CPY_STRCAT_AND_DEL(&ret, tmp);
872 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
873 CPY_STRCAT(&ret, l_meta);
874 tmp = PyObject_Repr(self->meta);
875 CPY_STRCAT_AND_DEL(&ret, tmp);
877 CPY_STRCAT(&ret, l_closing);
881 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
882 Values *v = (Values *)self;
888 static int Values_clear(PyObject *self) {
889 Values *v = (Values *)self;
895 static void Values_dealloc(PyObject *self) {
897 self->ob_type->tp_free(self);
900 static PyMemberDef Values_members[] = {
901 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
902 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
903 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
906 static PyMethodDef Values_methods[] = {
907 {"dispatch", (PyCFunction)Values_dispatch, METH_VARARGS | METH_KEYWORDS,
909 {"write", (PyCFunction)Values_write, METH_VARARGS | METH_KEYWORDS,
913 PyTypeObject ValuesType = {
914 CPY_INIT_TYPE "collectd.Values", /* tp_name */
915 sizeof(Values), /* tp_basicsize */
916 0, /* Will be filled in later */
917 Values_dealloc, /* tp_dealloc */
922 Values_repr, /* tp_repr */
923 0, /* tp_as_number */
924 0, /* tp_as_sequence */
925 0, /* tp_as_mapping */
931 0, /* tp_as_buffer */
932 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
933 Values_doc, /* tp_doc */
934 Values_traverse, /* tp_traverse */
935 Values_clear, /* tp_clear */
936 0, /* tp_richcompare */
937 0, /* tp_weaklistoffset */
940 Values_methods, /* tp_methods */
941 Values_members, /* tp_members */
945 0, /* tp_descr_get */
946 0, /* tp_descr_set */
947 0, /* tp_dictoffset */
948 Values_init, /* tp_init */
950 Values_new /* tp_new */
953 static char notification_meta_doc[] =
954 "These are the meta data for the Notification object.\n"
955 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
956 "strings. int and long objects will be dispatched as signed integers "
958 "they are between 2**63 and 2**64-1, which will result in an unsigned "
960 "One of these storage classes can be forced by using the classes\n"
961 "collectd.Signed and collectd.Unsigned. A meta object received by a\n"
962 "notification callback will always contain Signed or Unsigned objects.";
964 static char severity_doc[] =
965 "The severity of this notification. Assign or compare to\n"
966 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
968 static char message_doc[] = "Some kind of description what's going on and why "
969 "this Notification was generated.";
971 static char Notification_doc[] =
972 "The Notification class is a wrapper around the collectd notification.\n"
973 "It can be used to notify other plugins about bad stuff happening. It "
975 "similar to Values but has a severity and a message instead of interval\n"
977 "Notifications can be dispatched at any time and can be received with "
978 "register_notification.";
980 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
981 Notification *self = (Notification *)s;
984 char *message = NULL;
985 PyObject *meta = NULL;
986 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
987 *plugin = NULL, *host = NULL;
988 static char *kwlist[] = {"type", "message", "plugin_instance",
989 "type_instance", "plugin", "host",
990 "time", "severity", "meta",
993 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
994 &type, NULL, &message, NULL,
995 &plugin_instance, NULL, &type_instance, NULL,
996 &plugin, NULL, &host, &time, &severity, &meta))
999 if (type && plugin_get_ds(type) == NULL) {
1000 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
1002 PyMem_Free(message);
1006 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
1007 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
1008 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
1009 sizeof(self->data.plugin_instance));
1010 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
1011 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
1012 sizeof(self->data.type_instance));
1013 sstrncpy(self->message, message ? message : "", sizeof(self->message));
1014 self->data.time = time;
1015 self->severity = severity;
1018 PyMem_Free(message);
1021 meta = PyDict_New();
1027 PyObject *tmp = self->meta;
1034 static PyObject *Notification_dispatch(Notification *self, PyObject *args,
1037 const data_set_t *ds;
1038 notification_t notification;
1039 double t = self->data.time;
1040 PyObject *meta = self->meta;
1041 int severity = self->severity;
1042 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
1043 *type_instance = NULL;
1044 char *message = NULL;
1046 static char *kwlist[] = {"type", "message", "plugin_instance",
1047 "type_instance", "plugin", "host",
1048 "time", "severity", "meta",
1050 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
1051 &type, NULL, &message, NULL,
1052 &plugin_instance, NULL, &type_instance, NULL,
1053 &plugin, NULL, &host, &t, &severity, &meta))
1056 notification.time = DOUBLE_TO_CDTIME_T(t);
1057 notification.severity = severity;
1058 sstrncpy(notification.message, message ? message : self->message,
1059 sizeof(notification.message));
1060 sstrncpy(notification.host, host ? host : self->data.host,
1061 sizeof(notification.host));
1062 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin,
1063 sizeof(notification.plugin));
1064 sstrncpy(notification.plugin_instance,
1065 plugin_instance ? plugin_instance : self->data.plugin_instance,
1066 sizeof(notification.plugin_instance));
1067 sstrncpy(notification.type, type ? type : self->data.type,
1068 sizeof(notification.type));
1069 sstrncpy(notification.type_instance,
1070 type_instance ? type_instance : self->data.type_instance,
1071 sizeof(notification.type_instance));
1072 notification.meta = NULL;
1074 PyMem_Free(message);
1076 if (notification.type[0] == 0) {
1077 PyErr_SetString(PyExc_RuntimeError, "type not set");
1080 ds = plugin_get_ds(notification.type);
1082 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
1085 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
1086 PyErr_Format(PyExc_TypeError, "meta must be a dict");
1089 cpy_build_notification_meta(¬ification, meta);
1091 if (notification.time == 0)
1092 notification.time = cdtime();
1093 if (notification.host[0] == 0)
1094 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
1095 if (notification.plugin[0] == 0)
1096 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
1097 Py_BEGIN_ALLOW_THREADS;
1098 ret = plugin_dispatch_notification(¬ification);
1099 if (notification.meta)
1100 plugin_notification_meta_free(notification.meta);
1101 Py_END_ALLOW_THREADS;
1103 PyErr_SetString(PyExc_RuntimeError,
1104 "error dispatching notification, read the logs");
1110 static PyObject *Notification_new(PyTypeObject *type, PyObject *args,
1114 self = (Notification *)PluginData_new(type, args, kwds);
1118 self->meta = PyDict_New();
1119 self->message[0] = 0;
1121 return (PyObject *)self;
1124 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
1128 if (value == NULL) {
1129 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
1133 new = cpy_unicode_or_bytes_to_string(&value);
1138 old = ((char *)self) + (intptr_t)data;
1139 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
1144 static PyObject *Notification_repr(PyObject *s) {
1145 PyObject *ret, *tmp;
1146 static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
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, notification_meta_doc},
1213 static PyGetSetDef Notification_getseters[] = {
1214 {"message", PluginData_getstring, Notification_setstring, message_doc,
1215 (void *)offsetof(Notification, message)},
1218 PyTypeObject NotificationType = {
1219 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1220 sizeof(Notification), /* tp_basicsize */
1221 0, /* Will be filled in later */
1222 Notification_dealloc, /* tp_dealloc */
1227 Notification_repr, /* tp_repr */
1228 0, /* tp_as_number */
1229 0, /* tp_as_sequence */
1230 0, /* tp_as_mapping */
1234 0, /* tp_getattro */
1235 0, /* tp_setattro */
1236 0, /* tp_as_buffer */
1237 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
1238 Notification_doc, /* tp_doc */
1239 Notification_traverse, /* tp_traverse */
1240 Notification_clear, /* tp_clear */
1241 0, /* tp_richcompare */
1242 0, /* tp_weaklistoffset */
1244 0, /* tp_iternext */
1245 Notification_methods, /* tp_methods */
1246 Notification_members, /* tp_members */
1247 Notification_getseters, /* tp_getset */
1250 0, /* tp_descr_get */
1251 0, /* tp_descr_set */
1252 0, /* tp_dictoffset */
1253 Notification_init, /* tp_init */
1255 Notification_new /* tp_new */
1258 static char Signed_doc[] =
1259 "This is a long by another name. Use it in meta data dicts\n"
1260 "to choose the way it is stored in the meta data.";
1262 PyTypeObject SignedType = {
1263 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1264 sizeof(Signed), /* tp_basicsize */
1265 0, /* Will be filled in later */
1272 0, /* tp_as_number */
1273 0, /* tp_as_sequence */
1274 0, /* tp_as_mapping */
1278 0, /* tp_getattro */
1279 0, /* tp_setattro */
1280 0, /* tp_as_buffer */
1281 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1282 Signed_doc /* tp_doc */
1285 static char Unsigned_doc[] =
1286 "This is a long by another name. Use it in meta data dicts\n"
1287 "to choose the way it is stored in the meta data.";
1289 PyTypeObject UnsignedType = {
1290 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1291 sizeof(Unsigned), /* tp_basicsize */
1292 0, /* Will be filled in later */
1299 0, /* tp_as_number */
1300 0, /* tp_as_sequence */
1301 0, /* tp_as_mapping */
1305 0, /* tp_getattro */
1306 0, /* tp_setattro */
1307 0, /* tp_as_buffer */
1308 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1309 Unsigned_doc /* tp_doc */