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