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"
32 #include "common.h"
34 #include "cpython.h"
36 #define FreeAll() \
37 do { \
38 PyMem_Free(type); \
39 PyMem_Free(plugin_instance); \
40 PyMem_Free(type_instance); \
41 PyMem_Free(plugin); \
42 PyMem_Free(host); \
43 } while (0)
45 static PyObject *cpy_common_repr(PyObject *s) {
46 PyObject *ret, *tmp;
47 static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL,
48 *l_plugin_instance = NULL;
49 static PyObject *l_host = NULL, *l_time = NULL;
50 PluginData *self = (PluginData *)s;
52 if (l_type == NULL)
53 l_type = cpy_string_to_unicode_or_bytes("(type=");
54 if (l_type_instance == NULL)
55 l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
56 if (l_plugin == NULL)
57 l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
58 if (l_plugin_instance == NULL)
59 l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
60 if (l_host == NULL)
61 l_host = cpy_string_to_unicode_or_bytes(",host=");
62 if (l_time == NULL)
63 l_time = cpy_string_to_unicode_or_bytes(",time=");
65 if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance ||
66 !l_host || !l_time)
67 return NULL;
69 ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
71 CPY_STRCAT(&ret, l_type);
72 tmp = cpy_string_to_unicode_or_bytes(self->type);
73 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
74 CPY_STRCAT_AND_DEL(&ret, tmp);
76 if (self->type_instance[0] != 0) {
77 CPY_STRCAT(&ret, l_type_instance);
78 tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
79 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
80 CPY_STRCAT_AND_DEL(&ret, tmp);
81 }
83 if (self->plugin[0] != 0) {
84 CPY_STRCAT(&ret, l_plugin);
85 tmp = cpy_string_to_unicode_or_bytes(self->plugin);
86 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
87 CPY_STRCAT_AND_DEL(&ret, tmp);
88 }
90 if (self->plugin_instance[0] != 0) {
91 CPY_STRCAT(&ret, l_plugin_instance);
92 tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
93 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
94 CPY_STRCAT_AND_DEL(&ret, tmp);
95 }
97 if (self->host[0] != 0) {
98 CPY_STRCAT(&ret, l_host);
99 tmp = cpy_string_to_unicode_or_bytes(self->host);
100 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
101 CPY_STRCAT_AND_DEL(&ret, tmp);
102 }
104 if (self->time != 0) {
105 CPY_STRCAT(&ret, l_time);
106 tmp = PyFloat_FromDouble(self->time);
107 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
108 CPY_STRCAT_AND_DEL(&ret, tmp);
109 }
110 return ret;
111 }
113 static char time_doc[] =
114 "This is the Unix timestamp of the time this value was read.\n"
115 "For dispatching values this can be set to 0 which means \"now\".\n"
116 "This means the time the value is actually dispatched, not the time\n"
117 "it was set to 0.";
119 static char host_doc[] =
120 "The hostname of the host this value was read from.\n"
121 "For dispatching this can be set to an empty string which means\n"
122 "the local hostname as defined in collectd.conf.";
124 static char type_doc[] =
125 "The type of this value. This type has to be defined\n"
126 "in the types.db file. Attempting to set it to any other value\n"
127 "will raise a TypeError exception.\n"
128 "Assigning a type is mandatory, calling dispatch without doing\n"
129 "so will raise a RuntimeError exception.";
131 static char type_instance_doc[] = "";
133 static char plugin_doc[] =
134 "The name of the plugin that read the data. Setting this\n"
135 "member to an empty string will insert \"python\" upon dispatching.";
137 static char plugin_instance_doc[] = "";
139 static char PluginData_doc[] =
140 "This is an internal class that is the base for Values\n"
141 "and Notification. It is pretty useless by itself and is therefore not\n"
142 "exported to the collectd module.";
144 static PyObject *PluginData_new(PyTypeObject *type, PyObject *args,
145 PyObject *kwds) {
146 PluginData *self;
148 self = (PluginData *)type->tp_alloc(type, 0);
149 if (self == NULL)
150 return NULL;
152 self->time = 0;
153 self->host[0] = 0;
154 self->plugin[0] = 0;
155 self->plugin_instance[0] = 0;
156 self->type[0] = 0;
157 self->type_instance[0] = 0;
158 return (PyObject *)self;
159 }
161 static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
162 PluginData *self = (PluginData *)s;
163 double time = 0;
164 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
165 *plugin = NULL, *host = NULL;
166 static char *kwlist[] = {
167 "type", "plugin_instance", "type_instance", "plugin", "host", "time",
168 NULL};
170 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL,
171 &type, NULL, &plugin_instance, NULL,
172 &type_instance, NULL, &plugin, NULL, &host,
173 &time))
174 return -1;
176 if (type && plugin_get_ds(type) == NULL) {
177 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
178 FreeAll();
179 return -1;
180 }
182 sstrncpy(self->host, host ? host : "", sizeof(self->host));
183 sstrncpy(self->plugin, plugin ? plugin : "", sizeof(self->plugin));
184 sstrncpy(self->plugin_instance, plugin_instance ? plugin_instance : "",
185 sizeof(self->plugin_instance));
186 sstrncpy(self->type, type ? type : "", sizeof(self->type));
187 sstrncpy(self->type_instance, type_instance ? type_instance : "",
188 sizeof(self->type_instance));
189 self->time = time;
191 FreeAll();
193 return 0;
194 }
196 static PyObject *PluginData_repr(PyObject *s) {
197 PyObject *ret;
198 static PyObject *l_closing = NULL;
200 if (l_closing == NULL)
201 l_closing = cpy_string_to_unicode_or_bytes(")");
203 if (l_closing == NULL)
204 return NULL;
206 ret = cpy_common_repr(s);
207 CPY_STRCAT(&ret, l_closing);
208 return ret;
209 }
211 static PyMemberDef PluginData_members[] = {
212 {"time", T_DOUBLE, offsetof(PluginData, time), 0, time_doc}, {NULL}};
214 static PyObject *PluginData_getstring(PyObject *self, void *data) {
215 const char *value = ((char *)self) + (intptr_t)data;
217 return cpy_string_to_unicode_or_bytes(value);
218 }
220 static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
221 char *old;
222 const char *new;
224 if (value == NULL) {
225 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
226 return -1;
227 }
228 Py_INCREF(value);
229 new = cpy_unicode_or_bytes_to_string(&value);
230 if (new == NULL) {
231 Py_DECREF(value);
232 return -1;
233 }
234 old = ((char *)self) + (intptr_t)data;
235 sstrncpy(old, new, DATA_MAX_NAME_LEN);
236 Py_DECREF(value);
237 return 0;
238 }
240 static int PluginData_settype(PyObject *self, PyObject *value, void *data) {
241 char *old;
242 const char *new;
244 if (value == NULL) {
245 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
246 return -1;
247 }
248 Py_INCREF(value);
249 new = cpy_unicode_or_bytes_to_string(&value);
250 if (new == NULL) {
251 Py_DECREF(value);
252 return -1;
253 }
255 if (plugin_get_ds(new) == NULL) {
256 PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
257 Py_DECREF(value);
258 return -1;
259 }
261 old = ((char *)self) + (intptr_t)data;
262 sstrncpy(old, new, DATA_MAX_NAME_LEN);
263 Py_DECREF(value);
264 return 0;
265 }
267 static PyGetSetDef PluginData_getseters[] = {
268 {"host", PluginData_getstring, PluginData_setstring, host_doc,
269 (void *)offsetof(PluginData, host)},
270 {"plugin", PluginData_getstring, PluginData_setstring, plugin_doc,
271 (void *)offsetof(PluginData, plugin)},
272 {"plugin_instance", PluginData_getstring, PluginData_setstring,
273 plugin_instance_doc, (void *)offsetof(PluginData, plugin_instance)},
274 {"type_instance", PluginData_getstring, PluginData_setstring,
275 type_instance_doc, (void *)offsetof(PluginData, type_instance)},
276 {"type", PluginData_getstring, PluginData_settype, type_doc,
277 (void *)offsetof(PluginData, type)},
278 {NULL}};
280 PyTypeObject PluginDataType = {
281 CPY_INIT_TYPE "collectd.PluginData", /* tp_name */
282 sizeof(PluginData), /* tp_basicsize */
283 0, /* Will be filled in later */
284 0, /* tp_dealloc */
285 0, /* tp_print */
286 0, /* tp_getattr */
287 0, /* tp_setattr */
288 0, /* tp_compare */
289 PluginData_repr, /* tp_repr */
290 0, /* tp_as_number */
291 0, /* tp_as_sequence */
292 0, /* tp_as_mapping */
293 0, /* tp_hash */
294 0, /* tp_call */
295 0, /* tp_str */
296 0, /* tp_getattro */
297 0, /* tp_setattro */
298 0, /* tp_as_buffer */
299 Py_TPFLAGS_DEFAULT |
300 Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/
301 PluginData_doc, /* tp_doc */
302 0, /* tp_traverse */
303 0, /* tp_clear */
304 0, /* tp_richcompare */
305 0, /* tp_weaklistoffset */
306 0, /* tp_iter */
307 0, /* tp_iternext */
308 0, /* tp_methods */
309 PluginData_members, /* tp_members */
310 PluginData_getseters, /* tp_getset */
311 0, /* tp_base */
312 0, /* tp_dict */
313 0, /* tp_descr_get */
314 0, /* tp_descr_set */
315 0, /* tp_dictoffset */
316 PluginData_init, /* tp_init */
317 0, /* tp_alloc */
318 PluginData_new /* tp_new */
319 };
321 static char interval_doc[] =
322 "The interval is the timespan in seconds between two submits for\n"
323 "the same data source. This value has to be a positive integer, so you "
324 "can't\n"
325 "submit more than one value per second. If this member is set to a\n"
326 "non-positive value, the default value as specified in the config file "
327 "will\n"
328 "be used (default: 10).\n"
329 "\n"
330 "If you submit values more often than the specified interval, the average\n"
331 "will be used. If you submit less values, your graphs will have gaps.";
333 static char values_doc[] =
334 "These are the actual values that get dispatched to collectd.\n"
335 "It has to be a sequence (a tuple or list) of numbers.\n"
336 "The size of the sequence and the type of its content depend on the type\n"
337 "member in the types.db file. For more information on this read the\n"
338 "types.db man page.\n"
339 "\n"
340 "If the sequence does not have the correct size upon dispatch a "
341 "RuntimeError\n"
342 "exception will be raised. If the content of the sequence is not a "
343 "number,\n"
344 "a TypeError exception will be raised.";
346 static char meta_doc[] =
347 "These are the meta data for this Value object.\n"
348 "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
349 "strings. int and long objects will be dispatched as signed integers "
350 "unless\n"
351 "they are between 2**63 and 2**64-1, which will result in an unsigned "
352 "integer.\n"
353 "You can force one of these storage classes by using the classes\n"
354 "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
355 "callback will always contain Signed or Unsigned objects.";
357 static char dispatch_doc[] =
358 "dispatch([type][, values][, plugin_instance][, type_instance]"
359 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
360 "\n"
361 "Dispatch this instance to the collectd process. The object has members\n"
362 "for each of the possible arguments for this method. For a detailed "
363 "explanation\n"
364 "of these parameters see the member of the same same.\n"
365 "\n"
366 "If you do not submit a parameter the value saved in its member will be "
367 "submitted.\n"
368 "If you do provide a parameter it will be used instead, without altering "
369 "the member.";
371 static char write_doc[] =
372 "write([destination][, type][, values][, plugin_instance][, type_instance]"
373 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
374 "\n"
375 "Write this instance to a single plugin or all plugins if 'destination' is "
376 "omitted.\n"
377 "This will bypass the main collectd process and all filtering and "
378 "caching.\n"
379 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' "
380 "should be\n"
381 "used instead of 'write'.\n";
383 static char Values_doc[] = "A Values object used for dispatching values to "
384 "collectd and receiving values from write "
385 "callbacks.";
387 static PyObject *Values_new(PyTypeObject *type, PyObject *args,
388 PyObject *kwds) {
389 Values *self;
391 self = (Values *)PluginData_new(type, args, kwds);
392 if (self == NULL)
393 return NULL;
395 self->values = PyList_New(0);
396 self->meta = PyDict_New();
397 self->interval = 0;
398 return (PyObject *)self;
399 }
401 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
402 Values *self = (Values *)s;
403 double interval = 0, time = 0;
404 PyObject *values = NULL, *meta = NULL, *tmp;
405 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
406 *plugin = NULL, *host = NULL;
407 static char *kwlist[] = {
408 "type", "values", "plugin_instance", "type_instance", "plugin",
409 "host", "time", "interval", "meta", NULL};
411 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
412 &type, &values, NULL, &plugin_instance, NULL,
413 &type_instance, NULL, &plugin, NULL, &host,
414 &time, &interval, &meta))
415 return -1;
417 if (type && plugin_get_ds(type) == NULL) {
418 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
419 FreeAll();
420 return -1;
421 }
423 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
424 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
425 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
426 sizeof(self->data.plugin_instance));
427 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
428 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
429 sizeof(self->data.type_instance));
430 self->data.time = time;
432 FreeAll();
434 if (values == NULL) {
435 values = PyList_New(0);
436 PyErr_Clear();
437 } else {
438 Py_INCREF(values);
439 }
441 if (meta == NULL) {
442 meta = PyDict_New();
443 PyErr_Clear();
444 } else {
445 Py_INCREF(meta);
446 }
448 tmp = self->values;
449 self->values = values;
450 Py_XDECREF(tmp);
452 tmp = self->meta;
453 self->meta = meta;
454 Py_XDECREF(tmp);
456 self->interval = interval;
457 return 0;
458 }
460 static meta_data_t *cpy_build_meta(PyObject *meta) {
461 int s;
462 meta_data_t *m = NULL;
463 PyObject *l;
465 if ((meta == NULL) || (meta == Py_None))
466 return NULL;
468 l = PyDict_Items(meta); /* New reference. */
469 if (!l) {
470 cpy_log_exception("building meta data");
471 return NULL;
472 }
473 s = PyList_Size(l);
474 if (s <= 0) {
475 Py_XDECREF(l);
476 return NULL;
477 }
479 m = meta_data_create();
480 for (int i = 0; i < s; ++i) {
481 const char *string, *keystring;
482 PyObject *key, *value, *item, *tmp;
484 item = PyList_GET_ITEM(l, i);
485 key = PyTuple_GET_ITEM(item, 0);
486 Py_INCREF(key);
487 keystring = cpy_unicode_or_bytes_to_string(&key);
488 if (!keystring) {
489 PyErr_Clear();
490 Py_XDECREF(key);
491 continue;
492 }
493 value = PyTuple_GET_ITEM(item, 1);
494 Py_INCREF(value);
495 if (value == Py_True) {
496 meta_data_add_boolean(m, keystring, 1);
497 } else if (value == Py_False) {
498 meta_data_add_boolean(m, keystring, 0);
499 } else if (PyFloat_Check(value)) {
500 meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
501 } else if (PyObject_TypeCheck(value, &SignedType)) {
502 long long int lli;
503 lli = PyLong_AsLongLong(value);
504 if (!PyErr_Occurred() && (lli == (int64_t)lli))
505 meta_data_add_signed_int(m, keystring, lli);
506 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
507 long long unsigned llu;
508 llu = PyLong_AsUnsignedLongLong(value);
509 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
510 meta_data_add_unsigned_int(m, keystring, llu);
511 } else if (PyNumber_Check(value)) {
512 long long int lli;
513 long long unsigned llu;
514 tmp = PyNumber_Long(value);
515 lli = PyLong_AsLongLong(tmp);
516 if (!PyErr_Occurred() && (lli == (int64_t)lli)) {
517 meta_data_add_signed_int(m, keystring, lli);
518 } else {
519 PyErr_Clear();
520 llu = PyLong_AsUnsignedLongLong(tmp);
521 if (!PyErr_Occurred() && (llu == (uint64_t)llu))
522 meta_data_add_unsigned_int(m, keystring, llu);
523 }
524 Py_XDECREF(tmp);
525 } else {
526 string = cpy_unicode_or_bytes_to_string(&value);
527 if (string) {
528 meta_data_add_string(m, keystring, string);
529 } else {
530 PyErr_Clear();
531 tmp = PyObject_Str(value);
532 string = cpy_unicode_or_bytes_to_string(&tmp);
533 if (string)
534 meta_data_add_string(m, keystring, string);
535 Py_XDECREF(tmp);
536 }
537 }
538 if (PyErr_Occurred())
539 cpy_log_exception("building meta data");
540 Py_XDECREF(value);
541 Py_DECREF(key);
542 }
543 Py_XDECREF(l);
544 return m;
545 }
547 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
548 int ret;
549 const data_set_t *ds;
550 size_t size;
551 value_t *value;
552 value_list_t value_list = VALUE_LIST_INIT;
553 PyObject *values = self->values, *meta = self->meta;
554 double time = self->data.time, interval = self->interval;
555 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
556 *type_instance = NULL;
558 static char *kwlist[] = {
559 "type", "values", "plugin_instance", "type_instance", "plugin",
560 "host", "time", "interval", "meta", NULL};
561 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL,
562 &type, &values, NULL, &plugin_instance, NULL,
563 &type_instance, NULL, &plugin, NULL, &host,
564 &time, &interval, &meta))
565 return NULL;
567 sstrncpy(value_list.host, host ? host : self->data.host,
568 sizeof(value_list.host));
569 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
570 sizeof(value_list.plugin));
571 sstrncpy(value_list.plugin_instance,
572 plugin_instance ? plugin_instance : self->data.plugin_instance,
573 sizeof(value_list.plugin_instance));
574 sstrncpy(value_list.type, type ? type : self->data.type,
575 sizeof(value_list.type));
576 sstrncpy(value_list.type_instance,
577 type_instance ? type_instance : self->data.type_instance,
578 sizeof(value_list.type_instance));
579 FreeAll();
580 if (value_list.type[0] == 0) {
581 PyErr_SetString(PyExc_RuntimeError, "type not set");
582 FreeAll();
583 return NULL;
584 }
585 ds = plugin_get_ds(value_list.type);
586 if (ds == NULL) {
587 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
588 return NULL;
589 }
590 if (values == NULL ||
591 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
592 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
593 return NULL;
594 }
595 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
596 PyErr_Format(PyExc_TypeError, "meta must be a dict");
597 return NULL;
598 }
599 size = (size_t)PySequence_Length(values);
600 if (size != ds->ds_num) {
601 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
602 value_list.type, ds->ds_num, size);
603 return NULL;
604 }
605 value = calloc(size, sizeof(*value));
606 for (size_t i = 0; i < size; ++i) {
607 PyObject *item, *num;
608 item = PySequence_Fast_GET_ITEM(values, (int)i); /* Borrowed reference. */
609 switch (ds->ds[i].type) {
610 case DS_TYPE_COUNTER:
611 num = PyNumber_Long(item); /* New reference. */
612 if (num != NULL) {
613 value[i].counter = PyLong_AsUnsignedLongLong(num);
614 Py_XDECREF(num);
615 }
616 break;
617 case DS_TYPE_GAUGE:
618 num = PyNumber_Float(item); /* New reference. */
619 if (num != NULL) {
620 value[i].gauge = PyFloat_AsDouble(num);
621 Py_XDECREF(num);
622 }
623 break;
624 case DS_TYPE_DERIVE:
625 /* This might overflow without raising an exception.
626 * Not much we can do about it */
627 num = PyNumber_Long(item); /* New reference. */
628 if (num != NULL) {
629 value[i].derive = PyLong_AsLongLong(num);
630 Py_XDECREF(num);
631 }
632 break;
633 case DS_TYPE_ABSOLUTE:
634 /* This might overflow without raising an exception.
635 * Not much we can do about it */
636 num = PyNumber_Long(item); /* New reference. */
637 if (num != NULL) {
638 value[i].absolute = PyLong_AsUnsignedLongLong(num);
639 Py_XDECREF(num);
640 }
641 break;
642 default:
643 free(value);
644 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
645 ds->ds[i].type, value_list.type);
646 return NULL;
647 }
648 if (PyErr_Occurred() != NULL) {
649 free(value);
650 return NULL;
651 }
652 }
653 value_list.values = value;
654 value_list.meta = cpy_build_meta(meta);
655 value_list.values_len = size;
656 value_list.time = DOUBLE_TO_CDTIME_T(time);
657 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
658 if (value_list.host[0] == 0)
659 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
660 if (value_list.plugin[0] == 0)
661 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
662 Py_BEGIN_ALLOW_THREADS;
663 ret = plugin_dispatch_values(&value_list);
664 Py_END_ALLOW_THREADS;
665 meta_data_destroy(value_list.meta);
666 free(value);
667 if (ret != 0) {
668 PyErr_SetString(PyExc_RuntimeError,
669 "error dispatching values, read the logs");
670 return NULL;
671 }
672 Py_RETURN_NONE;
673 }
675 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
676 int ret;
677 const data_set_t *ds;
678 size_t size;
679 value_t *value;
680 value_list_t value_list = VALUE_LIST_INIT;
681 PyObject *values = self->values, *meta = self->meta;
682 double time = self->data.time, interval = self->interval;
683 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
684 *type_instance = NULL, *dest = NULL;
686 static char *kwlist[] = {
687 "destination", "type", "values", "plugin_instance",
688 "type_instance", "plugin", "host", "time",
689 "interval", "meta", NULL};
690 if (!PyArg_ParseTupleAndKeywords(
691 args, kwds, "et|etOetetetetdiO", kwlist, NULL, &dest, NULL, &type,
692 &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin,
693 NULL, &host, &time, &interval, &meta))
694 return NULL;
696 sstrncpy(value_list.host, host ? host : self->data.host,
697 sizeof(value_list.host));
698 sstrncpy(value_list.plugin, plugin ? plugin : self->data.plugin,
699 sizeof(value_list.plugin));
700 sstrncpy(value_list.plugin_instance,
701 plugin_instance ? plugin_instance : self->data.plugin_instance,
702 sizeof(value_list.plugin_instance));
703 sstrncpy(value_list.type, type ? type : self->data.type,
704 sizeof(value_list.type));
705 sstrncpy(value_list.type_instance,
706 type_instance ? type_instance : self->data.type_instance,
707 sizeof(value_list.type_instance));
708 FreeAll();
709 if (value_list.type[0] == 0) {
710 PyErr_SetString(PyExc_RuntimeError, "type not set");
711 return NULL;
712 }
713 ds = plugin_get_ds(value_list.type);
714 if (ds == NULL) {
715 PyErr_Format(PyExc_TypeError, "Dataset %s not found", value_list.type);
716 return NULL;
717 }
718 if (values == NULL ||
719 (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
720 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
721 return NULL;
722 }
723 size = (size_t)PySequence_Length(values);
724 if (size != ds->ds_num) {
725 PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
726 value_list.type, ds->ds_num, size);
727 return NULL;
728 }
729 value = calloc(size, sizeof(*value));
730 for (size_t i = 0; i < size; ++i) {
731 PyObject *item, *num;
732 item = PySequence_Fast_GET_ITEM(values, i); /* Borrowed reference. */
733 switch (ds->ds[i].type) {
734 case DS_TYPE_COUNTER:
735 num = PyNumber_Long(item); /* New reference. */
736 if (num != NULL) {
737 value[i].counter = PyLong_AsUnsignedLongLong(num);
738 Py_XDECREF(num);
739 }
740 break;
741 case DS_TYPE_GAUGE:
742 num = PyNumber_Float(item); /* New reference. */
743 if (num != NULL) {
744 value[i].gauge = PyFloat_AsDouble(num);
745 Py_XDECREF(num);
746 }
747 break;
748 case DS_TYPE_DERIVE:
749 /* This might overflow without raising an exception.
750 * Not much we can do about it */
751 num = PyNumber_Long(item); /* New reference. */
752 if (num != NULL) {
753 value[i].derive = PyLong_AsLongLong(num);
754 Py_XDECREF(num);
755 }
756 break;
757 case DS_TYPE_ABSOLUTE:
758 /* This might overflow without raising an exception.
759 * Not much we can do about it */
760 num = PyNumber_Long(item); /* New reference. */
761 if (num != NULL) {
762 value[i].absolute = PyLong_AsUnsignedLongLong(num);
763 Py_XDECREF(num);
764 }
765 break;
766 default:
767 free(value);
768 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s",
769 ds->ds[i].type, value_list.type);
770 return NULL;
771 }
772 if (PyErr_Occurred() != NULL) {
773 free(value);
774 return NULL;
775 }
776 }
777 value_list.values = value;
778 value_list.values_len = size;
779 value_list.time = DOUBLE_TO_CDTIME_T(time);
780 value_list.interval = DOUBLE_TO_CDTIME_T(interval);
781 value_list.meta = cpy_build_meta(meta);
782 if (value_list.host[0] == 0)
783 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
784 if (value_list.plugin[0] == 0)
785 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
786 Py_BEGIN_ALLOW_THREADS;
787 ret = plugin_write(dest, NULL, &value_list);
788 Py_END_ALLOW_THREADS;
789 meta_data_destroy(value_list.meta);
790 free(value);
791 if (ret != 0) {
792 PyErr_SetString(PyExc_RuntimeError,
793 "error dispatching values, read the logs");
794 return NULL;
795 }
796 Py_RETURN_NONE;
797 }
799 static PyObject *Values_repr(PyObject *s) {
800 PyObject *ret, *tmp;
801 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL,
802 *l_closing = NULL;
803 Values *self = (Values *)s;
805 if (l_interval == NULL)
806 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
807 if (l_values == NULL)
808 l_values = cpy_string_to_unicode_or_bytes(",values=");
809 if (l_meta == NULL)
810 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
811 if (l_closing == NULL)
812 l_closing = cpy_string_to_unicode_or_bytes(")");
814 if (l_interval == NULL || l_values == NULL || l_meta == NULL ||
815 l_closing == NULL)
816 return NULL;
818 ret = cpy_common_repr(s);
819 if (self->interval != 0) {
820 CPY_STRCAT(&ret, l_interval);
821 tmp = PyFloat_FromDouble(self->interval);
822 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
823 CPY_STRCAT_AND_DEL(&ret, tmp);
824 }
825 if (self->values &&
826 (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
827 CPY_STRCAT(&ret, l_values);
828 tmp = PyObject_Repr(self->values);
829 CPY_STRCAT_AND_DEL(&ret, tmp);
830 }
831 if (self->meta &&
832 (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
833 CPY_STRCAT(&ret, l_meta);
834 tmp = PyObject_Repr(self->meta);
835 CPY_STRCAT_AND_DEL(&ret, tmp);
836 }
837 CPY_STRCAT(&ret, l_closing);
838 return ret;
839 }
841 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
842 Values *v = (Values *)self;
843 Py_VISIT(v->values);
844 Py_VISIT(v->meta);
845 return 0;
846 }
848 static int Values_clear(PyObject *self) {
849 Values *v = (Values *)self;
850 Py_CLEAR(v->values);
851 Py_CLEAR(v->meta);
852 return 0;
853 }
855 static void Values_dealloc(PyObject *self) {
856 Values_clear(self);
857 self->ob_type->tp_free(self);
858 }
860 static PyMemberDef Values_members[] = {
861 {"interval", T_DOUBLE, offsetof(Values, interval), 0, interval_doc},
862 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
863 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
864 {NULL}};
866 static PyMethodDef Values_methods[] = {
867 {"dispatch", (PyCFunction)Values_dispatch, METH_VARARGS | METH_KEYWORDS,
868 dispatch_doc},
869 {"write", (PyCFunction)Values_write, METH_VARARGS | METH_KEYWORDS,
870 write_doc},
871 {NULL}};
873 PyTypeObject ValuesType = {
874 CPY_INIT_TYPE "collectd.Values", /* tp_name */
875 sizeof(Values), /* tp_basicsize */
876 0, /* Will be filled in later */
877 Values_dealloc, /* tp_dealloc */
878 0, /* tp_print */
879 0, /* tp_getattr */
880 0, /* tp_setattr */
881 0, /* tp_compare */
882 Values_repr, /* tp_repr */
883 0, /* tp_as_number */
884 0, /* tp_as_sequence */
885 0, /* tp_as_mapping */
886 0, /* tp_hash */
887 0, /* tp_call */
888 0, /* tp_str */
889 0, /* tp_getattro */
890 0, /* tp_setattro */
891 0, /* tp_as_buffer */
892 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
893 Values_doc, /* tp_doc */
894 Values_traverse, /* tp_traverse */
895 Values_clear, /* tp_clear */
896 0, /* tp_richcompare */
897 0, /* tp_weaklistoffset */
898 0, /* tp_iter */
899 0, /* tp_iternext */
900 Values_methods, /* tp_methods */
901 Values_members, /* tp_members */
902 0, /* tp_getset */
903 0, /* tp_base */
904 0, /* tp_dict */
905 0, /* tp_descr_get */
906 0, /* tp_descr_set */
907 0, /* tp_dictoffset */
908 Values_init, /* tp_init */
909 0, /* tp_alloc */
910 Values_new /* tp_new */
911 };
913 static char severity_doc[] =
914 "The severity of this notification. Assign or compare to\n"
915 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
917 static char message_doc[] = "Some kind of description what's going on and why "
918 "this Notification was generated.";
920 static char Notification_doc[] =
921 "The Notification class is a wrapper around the collectd notification.\n"
922 "It can be used to notify other plugins about bad stuff happening. It "
923 "works\n"
924 "similar to Values but has a severity and a message instead of interval\n"
925 "and time.\n"
926 "Notifications can be dispatched at any time and can be received with "
927 "register_notification.";
929 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
930 Notification *self = (Notification *)s;
931 int severity = 0;
932 double time = 0;
933 char *message = NULL;
934 char *type = NULL, *plugin_instance = NULL, *type_instance = NULL,
935 *plugin = NULL, *host = NULL;
936 static char *kwlist[] = {"type", "message", "plugin_instance",
937 "type_instance", "plugin", "host",
938 "time", "severity", NULL};
940 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
941 &type, NULL, &message, NULL,
942 &plugin_instance, NULL, &type_instance, NULL,
943 &plugin, NULL, &host, &time, &severity))
944 return -1;
946 if (type && plugin_get_ds(type) == NULL) {
947 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
948 FreeAll();
949 PyMem_Free(message);
950 return -1;
951 }
953 sstrncpy(self->data.host, host ? host : "", sizeof(self->data.host));
954 sstrncpy(self->data.plugin, plugin ? plugin : "", sizeof(self->data.plugin));
955 sstrncpy(self->data.plugin_instance, plugin_instance ? plugin_instance : "",
956 sizeof(self->data.plugin_instance));
957 sstrncpy(self->data.type, type ? type : "", sizeof(self->data.type));
958 sstrncpy(self->data.type_instance, type_instance ? type_instance : "",
959 sizeof(self->data.type_instance));
960 sstrncpy(self->message, message ? message : "", sizeof(self->message));
961 self->data.time = time;
962 self->severity = severity;
964 FreeAll();
965 PyMem_Free(message);
966 return 0;
967 }
969 static PyObject *Notification_dispatch(Notification *self, PyObject *args,
970 PyObject *kwds) {
971 int ret;
972 const data_set_t *ds;
973 notification_t notification;
974 double t = self->data.time;
975 int severity = self->severity;
976 char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL,
977 *type_instance = NULL;
978 char *message = NULL;
980 static char *kwlist[] = {"type", "message", "plugin_instance",
981 "type_instance", "plugin", "host",
982 "time", "severity", NULL};
983 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist, NULL,
984 &type, NULL, &message, NULL,
985 &plugin_instance, NULL, &type_instance, NULL,
986 &plugin, NULL, &host, &t, &severity))
987 return NULL;
989 notification.time = DOUBLE_TO_CDTIME_T(t);
990 notification.severity = severity;
991 sstrncpy(notification.message, message ? message : self->message,
992 sizeof(notification.message));
993 sstrncpy(notification.host, host ? host : self->data.host,
994 sizeof(notification.host));
995 sstrncpy(notification.plugin, plugin ? plugin : self->data.plugin,
996 sizeof(notification.plugin));
997 sstrncpy(notification.plugin_instance,
998 plugin_instance ? plugin_instance : self->data.plugin_instance,
999 sizeof(notification.plugin_instance));
1000 sstrncpy(notification.type, type ? type : self->data.type,
1001 sizeof(notification.type));
1002 sstrncpy(notification.type_instance,
1003 type_instance ? type_instance : self->data.type_instance,
1004 sizeof(notification.type_instance));
1005 notification.meta = NULL;
1006 FreeAll();
1007 PyMem_Free(message);
1009 if (notification.type[0] == 0) {
1010 PyErr_SetString(PyExc_RuntimeError, "type not set");
1011 return NULL;
1012 }
1013 ds = plugin_get_ds(notification.type);
1014 if (ds == NULL) {
1015 PyErr_Format(PyExc_TypeError, "Dataset %s not found", notification.type);
1016 return NULL;
1017 }
1019 if (notification.time == 0)
1020 notification.time = cdtime();
1021 if (notification.host[0] == 0)
1022 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
1023 if (notification.plugin[0] == 0)
1024 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
1025 Py_BEGIN_ALLOW_THREADS;
1026 ret = plugin_dispatch_notification(¬ification);
1027 Py_END_ALLOW_THREADS;
1028 if (ret != 0) {
1029 PyErr_SetString(PyExc_RuntimeError,
1030 "error dispatching notification, read the logs");
1031 return NULL;
1032 }
1033 Py_RETURN_NONE;
1034 }
1036 static PyObject *Notification_new(PyTypeObject *type, PyObject *args,
1037 PyObject *kwds) {
1038 Notification *self;
1040 self = (Notification *)PluginData_new(type, args, kwds);
1041 if (self == NULL)
1042 return NULL;
1044 self->message[0] = 0;
1045 self->severity = 0;
1046 return (PyObject *)self;
1047 }
1049 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
1050 char *old;
1051 const char *new;
1053 if (value == NULL) {
1054 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
1055 return -1;
1056 }
1057 Py_INCREF(value);
1058 new = cpy_unicode_or_bytes_to_string(&value);
1059 if (new == NULL) {
1060 Py_DECREF(value);
1061 return -1;
1062 }
1063 old = ((char *)self) + (intptr_t)data;
1064 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
1065 Py_DECREF(value);
1066 return 0;
1067 }
1069 static PyObject *Notification_repr(PyObject *s) {
1070 PyObject *ret, *tmp;
1071 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
1072 Notification *self = (Notification *)s;
1074 if (l_severity == NULL)
1075 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
1076 if (l_message == NULL)
1077 l_message = cpy_string_to_unicode_or_bytes(",message=");
1078 if (l_closing == NULL)
1079 l_closing = cpy_string_to_unicode_or_bytes(")");
1081 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
1082 return NULL;
1084 ret = cpy_common_repr(s);
1085 if (self->severity != 0) {
1086 CPY_STRCAT(&ret, l_severity);
1087 tmp = PyInt_FromLong(self->severity);
1088 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1089 CPY_STRCAT_AND_DEL(&ret, tmp);
1090 }
1091 if (self->message[0] != 0) {
1092 CPY_STRCAT(&ret, l_message);
1093 tmp = cpy_string_to_unicode_or_bytes(self->message);
1094 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
1095 CPY_STRCAT_AND_DEL(&ret, tmp);
1096 }
1097 CPY_STRCAT(&ret, l_closing);
1098 return ret;
1099 }
1101 static PyMethodDef Notification_methods[] = {
1102 {"dispatch", (PyCFunction)Notification_dispatch,
1103 METH_VARARGS | METH_KEYWORDS, dispatch_doc},
1104 {NULL}};
1106 static PyMemberDef Notification_members[] = {
1107 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
1108 {NULL}};
1110 static PyGetSetDef Notification_getseters[] = {
1111 {"message", PluginData_getstring, Notification_setstring, message_doc,
1112 (void *)offsetof(Notification, message)},
1113 {NULL}};
1115 PyTypeObject NotificationType = {
1116 CPY_INIT_TYPE "collectd.Notification", /* tp_name */
1117 sizeof(Notification), /* tp_basicsize */
1118 0, /* Will be filled in later */
1119 0, /* tp_dealloc */
1120 0, /* tp_print */
1121 0, /* tp_getattr */
1122 0, /* tp_setattr */
1123 0, /* tp_compare */
1124 Notification_repr, /* tp_repr */
1125 0, /* tp_as_number */
1126 0, /* tp_as_sequence */
1127 0, /* tp_as_mapping */
1128 0, /* tp_hash */
1129 0, /* tp_call */
1130 0, /* tp_str */
1131 0, /* tp_getattro */
1132 0, /* tp_setattro */
1133 0, /* tp_as_buffer */
1134 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1135 Notification_doc, /* tp_doc */
1136 0, /* tp_traverse */
1137 0, /* tp_clear */
1138 0, /* tp_richcompare */
1139 0, /* tp_weaklistoffset */
1140 0, /* tp_iter */
1141 0, /* tp_iternext */
1142 Notification_methods, /* tp_methods */
1143 Notification_members, /* tp_members */
1144 Notification_getseters, /* tp_getset */
1145 0, /* tp_base */
1146 0, /* tp_dict */
1147 0, /* tp_descr_get */
1148 0, /* tp_descr_set */
1149 0, /* tp_dictoffset */
1150 Notification_init, /* tp_init */
1151 0, /* tp_alloc */
1152 Notification_new /* tp_new */
1153 };
1155 static char Signed_doc[] =
1156 "This is a long by another name. Use it in meta data dicts\n"
1157 "to choose the way it is stored in the meta data.";
1159 PyTypeObject SignedType = {
1160 CPY_INIT_TYPE "collectd.Signed", /* tp_name */
1161 sizeof(Signed), /* tp_basicsize */
1162 0, /* Will be filled in later */
1163 0, /* tp_dealloc */
1164 0, /* tp_print */
1165 0, /* tp_getattr */
1166 0, /* tp_setattr */
1167 0, /* tp_compare */
1168 0, /* tp_repr */
1169 0, /* tp_as_number */
1170 0, /* tp_as_sequence */
1171 0, /* tp_as_mapping */
1172 0, /* tp_hash */
1173 0, /* tp_call */
1174 0, /* tp_str */
1175 0, /* tp_getattro */
1176 0, /* tp_setattro */
1177 0, /* tp_as_buffer */
1178 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1179 Signed_doc /* tp_doc */
1180 };
1182 static char Unsigned_doc[] =
1183 "This is a long by another name. Use it in meta data dicts\n"
1184 "to choose the way it is stored in the meta data.";
1186 PyTypeObject UnsignedType = {
1187 CPY_INIT_TYPE "collectd.Unsigned", /* tp_name */
1188 sizeof(Unsigned), /* tp_basicsize */
1189 0, /* Will be filled in later */
1190 0, /* tp_dealloc */
1191 0, /* tp_print */
1192 0, /* tp_getattr */
1193 0, /* tp_setattr */
1194 0, /* tp_compare */
1195 0, /* tp_repr */
1196 0, /* tp_as_number */
1197 0, /* tp_as_sequence */
1198 0, /* tp_as_mapping */
1199 0, /* tp_hash */
1200 0, /* tp_call */
1201 0, /* tp_str */
1202 0, /* tp_getattro */
1203 0, /* tp_setattro */
1204 0, /* tp_as_buffer */
1205 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1206 Unsigned_doc /* tp_doc */
1207 };