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 if (cpy_build_meta(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(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, "type %s needs %zu values, got %zu",
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_value_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, "type %s needs %zu values, got %zu",
769 value_list.type, ds->ds_num, size);
772 value = calloc(size, sizeof(*value));
773 for (size_t i = 0; i < size; ++i) {
774 PyObject *item, *num;
775 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
776 switch (ds->ds[i].type) {
777 case DS_TYPE_COUNTER:
778 num = PyNumber_Long(item); /* New reference. */
780 value[i].counter = PyLong_AsUnsignedLongLong(num);
785 num = PyNumber_Float(item); /* New reference. */
787 value[i].gauge = PyFloat_AsDouble(num);
792 /* This might overflow without raising an exception.
793 * Not much we can do about it */
794 num = PyNumber_Long(item); /* New reference. */
796 value[i].derive = PyLong_AsLongLong(num);
800 case DS_TYPE_ABSOLUTE:
801 /* This might overflow without raising an exception.
802 * Not much we can do about it */
803 num = PyNumber_Long(item); /* New reference. */
805 value[i].absolute = PyLong_AsUnsignedLongLong(num);
811 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
812 ds->ds[i].type, value_list.type);
815 if (PyErr_Occurred() != NULL) {
820 value_list.values = value;
821 value_list.values_len = size;
822 value_list.time = DOUBLE_TO_CDTIME_T(time);
823 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
824 value_list.meta = cpy_build_value_meta(meta);
825 if (value_list.host[0] == 0)
826 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
827 if (value_list.plugin[0] == 0)
828 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
829 Py_BEGIN_ALLOW_THREADS;
830 ret = plugin_write(dest, NULL, &value_list);
831 Py_END_ALLOW_THREADS;
832 meta_data_destroy(value_list.meta);
835 PyErr_SetString(PyExc_RuntimeError,
836 "error dispatching values, read the logs");
842 static PyObject *Values_repr(PyObject *s) {
844 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL,
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[] = {"type", "message", "plugin_instance",
992 "type_instance", "plugin", "host",
993 "time", "severity", "meta",
996 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdiO", kwlist, NULL,
997 &type, NULL, &message, NULL,
998 &plugin_instance, NULL, &type_instance, NULL,
999 &plugin, NULL, &host, &time, &severity, &meta))
1002 if (type && plugin_get_ds(type) == NULL) {
1003 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
1005 PyMem_Free(message);
1009 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
1010 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
1011 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
1012 sizeof(self->data.plugin_instance));
1013 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
1014 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
1015 sizeof(self->data.type_instance));
1016 sstrncpy(self->message, message ? message : "", sizeof(self->message));
1017 self->data.time = time;
1018 self->severity = severity;
1021 PyMem_Free(message);
1024 meta = PyDict_New();
1030 PyObject *tmp = self->meta;
1037 static PyObject *Notification_dispatch(Notification *self, PyObject *args,
1040 const data_set_t *ds;
1041 notification_t notification;
1042 double t = self->data.time;
1043 PyObject *meta = self->meta;
1044 int severity = self->severity;
1045 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
1046 *type_instance = NULL;
1047 char *message = NULL;
1049 static char *kwlist[] = {"type", "message", "plugin_instance",
1050 "type_instance", "plugin", "host",
1051 "time", "severity", "meta",
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, notification_meta_doc},
1216 static PyGetSetDef Notification_getseters[] = {
1217 {"message", PluginData_getstring, Notification_setstring, message_doc,
1218 (void *)offsetof(Notification, message)},
1221 PyTypeObject NotificationType = {
1222 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1223 sizeof(Notification), /* tp_basicsize */
1224 0, /* Will be filled in later */
1225 Notification_dealloc, /* tp_dealloc */
1230 Notification_repr, /* tp_repr */
1231 0, /* tp_as_number */
1232 0, /* tp_as_sequence */
1233 0, /* tp_as_mapping */
1237 0, /* tp_getattro */
1238 0, /* tp_setattro */
1239 0, /* tp_as_buffer */
1240 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
1241 Notification_doc, /* tp_doc */
1242 Notification_traverse, /* tp_traverse */
1243 Notification_clear, /* tp_clear */
1244 0, /* tp_richcompare */
1245 0, /* tp_weaklistoffset */
1247 0, /* tp_iternext */
1248 Notification_methods, /* tp_methods */
1249 Notification_members, /* tp_members */
1250 Notification_getseters, /* tp_getset */
1253 0, /* tp_descr_get */
1254 0, /* tp_descr_set */
1255 0, /* tp_dictoffset */
1256 Notification_init, /* tp_init */
1258 Notification_new /* tp_new */
1261 static char Signed_doc[] =
1262 "This is a long by another name. Use it in meta data dicts\n"
1263 "to choose the way it is stored in the meta data.";
1265 PyTypeObject SignedType = {
1266 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1267 sizeof(Signed), /* tp_basicsize */
1268 0, /* Will be filled in later */
1275 0, /* tp_as_number */
1276 0, /* tp_as_sequence */
1277 0, /* tp_as_mapping */
1281 0, /* tp_getattro */
1282 0, /* tp_setattro */
1283 0, /* tp_as_buffer */
1284 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1285 Signed_doc /* tp_doc */
1288 static char Unsigned_doc[] =
1289 "This is a long by another name. Use it in meta data dicts\n"
1290 "to choose the way it is stored in the meta data.";
1292 PyTypeObject UnsignedType = {
1293 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1294 sizeof(Unsigned), /* tp_basicsize */
1295 0, /* Will be filled in later */
1302 0, /* tp_as_number */
1303 0, /* tp_as_sequence */
1304 0, /* tp_as_mapping */
1308 0, /* tp_getattro */
1309 0, /* tp_setattro */
1310 0, /* tp_as_buffer */
1311 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1312 Unsigned_doc /* tp_doc */