2 * collectd - src/pyvalues.c
3 * Copyright (C) 2009 Sven Trenkel
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Sven Trenkel <collectd at semidefinite.de>
28 #include <structmember.h>
37 int (*add_string)(void *, const char *, const char *);
38 int (*add_signed_int)(void *, const char *, int64_t);
39 int (*add_unsigned_int)(void *, const char *, uint64_t);
40 int (*add_double)(void *, const char *, double);
41 int (*add_boolean)(void *, const char *, _Bool);
42 } cpy_build_meta_handler_t;
47 PyMem_Free(plugin_instance); \
48 PyMem_Free(type_instance); \
53 static PyObject *cpy_common_repr(PyObject *s) {
55 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL,
56 *l_plugin_instance = NULL;
57 static PyObject *l_host = NULL, *l_time = NULL;
58 PluginData *self = (PluginData *)s;
61 l_type = cpy_string_to_unicode_or_bytes("(type=");
62 if (l_type_instance == NULL)
63 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
65 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
66 if (l_plugin_instance == NULL)
67 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
69 l_host = cpy_string_to_unicode_or_bytes(",host=");
71 l_time = cpy_string_to_unicode_or_bytes(",time=");
73 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance ||
77 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
79 CPY_STRCAT(&ret, l_type);
80 tmp = cpy_string_to_unicode_or_bytes(self->type);
81 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
82 CPY_STRCAT_AND_DEL(&ret, tmp);
84 if (self->type_instance[0] != 0) {
85 CPY_STRCAT(&ret, l_type_instance);
86 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
87 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
88 CPY_STRCAT_AND_DEL(&ret, tmp);
91 if (self->plugin[0] != 0) {
92 CPY_STRCAT(&ret, l_plugin);
93 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
94 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
95 CPY_STRCAT_AND_DEL(&ret, tmp);
98 if (self->plugin_instance[0] != 0) {
99 CPY_STRCAT(&ret, l_plugin_instance);
100 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
101 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
102 CPY_STRCAT_AND_DEL(&ret, tmp);
105 if (self->host[0] != 0) {
106 CPY_STRCAT(&ret, l_host);
107 tmp = cpy_string_to_unicode_or_bytes(self->host);
108 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
109 CPY_STRCAT_AND_DEL(&ret, tmp);
112 if (self->time != 0) {
113 CPY_STRCAT(&ret, l_time);
114 tmp = PyFloat_FromDouble(self->time);
115 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
116 CPY_STRCAT_AND_DEL(&ret, tmp);
121 static char time_doc[] =
122 "This is the Unix timestamp of the time this value was read.\n"
123 "For dispatching values this can be set to 0 which means \"now\".\n"
124 "This means the time the value is actually dispatched, not the time\n"
127 static char host_doc[] =
128 "The hostname of the host this value was read from.\n"
129 "For dispatching this can be set to an empty string which means\n"
130 "the local hostname as defined in collectd.conf.";
132 static char type_doc[] =
133 "The type of this value. This type has to be defined\n"
134 "in the types.db file. Attempting to set it to any other value\n"
135 "will raise a TypeError exception.\n"
136 "Assigning a type is mandatory, calling dispatch without doing\n"
137 "so will raise a RuntimeError exception.";
139 static char type_instance_doc[] = "";
141 static char plugin_doc[] =
142 "The name of the plugin that read the data. Setting this\n"
143 "member to an empty string will insert \"python\" upon dispatching.";
145 static char plugin_instance_doc[] = "";
147 static char PluginData_doc[] =
148 "This is an internal class that is the base for Values\n"
149 "and Notification. It is pretty useless by itself and is therefore not\n"
150 "exported to the collectd module.";
152 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args,
156 self = (PluginData *)type->tp_alloc(type, 0);
163 self->plugin_instance[0] = 0;
165 self->type_instance[0] = 0;
166 return (PyObject *)self;
169 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
170 PluginData *self = (PluginData *)s;
172 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
173 *plugin = NULL, *host = NULL;
174 static char *kwlist[] = {
175 "type", "plugin_instance", "type_instance", "plugin", "host", "time",
178 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL,
179 &type, NULL, &plugin_instance, NULL,
180 &type_instance, NULL, &plugin, NULL, &host,
184 if (type && plugin_get_ds(type) == NULL) {
185 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
190 sstrncpy(self->host, host ? host : "", sizeof(self->host));
191 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
192 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "",
193 sizeof(self->plugin_instance));
194 sstrncpy(self->type, type ? type : "", sizeof(self->type));
195 sstrncpy(self->type_instance, type_instance ? type_instance : "",
196 sizeof(self->type_instance));
204 static PyObject *PluginData_repr(PyObject *s) {
206 static PyObject *l_closing = NULL;
208 if (l_closing == NULL)
209 l_closing = cpy_string_to_unicode_or_bytes(")");
211 if (l_closing == NULL)
214 ret = cpy_common_repr(s);
215 CPY_STRCAT(&ret, l_closing);
219 static PyMemberDef PluginData_members[] = {
220 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc}, {NULL}};
222 static PyObject *PluginData_getstring(PyObject *self, void *data) {
223 const char *value = ((char *)self) + (intptr_t)data;
225 return cpy_string_to_unicode_or_bytes(value);
228 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
233 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
237 new = cpy_unicode_or_bytes_to_string(&value);
242 old = ((char *)self) + (intptr_t)data;
243 sstrncpy(old, new, DATA_MAX_NAME_LEN);
248 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
253 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
257 new = cpy_unicode_or_bytes_to_string(&value);
263 if (plugin_get_ds(new) == NULL) {
264 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
269 old = ((char *)self) + (intptr_t)data;
270 sstrncpy(old, new, DATA_MAX_NAME_LEN);
275 static PyGetSetDef PluginData_getseters[] = {
276 {"host", PluginData_getstring, PluginData_setstring, host_doc,
277 (void *)offsetof(PluginData, host)},
278 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc,
279 (void *)offsetof(PluginData, plugin)},
280 {"plugin_instance", PluginData_getstring, PluginData_setstring,
281 plugin_instance_doc, (void *)offsetof(PluginData, plugin_instance)},
282 {"type_instance", PluginData_getstring, PluginData_setstring,
283 type_instance_doc, (void *)offsetof(PluginData, type_instance)},
284 {"type", PluginData_getstring, PluginData_settype, type_doc,
285 (void *)offsetof(PluginData, type)},
288 PyTypeObject PluginDataType = {
289 CPY_INIT_TYPE "collectd.PluginData", /* tp_name */
290 sizeof(PluginData), /* tp_basicsize */
291 0, /* Will be filled in later */
297 PluginData_repr, /* tp_repr */
298 0, /* tp_as_number */
299 0, /* tp_as_sequence */
300 0, /* tp_as_mapping */
306 0, /* tp_as_buffer */
308 Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
309 PluginData_doc, /* tp_doc */
312 0, /* tp_richcompare */
313 0, /* tp_weaklistoffset */
317 PluginData_members, /* tp_members */
318 PluginData_getseters, /* tp_getset */
321 0, /* tp_descr_get */
322 0, /* tp_descr_set */
323 0, /* tp_dictoffset */
324 PluginData_init, /* tp_init */
326 PluginData_new /* tp_new */
329 static char interval_doc[] =
330 "The interval is the timespan in seconds between two submits for\n"
331 "the same data source. This value has to be a positive integer, so you "
333 "submit more than one value per second. If this member is set to a\n"
334 "non-positive value, the default value as specified in the config file "
336 "be used (default: 10).\n"
338 "If you submit values more often than the specified interval, the average\n"
339 "will be used. If you submit less values, your graphs will have gaps.";
341 static char values_doc[] =
342 "These are the actual values that get dispatched to collectd.\n"
343 "It has to be a sequence (a tuple or list) of numbers.\n"
344 "The size of the sequence and the type of its content depend on the type\n"
345 "member in the types.db file. For more information on this read the\n"
346 "types.db man page.\n"
348 "If the sequence does not have the correct size upon dispatch a "
350 "exception will be raised. If the content of the sequence is not a "
352 "a TypeError exception will be raised.";
354 static char meta_doc[] =
355 "These are the meta data for this Value object.\n"
356 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
357 "strings. int and long objects will be dispatched as signed integers "
359 "they are between 2**63 and 2**64-1, which will result in an unsigned "
361 "You can force one of these storage classes by using the classes\n"
362 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
363 "callback will always contain Signed or Unsigned objects.";
365 static char dispatch_doc[] =
366 "dispatch([type][, values][, plugin_instance][, type_instance]"
367 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
369 "Dispatch this instance to the collectd process. The object has members\n"
370 "for each of the possible arguments for this method. For a detailed "
372 "of these parameters see the member of the same same.\n"
374 "If you do not submit a parameter the value saved in its member will be "
376 "If you do provide a parameter it will be used instead, without altering "
379 static char write_doc[] =
380 "write([destination][, type][, values][, plugin_instance][, type_instance]"
381 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
383 "Write this instance to a single plugin or all plugins if 'destination' is "
385 "This will bypass the main collectd process and all filtering and "
387 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' "
389 "used instead of 'write'.\n";
391 static char Values_doc[] = "A Values object used for dispatching values to "
392 "collectd and receiving values from write "
395 static PyObject *Values_new(PyTypeObject *type, PyObject *args,
399 self = (Values *)PluginData_new(type, args, kwds);
403 self->values = PyList_New(0);
404 self->meta = PyDict_New();
406 return (PyObject *)self;
409 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
410 Values *self = (Values *)s;
411 double interval = 0, time = 0;
412 PyObject *values = NULL, *meta = NULL, *tmp;
413 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
414 *plugin = NULL, *host = NULL;
415 static char *kwlist[] = {
416 "type", "values", "plugin_instance", "type_instance", "plugin",
417 "host", "time", "interval", "meta", NULL};
419 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
420 &type, &values, NULL, &plugin_instance, NULL,
421 &type_instance, NULL, &plugin, NULL, &host,
422 &time, &interval, &meta))
425 if (type && plugin_get_ds(type) == NULL) {
426 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
431 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
432 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
433 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
434 sizeof(self->data.plugin_instance));
435 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
436 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
437 sizeof(self->data.type_instance));
438 self->data.time = time;
442 if (values == NULL) {
443 values = PyList_New(0);
457 self->values = values;
464 self->interval = interval;
468 static int cpy_build_meta_generic(PyObject *meta,
469 cpy_build_meta_handler_t *meta_func,
474 if ((meta == NULL) || (meta == Py_None))
477 l = PyDict_Items(meta); /* New reference. */
479 cpy_log_exception("building meta data");
488 for (int i = 0; i < s; ++i) {
489 const char *string, *keystring;
490 PyObject *key, *value, *item, *tmp;
492 item = PyList_GET_ITEM(l, i);
493 key = PyTuple_GET_ITEM(item, 0);
495 keystring = cpy_unicode_or_bytes_to_string(&key);
501 value = PyTuple_GET_ITEM(item, 1);
503 if (value == Py_True) {
504 meta_func->add_boolean(m, keystring, 1);
505 } else if (value == Py_False) {
506 meta_func->add_boolean(m, keystring, 0);
507 } else if (PyFloat_Check(value)) {
508 meta_func->add_double(m, keystring, PyFloat_AsDouble(value));
509 } else if (PyObject_TypeCheck(value, &SignedType)) {
511 lli = PyLong_AsLongLong(value);
512 if (!PyErr_Occurred() && (lli == (int64_t)lli))
513 meta_func->add_signed_int(m, keystring, lli);
514 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
515 long long unsigned llu;
516 llu = PyLong_AsUnsignedLongLong(value);
517 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
518 meta_func->add_unsigned_int(m, keystring, llu);
519 } else if (PyNumber_Check(value)) {
521 long long unsigned llu;
522 tmp = PyNumber_Long(value);
523 lli = PyLong_AsLongLong(tmp);
524 if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
525 meta_func->add_signed_int(m, keystring, lli);
528 llu = PyLong_AsUnsignedLongLong(tmp);
529 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
530 meta_func->add_unsigned_int(m, keystring, llu);
534 string = cpy_unicode_or_bytes_to_string(&value);
536 meta_func->add_string(m, keystring, string);
539 tmp = PyObject_Str(value);
540 string = cpy_unicode_or_bytes_to_string(&tmp);
542 meta_func->add_string(m, keystring, string);
546 if (PyErr_Occurred())
547 cpy_log_exception("building meta data");
555 #define CPY_BUILD_META_FUNC(meta_type, func, val_type) \
556 static int cpy_##func(void *meta, const char *key, val_type val) { \
557 return func((meta_type *)meta, key, val); \
560 #define CPY_BUILD_META_HANDLER(func_prefix, meta_type) \
561 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_string, const char *) \
562 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t) \
563 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t) \
564 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double) \
565 CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool) \
567 static cpy_build_meta_handler_t cpy_##func_prefix = { \
568 .add_string = cpy_##func_prefix##_add_string, \
569 .add_signed_int = cpy_##func_prefix##_add_signed_int, \
570 .add_unsigned_int = cpy_##func_prefix##_add_unsigned_int, \
571 .add_double = cpy_##func_prefix##_add_double, \
572 .add_boolean = cpy_##func_prefix##_add_boolean}
574 CPY_BUILD_META_HANDLER(meta_data, meta_data_t);
575 CPY_BUILD_META_HANDLER(plugin_notification_meta, notification_t);
577 static meta_data_t *cpy_build_meta(PyObject *meta) {
578 meta_data_t *m = meta_data_create();
579 if (cpy_build_meta_generic(meta, &cpy_meta_data, (void *)m) < 0) {
580 meta_data_destroy(m);
586 static void cpy_build_notification_meta(notification_t *n, PyObject *meta) {
587 cpy_build_meta_generic(meta, &cpy_plugin_notification_meta, (void *)n);
590 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
592 const data_set_t *ds;
595 value_list_t value_list = VALUE_LIST_INIT;
596 PyObject *values = self->values, *meta = self->meta;
597 double time = self->data.time, interval = self->interval;
598 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
599 *type_instance = NULL;
601 static char *kwlist[] = {
602 "type", "values", "plugin_instance", "type_instance", "plugin",
603 "host", "time", "interval", "meta", NULL};
604 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
605 &type, &values, NULL, &plugin_instance, NULL,
606 &type_instance, NULL, &plugin, NULL, &host,
607 &time, &interval, &meta))
610 sstrncpy(value_list.host, host ? host : self->data.host,
611 sizeof(value_list.host));
612 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
613 sizeof(value_list.plugin));
614 sstrncpy(value_list.plugin_instance,
615 plugin_instance ? plugin_instance : self->data.plugin_instance,
616 sizeof(value_list.plugin_instance));
617 sstrncpy(value_list.type, type ? type : self->data.type,
618 sizeof(value_list.type));
619 sstrncpy(value_list.type_instance,
620 type_instance ? type_instance : self->data.type_instance,
621 sizeof(value_list.type_instance));
623 if (value_list.type[0] == 0) {
624 PyErr_SetString(PyExc_RuntimeError, "type not set");
628 ds = plugin_get_ds(value_list.type);
630 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
633 if (values == NULL ||
634 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
635 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
638 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
639 PyErr_Format(PyExc_TypeError, "meta must be a dict");
642 size = (size_t)PySequence_Length(values);
643 if (size != ds->ds_num) {
644 PyErr_Format(PyExc_RuntimeError, "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_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_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[] = {
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 = NULL, *l_message = NULL, *l_meta = NULL,
1149 Notification *self = (Notification *)s;
1151 if (l_severity == NULL)
1152 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
1153 if (l_message == NULL)
1154 l_message = cpy_string_to_unicode_or_bytes(",message=");
1156 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
1157 if (l_closing == NULL)
1158 l_closing = cpy_string_to_unicode_or_bytes(")");
1160 if (l_severity == NULL || l_message == NULL || l_meta == NULL ||
1164 ret = cpy_common_repr(s);
1165 if (self->severity != 0) {
1166 CPY_STRCAT(&ret, l_severity);
1167 tmp = PyInt_FromLong(self->severity);
1168 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1169 CPY_STRCAT_AND_DEL(&ret, tmp);
1171 if (self->message[0] != 0) {
1172 CPY_STRCAT(&ret, l_message);
1173 tmp = cpy_string_to_unicode_or_bytes(self->message);
1174 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1175 CPY_STRCAT_AND_DEL(&ret, tmp);
1178 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
1179 CPY_STRCAT(&ret, l_meta);
1180 tmp = PyObject_Repr(self->meta);
1181 CPY_STRCAT_AND_DEL(&ret, tmp);
1183 CPY_STRCAT(&ret, l_closing);
1187 static int Notification_traverse(PyObject *self, visitproc visit, void *arg) {
1188 Notification *n = (Notification *)self;
1193 static int Notification_clear(PyObject *self) {
1194 Notification *n = (Notification *)self;
1199 static void Notification_dealloc(PyObject *self) {
1200 Notification_clear(self);
1201 self->ob_type->tp_free(self);
1204 static PyMethodDef Notification_methods[] = {
1205 {"dispatch", (PyCFunction)Notification_dispatch,
1206 METH_VARARGS | METH_KEYWORDS, dispatch_doc},
1209 static PyMemberDef Notification_members[] = {
1210 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1211 {"meta", T_OBJECT_EX, offsetof(Notification, meta), 0,
1212 notification_meta_doc},
1215 static PyGetSetDef Notification_getseters[] = {
1216 {"message", PluginData_getstring, Notification_setstring, message_doc,
1217 (void *)offsetof(Notification, message)},
1220 PyTypeObject NotificationType = {
1221 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1222 sizeof(Notification), /* tp_basicsize */
1223 0, /* Will be filled in later */
1224 Notification_dealloc, /* tp_dealloc */
1229 Notification_repr, /* tp_repr */
1230 0, /* tp_as_number */
1231 0, /* tp_as_sequence */
1232 0, /* tp_as_mapping */
1236 0, /* tp_getattro */
1237 0, /* tp_setattro */
1238 0, /* tp_as_buffer */
1239 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
1240 Notification_doc, /* tp_doc */
1241 Notification_traverse, /* tp_traverse */
1242 Notification_clear, /* tp_clear */
1243 0, /* tp_richcompare */
1244 0, /* tp_weaklistoffset */
1246 0, /* tp_iternext */
1247 Notification_methods, /* tp_methods */
1248 Notification_members, /* tp_members */
1249 Notification_getseters, /* tp_getset */
1252 0, /* tp_descr_get */
1253 0, /* tp_descr_set */
1254 0, /* tp_dictoffset */
1255 Notification_init, /* tp_init */
1257 Notification_new /* tp_new */
1260 static char Signed_doc[] =
1261 "This is a long by another name. Use it in meta data dicts\n"
1262 "to choose the way it is stored in the meta data.";
1264 PyTypeObject SignedType = {
1265 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1266 sizeof(Signed), /* tp_basicsize */
1267 0, /* Will be filled in later */
1274 0, /* tp_as_number */
1275 0, /* tp_as_sequence */
1276 0, /* tp_as_mapping */
1280 0, /* tp_getattro */
1281 0, /* tp_setattro */
1282 0, /* tp_as_buffer */
1283 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1284 Signed_doc /* tp_doc */
1287 static char Unsigned_doc[] =
1288 "This is a long by another name. Use it in meta data dicts\n"
1289 "to choose the way it is stored in the meta data.";
1291 PyTypeObject UnsignedType = {
1292 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1293 sizeof(Unsigned), /* tp_basicsize */
1294 0, /* Will be filled in later */
1301 0, /* tp_as_number */
1302 0, /* tp_as_sequence */
1303 0, /* tp_as_mapping */
1307 0, /* tp_getattro */
1308 0, /* tp_setattro */
1309 0, /* tp_as_buffer */
1310 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1311 Unsigned_doc /* tp_doc */