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