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