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