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