1 /**
2 * collectd - src/pyvalues.c
3 * Copyright (C) 2009 Sven Trenkel
4 *
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:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
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.
22 *
23 * Authors:
24 * Sven Trenkel <collectd at semidefinite.de>
25 **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include "collectd.h"
32 #include "common.h"
34 #include "cpython.h"
36 #define FreeAll() do {\
37 PyMem_Free(type);\
38 PyMem_Free(plugin_instance);\
39 PyMem_Free(type_instance);\
40 PyMem_Free(plugin);\
41 PyMem_Free(host);\
42 } while(0)
44 static PyObject *cpy_common_repr(PyObject *s) {
45 PyObject *ret, *tmp;
46 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
47 static PyObject *l_host = NULL, *l_time = NULL;
48 PluginData *self = (PluginData *) s;
50 if (l_type == NULL)
51 l_type = cpy_string_to_unicode_or_bytes("(type=");
52 if (l_type_instance == NULL)
53 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
54 if (l_plugin == NULL)
55 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
56 if (l_plugin_instance == NULL)
57 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
58 if (l_host == NULL)
59 l_host = cpy_string_to_unicode_or_bytes(",host=");
60 if (l_time == NULL)
61 l_time = cpy_string_to_unicode_or_bytes(",time=");
63 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
64 return NULL;
66 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
68 CPY_STRCAT(&ret, l_type);
69 tmp = cpy_string_to_unicode_or_bytes(self->type);
70 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
71 CPY_STRCAT_AND_DEL(&ret, tmp);
73 if (self->type_instance[0] != 0) {
74 CPY_STRCAT(&ret, l_type_instance);
75 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
76 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
77 CPY_STRCAT_AND_DEL(&ret, tmp);
78 }
80 if (self->plugin[0] != 0) {
81 CPY_STRCAT(&ret, l_plugin);
82 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
83 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
84 CPY_STRCAT_AND_DEL(&ret, tmp);
85 }
87 if (self->plugin_instance[0] != 0) {
88 CPY_STRCAT(&ret, l_plugin_instance);
89 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
90 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
91 CPY_STRCAT_AND_DEL(&ret, tmp);
92 }
94 if (self->host[0] != 0) {
95 CPY_STRCAT(&ret, l_host);
96 tmp = cpy_string_to_unicode_or_bytes(self->host);
97 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
98 CPY_STRCAT_AND_DEL(&ret, tmp);
99 }
101 if (self->time != 0) {
102 CPY_STRCAT(&ret, l_time);
103 tmp = PyFloat_FromDouble(self->time);
104 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
105 CPY_STRCAT_AND_DEL(&ret, tmp);
106 }
107 return ret;
108 }
110 static char time_doc[] = "This is the Unix timestamp of the time this value was read.\n"
111 "For dispatching values this can be set to 0 which means \"now\".\n"
112 "This means the time the value is actually dispatched, not the time\n"
113 "it was set to 0.";
115 static char host_doc[] = "The hostname of the host this value was read from.\n"
116 "For dispatching this can be set to an empty string which means\n"
117 "the local hostname as defined in collectd.conf.";
119 static char type_doc[] = "The type of this value. This type has to be defined\n"
120 "in the types.db file. Attempting to set it to any other value\n"
121 "will raise a TypeError exception.\n"
122 "Assigning a type is mandatory, calling dispatch without doing\n"
123 "so will raise a RuntimeError exception.";
125 static char type_instance_doc[] = "";
127 static char plugin_doc[] = "The name of the plugin that read the data. Setting this\n"
128 "member to an empty string will insert \"python\" upon dispatching.";
130 static char plugin_instance_doc[] = "";
132 static char PluginData_doc[] = "This is an internal class that is the base for Values\n"
133 "and Notification. It is pretty useless by itself and is therefore not\n"
134 "exported to the collectd module.";
136 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
137 PluginData *self;
139 self = (PluginData *) type->tp_alloc(type, 0);
140 if (self == NULL)
141 return NULL;
143 self->time = 0;
144 self->host[0] = 0;
145 self->plugin[0] = 0;
146 self->plugin_instance[0] = 0;
147 self->type[0] = 0;
148 self->type_instance[0] = 0;
149 return (PyObject *) self;
150 }
152 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
153 PluginData *self = (PluginData *) s;
154 double time = 0;
155 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
156 static char *kwlist[] = {"type", "plugin_instance", "type_instance",
157 "plugin", "host", "time", NULL};
159 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
160 NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
161 return -1;
163 if (type && plugin_get_ds(type) == NULL) {
164 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
165 FreeAll();
166 return -1;
167 }
169 sstrncpy(self->host, host ? host : "", sizeof(self->host));
170 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
171 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->plugin_instance));
172 sstrncpy(self->type, type ? type : "", sizeof(self->type));
173 sstrncpy(self->type_instance, type_instance ? type_instance : "", sizeof(self->type_instance));
174 self->time = time;
176 FreeAll();
178 return 0;
179 }
181 static PyObject *PluginData_repr(PyObject *s) {
182 PyObject *ret;
183 static PyObject *l_closing = NULL;
185 if (l_closing == NULL)
186 l_closing = cpy_string_to_unicode_or_bytes(")");
188 if (l_closing == NULL)
189 return NULL;
191 ret = cpy_common_repr(s);
192 CPY_STRCAT(&ret, l_closing);
193 return ret;
194 }
196 static PyMemberDef PluginData_members[] = {
197 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc},
198 {NULL}
199 };
201 static PyObject *PluginData_getstring(PyObject *self, void *data) {
202 const char *value = ((char *) self) + (intptr_t) data;
204 return cpy_string_to_unicode_or_bytes(value);
205 }
207 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
208 char *old;
209 const char *new;
211 if (value == NULL) {
212 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
213 return -1;
214 }
215 Py_INCREF(value);
216 new = cpy_unicode_or_bytes_to_string(&value);
217 if (new == NULL) {
218 Py_DECREF(value);
219 return -1;
220 }
221 old = ((char *) self) + (intptr_t) data;
222 sstrncpy(old, new, DATA_MAX_NAME_LEN);
223 Py_DECREF(value);
224 return 0;
225 }
227 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
228 char *old;
229 const char *new;
231 if (value == NULL) {
232 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
233 return -1;
234 }
235 Py_INCREF(value);
236 new = cpy_unicode_or_bytes_to_string(&value);
237 if (new == NULL) {
238 Py_DECREF(value);
239 return -1;
240 }
242 if (plugin_get_ds(new) == NULL) {
243 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
244 Py_DECREF(value);
245 return -1;
246 }
248 old = ((char *) self) + (intptr_t) data;
249 sstrncpy(old, new, DATA_MAX_NAME_LEN);
250 Py_DECREF(value);
251 return 0;
252 }
254 static PyGetSetDef PluginData_getseters[] = {
255 {"host", PluginData_getstring, PluginData_setstring, host_doc, (void *) offsetof(PluginData, host)},
256 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc, (void *) offsetof(PluginData, plugin)},
257 {"plugin_instance", PluginData_getstring, PluginData_setstring, plugin_instance_doc, (void *) offsetof(PluginData, plugin_instance)},
258 {"type_instance", PluginData_getstring, PluginData_setstring, type_instance_doc, (void *) offsetof(PluginData, type_instance)},
259 {"type", PluginData_getstring, PluginData_settype, type_doc, (void *) offsetof(PluginData, type)},
260 {NULL}
261 };
263 PyTypeObject PluginDataType = {
264 CPY_INIT_TYPE
265 "collectd.PluginData", /* tp_name */
266 sizeof(PluginData), /* tp_basicsize */
267 0, /* Will be filled in later */
268 0, /* tp_dealloc */
269 0, /* tp_print */
270 0, /* tp_getattr */
271 0, /* tp_setattr */
272 0, /* tp_compare */
273 PluginData_repr, /* tp_repr */
274 0, /* tp_as_number */
275 0, /* tp_as_sequence */
276 0, /* tp_as_mapping */
277 0, /* tp_hash */
278 0, /* tp_call */
279 0, /* tp_str */
280 0, /* tp_getattro */
281 0, /* tp_setattro */
282 0, /* tp_as_buffer */
283 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
284 PluginData_doc, /* tp_doc */
285 0, /* tp_traverse */
286 0, /* tp_clear */
287 0, /* tp_richcompare */
288 0, /* tp_weaklistoffset */
289 0, /* tp_iter */
290 0, /* tp_iternext */
291 0, /* tp_methods */
292 PluginData_members, /* tp_members */
293 PluginData_getseters, /* tp_getset */
294 0, /* tp_base */
295 0, /* tp_dict */
296 0, /* tp_descr_get */
297 0, /* tp_descr_set */
298 0, /* tp_dictoffset */
299 PluginData_init, /* tp_init */
300 0, /* tp_alloc */
301 PluginData_new /* tp_new */
302 };
304 static char interval_doc[] = "The interval is the timespan in seconds between two submits for\n"
305 "the same data source. This value has to be a positive integer, so you can't\n"
306 "submit more than one value per second. If this member is set to a\n"
307 "non-positive value, the default value as specified in the config file will\n"
308 "be used (default: 10).\n"
309 "\n"
310 "If you submit values more often than the specified interval, the average\n"
311 "will be used. If you submit less values, your graphs will have gaps.";
313 static char values_doc[] = "These are the actual values that get dispatched to collectd.\n"
314 "It has to be a sequence (a tuple or list) of numbers.\n"
315 "The size of the sequence and the type of its content depend on the type\n"
316 "member in the types.db file. For more information on this read the\n"
317 "types.db man page.\n"
318 "\n"
319 "If the sequence does not have the correct size upon dispatch a RuntimeError\n"
320 "exception will be raised. If the content of the sequence is not a number,\n"
321 "a TypeError exception will be raised.";
323 static char meta_doc[] = "These are the meta data for this Value object.\n"
324 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
325 "strings. int and long objects will be dispatched as signed integers unless\n"
326 "they are between 2**63 and 2**64-1, which will result in an unsigned integer.\n"
327 "You can force one of these storage classes by using the classes\n"
328 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
329 "callback will always contain Signed or Unsigned objects.";
331 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
332 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
333 "\n"
334 "Dispatch this instance to the collectd process. The object has members\n"
335 "for each of the possible arguments for this method. For a detailed explanation\n"
336 "of these parameters see the member of the same same.\n"
337 "\n"
338 "If you do not submit a parameter the value saved in its member will be submitted.\n"
339 "If you do provide a parameter it will be used instead, without altering the member.";
341 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
342 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
343 "\n"
344 "Write this instance to a single plugin or all plugins if 'destination' is omitted.\n"
345 "This will bypass the main collectd process and all filtering and caching.\n"
346 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
347 "used instead of 'write'.\n";
349 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
351 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
352 Values *self;
354 self = (Values *) PluginData_new(type, args, kwds);
355 if (self == NULL)
356 return NULL;
358 self->values = PyList_New(0);
359 self->meta = PyDict_New();
360 self->interval = 0;
361 return (PyObject *) self;
362 }
364 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
365 Values *self = (Values *) s;
366 double interval = 0, time = 0;
367 PyObject *values = NULL, *meta = NULL, *tmp;
368 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
369 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
370 "plugin", "host", "time", "interval", "meta", NULL};
372 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
373 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
374 NULL, &plugin, NULL, &host, &time, &interval, &meta))
375 return -1;
377 if (type && plugin_get_ds(type) == NULL) {
378 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
379 FreeAll();
380 return -1;
381 }
383 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
384 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
385 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
386 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
387 sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
388 self->data.time = time;
390 FreeAll();
392 if (values == NULL) {
393 values = PyList_New(0);
394 PyErr_Clear();
395 } else {
396 Py_INCREF(values);
397 }
399 if (meta == NULL) {
400 meta = PyDict_New();
401 PyErr_Clear();
402 } else {
403 Py_INCREF(meta);
404 }
406 tmp = self->values;
407 self->values = values;
408 Py_XDECREF(tmp);
410 tmp = self->meta;
411 self->meta = meta;
412 Py_XDECREF(tmp);
414 self->interval = interval;
415 return 0;
416 }
418 static meta_data_t *cpy_build_meta(PyObject *meta) {
419 int i, s;
420 meta_data_t *m = NULL;
421 PyObject *l;
423 if ((meta == NULL) || (meta == Py_None))
424 return NULL;
426 l = PyDict_Items(meta); /* New reference. */
427 if (!l) {
428 cpy_log_exception("building meta data");
429 return NULL;
430 }
431 s = PyList_Size(l);
432 if (s <= 0) {
433 Py_XDECREF(l);
434 return NULL;
435 }
437 m = meta_data_create();
438 for (i = 0; i < s; ++i) {
439 const char *string, *keystring;
440 PyObject *key, *value, *item, *tmp;
442 item = PyList_GET_ITEM(l, i);
443 key = PyTuple_GET_ITEM(item, 0);
444 Py_INCREF(key);
445 keystring = cpy_unicode_or_bytes_to_string(&key);
446 if (!keystring) {
447 PyErr_Clear();
448 Py_XDECREF(key);
449 continue;
450 }
451 value = PyTuple_GET_ITEM(item, 1);
452 Py_INCREF(value);
453 if (value == Py_True) {
454 meta_data_add_boolean(m, keystring, 1);
455 } else if (value == Py_False) {
456 meta_data_add_boolean(m, keystring, 0);
457 } else if (PyFloat_Check(value)) {
458 meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
459 } else if (PyObject_TypeCheck(value, &SignedType)) {
460 long long int lli;
461 lli = PyLong_AsLongLong(value);
462 if (!PyErr_Occurred() && (lli == (int64_t) lli))
463 meta_data_add_signed_int(m, keystring, lli);
464 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
465 long long unsigned llu;
466 llu = PyLong_AsUnsignedLongLong(value);
467 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
468 meta_data_add_unsigned_int(m, keystring, llu);
469 } else if (PyNumber_Check(value)) {
470 long long int lli;
471 long long unsigned llu;
472 tmp = PyNumber_Long(value);
473 lli = PyLong_AsLongLong(tmp);
474 if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
475 meta_data_add_signed_int(m, keystring, lli);
476 } else {
477 PyErr_Clear();
478 llu = PyLong_AsUnsignedLongLong(tmp);
479 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
480 meta_data_add_unsigned_int(m, keystring, llu);
481 }
482 Py_XDECREF(tmp);
483 } else {
484 string = cpy_unicode_or_bytes_to_string(&value);
485 if (string) {
486 meta_data_add_string(m, keystring, string);
487 } else {
488 PyErr_Clear();
489 tmp = PyObject_Str(value);
490 string = cpy_unicode_or_bytes_to_string(&tmp);
491 if (string)
492 meta_data_add_string(m, keystring, string);
493 Py_XDECREF(tmp);
494 }
495 }
496 if (PyErr_Occurred())
497 cpy_log_exception("building meta data");
498 Py_XDECREF(value);
499 Py_DECREF(key);
500 }
501 Py_XDECREF(l);
502 return m;
503 }
505 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
506 int ret;
507 const data_set_t *ds;
508 size_t size, i;
509 value_t *value;
510 value_list_t value_list = VALUE_LIST_INIT;
511 PyObject *values = self->values, *meta = self->meta;
512 double time = self->data.time, interval = self->interval;
513 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
515 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
516 "plugin", "host", "time", "interval", "meta", NULL};
517 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
518 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
519 NULL, &plugin, NULL, &host, &time, &interval, &meta))
520 return NULL;
522 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
523 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
524 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
525 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
526 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
527 FreeAll();
528 if (value_list.type[0] == 0) {
529 PyErr_SetString(PyExc_RuntimeError, "type not set");
530 FreeAll();
531 return NULL;
532 }
533 ds = plugin_get_ds(value_list.type);
534 if (ds == NULL) {
535 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
536 return NULL;
537 }
538 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
539 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
540 return NULL;
541 }
542 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
543 PyErr_Format(PyExc_TypeError, "meta must be a dict");
544 return NULL;
545 }
546 size = (size_t) PySequence_Length(values);
547 if (size != ds->ds_num) {
548 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
549 return NULL;
550 }
551 value = calloc(size, sizeof(*value));
552 for (i = 0; i < size; ++i) {
553 PyObject *item, *num;
554 item = PySequence_Fast_GET_ITEM(values, (int) i); /* Borrowed reference. */
555 if (ds->ds->type == DS_TYPE_COUNTER) {
556 num = PyNumber_Long(item); /* New reference. */
557 if (num != NULL) {
558 value[i].counter = PyLong_AsUnsignedLongLong(num);
559 Py_XDECREF(num);
560 }
561 } else if (ds->ds->type == DS_TYPE_GAUGE) {
562 num = PyNumber_Float(item); /* New reference. */
563 if (num != NULL) {
564 value[i].gauge = PyFloat_AsDouble(num);
565 Py_XDECREF(num);
566 }
567 } else if (ds->ds->type == DS_TYPE_DERIVE) {
568 /* This might overflow without raising an exception.
569 * Not much we can do about it */
570 num = PyNumber_Long(item); /* New reference. */
571 if (num != NULL) {
572 value[i].derive = PyLong_AsLongLong(num);
573 Py_XDECREF(num);
574 }
575 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
576 /* This might overflow without raising an exception.
577 * Not much we can do about it */
578 num = PyNumber_Long(item); /* New reference. */
579 if (num != NULL) {
580 value[i].absolute = PyLong_AsUnsignedLongLong(num);
581 Py_XDECREF(num);
582 }
583 } else {
584 free(value);
585 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
586 return NULL;
587 }
588 if (PyErr_Occurred() != NULL) {
589 free(value);
590 return NULL;
591 }
592 }
593 value_list.values = value;
594 value_list.meta = cpy_build_meta(meta);
595 value_list.values_len = size;
596 value_list.time = DOUBLE_TO_CDTIME_T(time);
597 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
598 if (value_list.host[0] == 0)
599 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
600 if (value_list.plugin[0] == 0)
601 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
602 Py_BEGIN_ALLOW_THREADS;
603 ret = plugin_dispatch_values(&value_list);
604 Py_END_ALLOW_THREADS;
605 meta_data_destroy(value_list.meta);
606 free(value);
607 if (ret != 0) {
608 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
609 return NULL;
610 }
611 Py_RETURN_NONE;
612 }
614 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
615 int ret;
616 const data_set_t *ds;
617 size_t size, i;
618 value_t *value;
619 value_list_t value_list = VALUE_LIST_INIT;
620 PyObject *values = self->values, *meta = self->meta;
621 double time = self->data.time, interval = self->interval;
622 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
624 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
625 "plugin", "host", "time", "interval", "meta", NULL};
626 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
627 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
628 NULL, &plugin, NULL, &host, &time, &interval, &meta))
629 return NULL;
631 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
632 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
633 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
634 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
635 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
636 FreeAll();
637 if (value_list.type[0] == 0) {
638 PyErr_SetString(PyExc_RuntimeError, "type not set");
639 return NULL;
640 }
641 ds = plugin_get_ds(value_list.type);
642 if (ds == NULL) {
643 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
644 return NULL;
645 }
646 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
647 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
648 return NULL;
649 }
650 size = (size_t) PySequence_Length(values);
651 if (size != ds->ds_num) {
652 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu", value_list.type, ds->ds_num, size);
653 return NULL;
654 }
655 value = calloc(size, sizeof(*value));
656 for (i = 0; i < size; ++i) {
657 PyObject *item, *num;
658 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
659 if (ds->ds->type == DS_TYPE_COUNTER) {
660 num = PyNumber_Long(item); /* New reference. */
661 if (num != NULL) {
662 value[i].counter = PyLong_AsUnsignedLongLong(num);
663 Py_XDECREF(num);
664 }
665 } else if (ds->ds->type == DS_TYPE_GAUGE) {
666 num = PyNumber_Float(item); /* New reference. */
667 if (num != NULL) {
668 value[i].gauge = PyFloat_AsDouble(num);
669 Py_XDECREF(num);
670 }
671 } else if (ds->ds->type == DS_TYPE_DERIVE) {
672 /* This might overflow without raising an exception.
673 * Not much we can do about it */
674 num = PyNumber_Long(item); /* New reference. */
675 if (num != NULL) {
676 value[i].derive = PyLong_AsLongLong(num);
677 Py_XDECREF(num);
678 }
679 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
680 /* This might overflow without raising an exception.
681 * Not much we can do about it */
682 num = PyNumber_Long(item); /* New reference. */
683 if (num != NULL) {
684 value[i].absolute = PyLong_AsUnsignedLongLong(num);
685 Py_XDECREF(num);
686 }
687 } else {
688 free(value);
689 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
690 return NULL;
691 }
692 if (PyErr_Occurred() != NULL) {
693 free(value);
694 return NULL;
695 }
696 }
697 value_list.values = value;
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 value_list.meta = cpy_build_meta(meta);
702 if (value_list.host[0] == 0)
703 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
704 if (value_list.plugin[0] == 0)
705 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
706 Py_BEGIN_ALLOW_THREADS;
707 ret = plugin_write(dest, NULL, &value_list);
708 Py_END_ALLOW_THREADS;
709 meta_data_destroy(value_list.meta);
710 free(value);
711 if (ret != 0) {
712 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
713 return NULL;
714 }
715 Py_RETURN_NONE;
716 }
718 static PyObject *Values_repr(PyObject *s) {
719 PyObject *ret, *tmp;
720 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
721 Values *self = (Values *) s;
723 if (l_interval == NULL)
724 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
725 if (l_values == NULL)
726 l_values = cpy_string_to_unicode_or_bytes(",values=");
727 if (l_meta == NULL)
728 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
729 if (l_closing == NULL)
730 l_closing = cpy_string_to_unicode_or_bytes(")");
732 if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
733 return NULL;
735 ret = cpy_common_repr(s);
736 if (self->interval != 0) {
737 CPY_STRCAT(&ret, l_interval);
738 tmp = PyFloat_FromDouble(self->interval);
739 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
740 CPY_STRCAT_AND_DEL(&ret, tmp);
741 }
742 if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
743 CPY_STRCAT(&ret, l_values);
744 tmp = PyObject_Repr(self->values);
745 CPY_STRCAT_AND_DEL(&ret, tmp);
746 }
747 if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
748 CPY_STRCAT(&ret, l_meta);
749 tmp = PyObject_Repr(self->meta);
750 CPY_STRCAT_AND_DEL(&ret, tmp);
751 }
752 CPY_STRCAT(&ret, l_closing);
753 return ret;
754 }
756 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
757 Values *v = (Values *) self;
758 Py_VISIT(v->values);
759 Py_VISIT(v->meta);
760 return 0;
761 }
763 static int Values_clear(PyObject *self) {
764 Values *v = (Values *) self;
765 Py_CLEAR(v->values);
766 Py_CLEAR(v->meta);
767 return 0;
768 }
770 static void Values_dealloc(PyObject *self) {
771 Values_clear(self);
772 self->ob_type->tp_free(self);
773 }
775 static PyMemberDef Values_members[] = {
776 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
777 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
778 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
779 {NULL}
780 };
782 static PyMethodDef Values_methods[] = {
783 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
784 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
785 {NULL}
786 };
788 PyTypeObject ValuesType = {
789 CPY_INIT_TYPE
790 "collectd.Values", /* tp_name */
791 sizeof(Values), /* tp_basicsize */
792 0, /* Will be filled in later */
793 Values_dealloc, /* tp_dealloc */
794 0, /* tp_print */
795 0, /* tp_getattr */
796 0, /* tp_setattr */
797 0, /* tp_compare */
798 Values_repr, /* tp_repr */
799 0, /* tp_as_number */
800 0, /* tp_as_sequence */
801 0, /* tp_as_mapping */
802 0, /* tp_hash */
803 0, /* tp_call */
804 0, /* tp_str */
805 0, /* tp_getattro */
806 0, /* tp_setattro */
807 0, /* tp_as_buffer */
808 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
809 Values_doc, /* tp_doc */
810 Values_traverse, /* tp_traverse */
811 Values_clear, /* tp_clear */
812 0, /* tp_richcompare */
813 0, /* tp_weaklistoffset */
814 0, /* tp_iter */
815 0, /* tp_iternext */
816 Values_methods, /* tp_methods */
817 Values_members, /* tp_members */
818 0, /* tp_getset */
819 0, /* tp_base */
820 0, /* tp_dict */
821 0, /* tp_descr_get */
822 0, /* tp_descr_set */
823 0, /* tp_dictoffset */
824 Values_init, /* tp_init */
825 0, /* tp_alloc */
826 Values_new /* tp_new */
827 };
829 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
830 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
832 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
834 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
835 "It can be used to notify other plugins about bad stuff happening. It works\n"
836 "similar to Values but has a severity and a message instead of interval\n"
837 "and time.\n"
838 "Notifications can be dispatched at any time and can be received with register_notification.";
840 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
841 Notification *self = (Notification *) s;
842 int severity = 0;
843 double time = 0;
844 char *message = NULL;
845 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
846 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
847 "plugin", "host", "time", "severity", NULL};
849 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
850 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
851 NULL, &plugin, NULL, &host, &time, &severity))
852 return -1;
854 if (type && plugin_get_ds(type) == NULL) {
855 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
856 FreeAll();
857 PyMem_Free(message);
858 return -1;
859 }
861 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
862 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
863 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
864 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
865 sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
866 sstrncpy(self->message, message ? message : "", sizeof(self->message));
867 self->data.time = time;
868 self->severity = severity;
870 FreeAll();
871 PyMem_Free(message);
872 return 0;
873 }
875 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
876 int ret;
877 const data_set_t *ds;
878 notification_t notification;
879 double t = self->data.time;
880 int severity = self->severity;
881 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
882 char *message = NULL;
884 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
885 "plugin", "host", "time", "severity", NULL};
886 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
887 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
888 NULL, &plugin, NULL, &host, &t, &severity))
889 return NULL;
891 notification.time = DOUBLE_TO_CDTIME_T(t);
892 notification.severity = severity;
893 sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
894 sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
895 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
896 sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
897 sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
898 sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
899 notification.meta = NULL;
900 FreeAll();
901 PyMem_Free(message);
903 if (notification.type[0] == 0) {
904 PyErr_SetString(PyExc_RuntimeError, "type not set");
905 return NULL;
906 }
907 ds = plugin_get_ds(notification.type);
908 if (ds == NULL) {
909 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
910 return NULL;
911 }
913 if (notification.time == 0)
914 notification.time = cdtime();
915 if (notification.host[0] == 0)
916 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
917 if (notification.plugin[0] == 0)
918 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
919 Py_BEGIN_ALLOW_THREADS;
920 ret = plugin_dispatch_notification(¬ification);
921 Py_END_ALLOW_THREADS;
922 if (ret != 0) {
923 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
924 return NULL;
925 }
926 Py_RETURN_NONE;
927 }
929 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
930 Notification *self;
932 self = (Notification *) PluginData_new(type, args, kwds);
933 if (self == NULL)
934 return NULL;
936 self->message[0] = 0;
937 self->severity = 0;
938 return (PyObject *) self;
939 }
941 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
942 char *old;
943 const char *new;
945 if (value == NULL) {
946 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
947 return -1;
948 }
949 Py_INCREF(value);
950 new = cpy_unicode_or_bytes_to_string(&value);
951 if (new == NULL) {
952 Py_DECREF(value);
953 return -1;
954 }
955 old = ((char *) self) + (intptr_t) data;
956 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
957 Py_DECREF(value);
958 return 0;
959 }
961 static PyObject *Notification_repr(PyObject *s) {
962 PyObject *ret, *tmp;
963 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
964 Notification *self = (Notification *) s;
966 if (l_severity == NULL)
967 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
968 if (l_message == NULL)
969 l_message = cpy_string_to_unicode_or_bytes(",message=");
970 if (l_closing == NULL)
971 l_closing = cpy_string_to_unicode_or_bytes(")");
973 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
974 return NULL;
976 ret = cpy_common_repr(s);
977 if (self->severity != 0) {
978 CPY_STRCAT(&ret, l_severity);
979 tmp = PyInt_FromLong(self->severity);
980 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
981 CPY_STRCAT_AND_DEL(&ret, tmp);
982 }
983 if (self->message[0] != 0) {
984 CPY_STRCAT(&ret, l_message);
985 tmp = cpy_string_to_unicode_or_bytes(self->message);
986 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
987 CPY_STRCAT_AND_DEL(&ret, tmp);
988 }
989 CPY_STRCAT(&ret, l_closing);
990 return ret;
991 }
993 static PyMethodDef Notification_methods[] = {
994 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
995 {NULL}
996 };
998 static PyMemberDef Notification_members[] = {
999 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1000 {NULL}
1001 };
1003 static PyGetSetDef Notification_getseters[] = {
1004 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1005 {NULL}
1006 };
1008 PyTypeObject NotificationType = {
1009 CPY_INIT_TYPE
1010 "collectd.Notification", /* tp_name */
1011 sizeof(Notification), /* tp_basicsize */
1012 0, /* Will be filled in later */
1013 0, /* tp_dealloc */
1014 0, /* tp_print */
1015 0, /* tp_getattr */
1016 0, /* tp_setattr */
1017 0, /* tp_compare */
1018 Notification_repr, /* tp_repr */
1019 0, /* tp_as_number */
1020 0, /* tp_as_sequence */
1021 0, /* tp_as_mapping */
1022 0, /* tp_hash */
1023 0, /* tp_call */
1024 0, /* tp_str */
1025 0, /* tp_getattro */
1026 0, /* tp_setattro */
1027 0, /* tp_as_buffer */
1028 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1029 Notification_doc, /* tp_doc */
1030 0, /* tp_traverse */
1031 0, /* tp_clear */
1032 0, /* tp_richcompare */
1033 0, /* tp_weaklistoffset */
1034 0, /* tp_iter */
1035 0, /* tp_iternext */
1036 Notification_methods, /* tp_methods */
1037 Notification_members, /* tp_members */
1038 Notification_getseters, /* tp_getset */
1039 0, /* tp_base */
1040 0, /* tp_dict */
1041 0, /* tp_descr_get */
1042 0, /* tp_descr_set */
1043 0, /* tp_dictoffset */
1044 Notification_init, /* tp_init */
1045 0, /* tp_alloc */
1046 Notification_new /* tp_new */
1047 };
1049 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1050 "to choose the way it is stored in the meta data.";
1052 PyTypeObject SignedType = {
1053 CPY_INIT_TYPE
1054 "collectd.Signed", /* tp_name */
1055 sizeof(Signed), /* tp_basicsize */
1056 0, /* Will be filled in later */
1057 0, /* tp_dealloc */
1058 0, /* tp_print */
1059 0, /* tp_getattr */
1060 0, /* tp_setattr */
1061 0, /* tp_compare */
1062 0, /* tp_repr */
1063 0, /* tp_as_number */
1064 0, /* tp_as_sequence */
1065 0, /* tp_as_mapping */
1066 0, /* tp_hash */
1067 0, /* tp_call */
1068 0, /* tp_str */
1069 0, /* tp_getattro */
1070 0, /* tp_setattro */
1071 0, /* tp_as_buffer */
1072 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1073 Signed_doc /* tp_doc */
1074 };
1076 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1077 "to choose the way it is stored in the meta data.";
1079 PyTypeObject UnsignedType = {
1080 CPY_INIT_TYPE
1081 "collectd.Unsigned", /* tp_name */
1082 sizeof(Unsigned), /* tp_basicsize */
1083 0, /* Will be filled in later */
1084 0, /* tp_dealloc */
1085 0, /* tp_print */
1086 0, /* tp_getattr */
1087 0, /* tp_setattr */
1088 0, /* tp_compare */
1089 0, /* tp_repr */
1090 0, /* tp_as_number */
1091 0, /* tp_as_sequence */
1092 0, /* tp_as_mapping */
1093 0, /* tp_hash */
1094 0, /* tp_call */
1095 0, /* tp_str */
1096 0, /* tp_getattro */
1097 0, /* tp_setattro */
1098 0, /* tp_as_buffer */
1099 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1100 Unsigned_doc /* tp_doc */
1101 };