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