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 }
431 s = PyList_Size(l);
432 if (s < 0)
433 return NULL;
435 m = meta_data_create();
436 for (i = 0; i < s; ++i) {
437 const char *string, *keystring;
438 PyObject *key, *value, *item, *tmp;
440 item = PyList_GET_ITEM(l, i);
441 key = PyTuple_GET_ITEM(item, 0);
442 Py_INCREF(key);
443 keystring = cpy_unicode_or_bytes_to_string(&key);
444 if (!keystring) {
445 PyErr_Clear();
446 Py_XDECREF(key);
447 continue;
448 }
449 value = PyTuple_GET_ITEM(item, 1);
450 Py_INCREF(value);
451 if (value == Py_True) {
452 meta_data_add_boolean(m, keystring, 1);
453 } else if (value == Py_False) {
454 meta_data_add_boolean(m, keystring, 0);
455 } else if (PyFloat_Check(value)) {
456 meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
457 } else if (PyObject_TypeCheck(value, &SignedType)) {
458 long long int lli;
459 lli = PyLong_AsLongLong(value);
460 if (!PyErr_Occurred() && (lli == (int64_t) lli))
461 meta_data_add_signed_int(m, keystring, lli);
462 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
463 long long unsigned llu;
464 llu = PyLong_AsUnsignedLongLong(value);
465 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
466 meta_data_add_unsigned_int(m, keystring, llu);
467 } else if (PyNumber_Check(value)) {
468 long long int lli;
469 long long unsigned llu;
470 tmp = PyNumber_Long(value);
471 lli = PyLong_AsLongLong(tmp);
472 if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
473 meta_data_add_signed_int(m, keystring, lli);
474 } else {
475 PyErr_Clear();
476 llu = PyLong_AsUnsignedLongLong(tmp);
477 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
478 meta_data_add_unsigned_int(m, keystring, llu);
479 }
480 Py_XDECREF(tmp);
481 } else {
482 string = cpy_unicode_or_bytes_to_string(&value);
483 if (string) {
484 meta_data_add_string(m, keystring, string);
485 } else {
486 PyErr_Clear();
487 tmp = PyObject_Str(value);
488 string = cpy_unicode_or_bytes_to_string(&tmp);
489 if (string)
490 meta_data_add_string(m, keystring, string);
491 Py_XDECREF(tmp);
492 }
493 }
494 if (PyErr_Occurred())
495 cpy_log_exception("building meta data");
496 Py_XDECREF(value);
497 Py_DECREF(key);
498 }
499 Py_XDECREF(l);
500 return m;
501 }
503 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
504 int i, ret;
505 const data_set_t *ds;
506 int size;
507 value_t *value;
508 value_list_t value_list = VALUE_LIST_INIT;
509 PyObject *values = self->values, *meta = self->meta;
510 double time = self->data.time, interval = self->interval;
511 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
513 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
514 "plugin", "host", "time", "interval", "meta", NULL};
515 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist,
516 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
517 NULL, &plugin, NULL, &host, &time, &interval, &meta))
518 return NULL;
520 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
521 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
522 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
523 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
524 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
525 FreeAll();
526 if (value_list.type[0] == 0) {
527 PyErr_SetString(PyExc_RuntimeError, "type not set");
528 FreeAll();
529 return NULL;
530 }
531 ds = plugin_get_ds(value_list.type);
532 if (ds == NULL) {
533 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
534 return NULL;
535 }
536 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
537 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
538 return NULL;
539 }
540 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
541 PyErr_Format(PyExc_TypeError, "meta must be a dict");
542 return NULL;
543 }
544 size = (int) PySequence_Length(values);
545 if (size != ds->ds_num) {
546 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
547 return NULL;
548 }
549 value = malloc(size * sizeof(*value));
550 for (i = 0; i < size; ++i) {
551 PyObject *item, *num;
552 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
553 if (ds->ds->type == DS_TYPE_COUNTER) {
554 num = PyNumber_Long(item); /* New reference. */
555 if (num != NULL) {
556 value[i].counter = PyLong_AsUnsignedLongLong(num);
557 Py_XDECREF(num);
558 }
559 } else if (ds->ds->type == DS_TYPE_GAUGE) {
560 num = PyNumber_Float(item); /* New reference. */
561 if (num != NULL) {
562 value[i].gauge = PyFloat_AsDouble(num);
563 Py_XDECREF(num);
564 }
565 } else if (ds->ds->type == DS_TYPE_DERIVE) {
566 /* This might overflow without raising an exception.
567 * Not much we can do about it */
568 num = PyNumber_Long(item); /* New reference. */
569 if (num != NULL) {
570 value[i].derive = PyLong_AsLongLong(num);
571 Py_XDECREF(num);
572 }
573 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
574 /* This might overflow without raising an exception.
575 * Not much we can do about it */
576 num = PyNumber_Long(item); /* New reference. */
577 if (num != NULL) {
578 value[i].absolute = PyLong_AsUnsignedLongLong(num);
579 Py_XDECREF(num);
580 }
581 } else {
582 free(value);
583 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
584 return NULL;
585 }
586 if (PyErr_Occurred() != NULL) {
587 free(value);
588 return NULL;
589 }
590 }
591 value_list.values = value;
592 value_list.meta = cpy_build_meta(meta);
593 value_list.values_len = size;
594 value_list.time = DOUBLE_TO_CDTIME_T(time);
595 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
596 if (value_list.host[0] == 0)
597 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
598 if (value_list.plugin[0] == 0)
599 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
600 Py_BEGIN_ALLOW_THREADS;
601 ret = plugin_dispatch_values(&value_list);
602 Py_END_ALLOW_THREADS;
603 meta_data_destroy(value_list.meta);
604 free(value);
605 if (ret != 0) {
606 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
607 return NULL;
608 }
609 Py_RETURN_NONE;
610 }
612 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
613 int i, ret;
614 const data_set_t *ds;
615 int size;
616 value_t *value;
617 value_list_t value_list = VALUE_LIST_INIT;
618 PyObject *values = self->values, *meta = self->meta;
619 double time = self->data.time, interval = self->interval;
620 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL;
622 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
623 "plugin", "host", "time", "interval", "meta", NULL};
624 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest,
625 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
626 NULL, &plugin, NULL, &host, &time, &interval, &meta))
627 return NULL;
629 sstrncpy(value_list.host, host ? host : self->data.host, sizeof(value_list.host));
630 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin, sizeof(value_list.plugin));
631 sstrncpy(value_list.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(value_list.plugin_instance));
632 sstrncpy(value_list.type, type ? type : self->data.type, sizeof(value_list.type));
633 sstrncpy(value_list.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(value_list.type_instance));
634 FreeAll();
635 if (value_list.type[0] == 0) {
636 PyErr_SetString(PyExc_RuntimeError, "type not set");
637 return NULL;
638 }
639 ds = plugin_get_ds(value_list.type);
640 if (ds == NULL) {
641 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
642 return NULL;
643 }
644 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
645 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
646 return NULL;
647 }
648 size = (int) PySequence_Length(values);
649 if (size != ds->ds_num) {
650 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", value_list.type, ds->ds_num, size);
651 return NULL;
652 }
653 value = malloc(size * sizeof(*value));
654 for (i = 0; i < size; ++i) {
655 PyObject *item, *num;
656 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
657 if (ds->ds->type == DS_TYPE_COUNTER) {
658 num = PyNumber_Long(item); /* New reference. */
659 if (num != NULL) {
660 value[i].counter = PyLong_AsUnsignedLongLong(num);
661 Py_XDECREF(num);
662 }
663 } else if (ds->ds->type == DS_TYPE_GAUGE) {
664 num = PyNumber_Float(item); /* New reference. */
665 if (num != NULL) {
666 value[i].gauge = PyFloat_AsDouble(num);
667 Py_XDECREF(num);
668 }
669 } else if (ds->ds->type == DS_TYPE_DERIVE) {
670 /* This might overflow without raising an exception.
671 * Not much we can do about it */
672 num = PyNumber_Long(item); /* New reference. */
673 if (num != NULL) {
674 value[i].derive = PyLong_AsLongLong(num);
675 Py_XDECREF(num);
676 }
677 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
678 /* This might overflow without raising an exception.
679 * Not much we can do about it */
680 num = PyNumber_Long(item); /* New reference. */
681 if (num != NULL) {
682 value[i].absolute = PyLong_AsUnsignedLongLong(num);
683 Py_XDECREF(num);
684 }
685 } else {
686 free(value);
687 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, value_list.type);
688 return NULL;
689 }
690 if (PyErr_Occurred() != NULL) {
691 free(value);
692 return NULL;
693 }
694 }
695 value_list.values = value;
696 value_list.values_len = size;
697 value_list.time = DOUBLE_TO_CDTIME_T(time);
698 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
699 value_list.meta = cpy_build_meta(meta);;
700 if (value_list.host[0] == 0)
701 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
702 if (value_list.plugin[0] == 0)
703 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
704 Py_BEGIN_ALLOW_THREADS;
705 ret = plugin_write(dest, NULL, &value_list);
706 Py_END_ALLOW_THREADS;
707 meta_data_destroy(value_list.meta);
708 free(value);
709 if (ret != 0) {
710 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
711 return NULL;
712 }
713 Py_RETURN_NONE;
714 }
716 static PyObject *Values_repr(PyObject *s) {
717 PyObject *ret, *tmp;
718 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
719 Values *self = (Values *) s;
721 if (l_interval == NULL)
722 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
723 if (l_values == NULL)
724 l_values = cpy_string_to_unicode_or_bytes(",values=");
725 if (l_meta == NULL)
726 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
727 if (l_closing == NULL)
728 l_closing = cpy_string_to_unicode_or_bytes(")");
730 if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
731 return NULL;
733 ret = cpy_common_repr(s);
734 if (self->interval != 0) {
735 CPY_STRCAT(&ret, l_interval);
736 tmp = PyFloat_FromDouble(self->interval);
737 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
738 CPY_STRCAT_AND_DEL(&ret, tmp);
739 }
740 if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
741 CPY_STRCAT(&ret, l_values);
742 tmp = PyObject_Repr(self->values);
743 CPY_STRCAT_AND_DEL(&ret, tmp);
744 }
745 if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
746 CPY_STRCAT(&ret, l_meta);
747 tmp = PyObject_Repr(self->meta);
748 CPY_STRCAT_AND_DEL(&ret, tmp);
749 }
750 CPY_STRCAT(&ret, l_closing);
751 return ret;
752 }
754 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
755 Values *v = (Values *) self;
756 Py_VISIT(v->values);
757 Py_VISIT(v->meta);
758 return 0;
759 }
761 static int Values_clear(PyObject *self) {
762 Values *v = (Values *) self;
763 Py_CLEAR(v->values);
764 Py_CLEAR(v->meta);
765 return 0;
766 }
768 static void Values_dealloc(PyObject *self) {
769 Values_clear(self);
770 self->ob_type->tp_free(self);
771 }
773 static PyMemberDef Values_members[] = {
774 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
775 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
776 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
777 {NULL}
778 };
780 static PyMethodDef Values_methods[] = {
781 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
782 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
783 {NULL}
784 };
786 PyTypeObject ValuesType = {
787 CPY_INIT_TYPE
788 "collectd.Values", /* tp_name */
789 sizeof(Values), /* tp_basicsize */
790 0, /* Will be filled in later */
791 Values_dealloc, /* tp_dealloc */
792 0, /* tp_print */
793 0, /* tp_getattr */
794 0, /* tp_setattr */
795 0, /* tp_compare */
796 Values_repr, /* tp_repr */
797 0, /* tp_as_number */
798 0, /* tp_as_sequence */
799 0, /* tp_as_mapping */
800 0, /* tp_hash */
801 0, /* tp_call */
802 0, /* tp_str */
803 0, /* tp_getattro */
804 0, /* tp_setattro */
805 0, /* tp_as_buffer */
806 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
807 Values_doc, /* tp_doc */
808 Values_traverse, /* tp_traverse */
809 Values_clear, /* tp_clear */
810 0, /* tp_richcompare */
811 0, /* tp_weaklistoffset */
812 0, /* tp_iter */
813 0, /* tp_iternext */
814 Values_methods, /* tp_methods */
815 Values_members, /* tp_members */
816 0, /* tp_getset */
817 0, /* tp_base */
818 0, /* tp_dict */
819 0, /* tp_descr_get */
820 0, /* tp_descr_set */
821 0, /* tp_dictoffset */
822 Values_init, /* tp_init */
823 0, /* tp_alloc */
824 Values_new /* tp_new */
825 };
827 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
828 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
830 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
832 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
833 "It can be used to notify other plugins about bad stuff happening. It works\n"
834 "similar to Values but has a severity and a message instead of interval\n"
835 "and time.\n"
836 "Notifications can be dispatched at any time and can be received with register_notification.";
838 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
839 Notification *self = (Notification *) s;
840 int severity = 0;
841 double time = 0;
842 char *message = NULL;
843 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL;
844 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
845 "plugin", "host", "time", "severity", NULL};
847 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
848 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
849 NULL, &plugin, NULL, &host, &time, &severity))
850 return -1;
852 if (type && plugin_get_ds(type) == NULL) {
853 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
854 FreeAll();
855 PyMem_Free(message);
856 return -1;
857 }
859 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
860 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
861 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "", sizeof(self->data.plugin_instance));
862 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
863 sstrncpy(self->data.type_instance, type_instance ? type_instance : "", sizeof(self->data.type_instance));
864 sstrncpy(self->message, message ? message : "", sizeof(self->message));
865 self->data.time = time;
866 self->severity = severity;
868 FreeAll();
869 PyMem_Free(message);
870 return 0;
871 }
873 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
874 int ret;
875 const data_set_t *ds;
876 notification_t notification;
877 double t = self->data.time;
878 int severity = self->severity;
879 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL;
880 char *message = NULL;
882 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
883 "plugin", "host", "time", "severity", NULL};
884 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
885 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
886 NULL, &plugin, NULL, &host, &t, &severity))
887 return NULL;
889 notification.time = DOUBLE_TO_CDTIME_T(t);
890 notification.severity = severity;
891 sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message));
892 sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host));
893 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin, sizeof(notification.plugin));
894 sstrncpy(notification.plugin_instance, plugin_instance ? plugin_instance : self->data.plugin_instance, sizeof(notification.plugin_instance));
895 sstrncpy(notification.type, type ? type : self->data.type, sizeof(notification.type));
896 sstrncpy(notification.type_instance, type_instance ? type_instance : self->data.type_instance, sizeof(notification.type_instance));
897 notification.meta = NULL;
898 FreeAll();
899 PyMem_Free(message);
901 if (notification.type[0] == 0) {
902 PyErr_SetString(PyExc_RuntimeError, "type not set");
903 return NULL;
904 }
905 ds = plugin_get_ds(notification.type);
906 if (ds == NULL) {
907 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
908 return NULL;
909 }
911 if (notification.time == 0)
912 notification.time = cdtime();
913 if (notification.host[0] == 0)
914 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
915 if (notification.plugin[0] == 0)
916 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
917 Py_BEGIN_ALLOW_THREADS;
918 ret = plugin_dispatch_notification(¬ification);
919 Py_END_ALLOW_THREADS;
920 if (ret != 0) {
921 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
922 return NULL;
923 }
924 Py_RETURN_NONE;
925 }
927 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
928 Notification *self;
930 self = (Notification *) PluginData_new(type, args, kwds);
931 if (self == NULL)
932 return NULL;
934 self->message[0] = 0;
935 self->severity = 0;
936 return (PyObject *) self;
937 }
939 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
940 char *old;
941 const char *new;
943 if (value == NULL) {
944 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
945 return -1;
946 }
947 Py_INCREF(value);
948 new = cpy_unicode_or_bytes_to_string(&value);
949 if (new == NULL) {
950 Py_DECREF(value);
951 return -1;
952 }
953 old = ((char *) self) + (intptr_t) data;
954 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
955 Py_DECREF(value);
956 return 0;
957 }
959 static PyObject *Notification_repr(PyObject *s) {
960 PyObject *ret, *tmp;
961 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
962 Notification *self = (Notification *) s;
964 if (l_severity == NULL)
965 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
966 if (l_message == NULL)
967 l_message = cpy_string_to_unicode_or_bytes(",message=");
968 if (l_closing == NULL)
969 l_closing = cpy_string_to_unicode_or_bytes(")");
971 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
972 return NULL;
974 ret = cpy_common_repr(s);
975 if (self->severity != 0) {
976 CPY_STRCAT(&ret, l_severity);
977 tmp = PyInt_FromLong(self->severity);
978 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
979 CPY_STRCAT_AND_DEL(&ret, tmp);
980 }
981 if (self->message[0] != 0) {
982 CPY_STRCAT(&ret, l_message);
983 tmp = cpy_string_to_unicode_or_bytes(self->message);
984 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
985 CPY_STRCAT_AND_DEL(&ret, tmp);
986 }
987 CPY_STRCAT(&ret, l_closing);
988 return ret;
989 }
991 static PyMethodDef Notification_methods[] = {
992 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
993 {NULL}
994 };
996 static PyMemberDef Notification_members[] = {
997 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
998 {NULL}
999 };
1001 static PyGetSetDef Notification_getseters[] = {
1002 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
1003 {NULL}
1004 };
1006 PyTypeObject NotificationType = {
1007 CPY_INIT_TYPE
1008 "collectd.Notification", /* tp_name */
1009 sizeof(Notification), /* tp_basicsize */
1010 0, /* Will be filled in later */
1011 0, /* tp_dealloc */
1012 0, /* tp_print */
1013 0, /* tp_getattr */
1014 0, /* tp_setattr */
1015 0, /* tp_compare */
1016 Notification_repr, /* tp_repr */
1017 0, /* tp_as_number */
1018 0, /* tp_as_sequence */
1019 0, /* tp_as_mapping */
1020 0, /* tp_hash */
1021 0, /* tp_call */
1022 0, /* tp_str */
1023 0, /* tp_getattro */
1024 0, /* tp_setattro */
1025 0, /* tp_as_buffer */
1026 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1027 Notification_doc, /* tp_doc */
1028 0, /* tp_traverse */
1029 0, /* tp_clear */
1030 0, /* tp_richcompare */
1031 0, /* tp_weaklistoffset */
1032 0, /* tp_iter */
1033 0, /* tp_iternext */
1034 Notification_methods, /* tp_methods */
1035 Notification_members, /* tp_members */
1036 Notification_getseters, /* tp_getset */
1037 0, /* tp_base */
1038 0, /* tp_dict */
1039 0, /* tp_descr_get */
1040 0, /* tp_descr_set */
1041 0, /* tp_dictoffset */
1042 Notification_init, /* tp_init */
1043 0, /* tp_alloc */
1044 Notification_new /* tp_new */
1045 };
1047 static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1048 "to choose the way it is stored in the meta data.";
1050 PyTypeObject SignedType = {
1051 CPY_INIT_TYPE
1052 "collectd.Signed", /* tp_name */
1053 sizeof(Signed), /* tp_basicsize */
1054 0, /* Will be filled in later */
1055 0, /* tp_dealloc */
1056 0, /* tp_print */
1057 0, /* tp_getattr */
1058 0, /* tp_setattr */
1059 0, /* tp_compare */
1060 0, /* tp_repr */
1061 0, /* tp_as_number */
1062 0, /* tp_as_sequence */
1063 0, /* tp_as_mapping */
1064 0, /* tp_hash */
1065 0, /* tp_call */
1066 0, /* tp_str */
1067 0, /* tp_getattro */
1068 0, /* tp_setattro */
1069 0, /* tp_as_buffer */
1070 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1071 Signed_doc /* tp_doc */
1072 };
1074 static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
1075 "to choose the way it is stored in the meta data.";
1077 PyTypeObject UnsignedType = {
1078 CPY_INIT_TYPE
1079 "collectd.Unsigned", /* tp_name */
1080 sizeof(Unsigned), /* tp_basicsize */
1081 0, /* Will be filled in later */
1082 0, /* tp_dealloc */
1083 0, /* tp_print */
1084 0, /* tp_getattr */
1085 0, /* tp_setattr */
1086 0, /* tp_compare */
1087 0, /* tp_repr */
1088 0, /* tp_as_number */
1089 0, /* tp_as_sequence */
1090 0, /* tp_as_mapping */
1091 0, /* tp_hash */
1092 0, /* tp_call */
1093 0, /* tp_str */
1094 0, /* tp_getattro */
1095 0, /* tp_setattro */
1096 0, /* tp_as_buffer */
1097 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1098 Unsigned_doc /* tp_doc */
1099 };