a928cbc2803846b68681c18837a9f96d53f7da56
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.\n"
313 "If the dict contains anything else a TypeError exception will be raised.";
315 static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
316 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
317 "\n"
318 "Dispatch this instance to the collectd process. The object has members\n"
319 "for each of the possible arguments for this method. For a detailed explanation\n"
320 "of these parameters see the member of the same same.\n"
321 "\n"
322 "If you do not submit a parameter the value saved in its member will be submitted.\n"
323 "If you do provide a parameter it will be used instead, without altering the member.";
325 static char write_doc[] = "write([destination][, type][, values][, plugin_instance][, type_instance]"
326 "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
327 "\n"
328 "Write this instance to a single plugin or all plugins if 'destination' is obmitted.\n"
329 "This will bypass the main collectd process and all filtering and caching.\n"
330 "Other than that it works similar to 'dispatch'. In most cases 'dispatch' should be\n"
331 "used instead of 'write'.\n";
333 static char Values_doc[] = "A Values object used for dispatching values to collectd and receiving values from write callbacks.";
335 static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
336 Values *self;
338 self = (Values *) PluginData_new(type, args, kwds);
339 if (self == NULL)
340 return NULL;
342 self->values = PyList_New(0);
343 self->interval = 0;
344 return (PyObject *) self;
345 }
347 static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
348 Values *self = (Values *) s;
349 int interval = 0;
350 double time = 0;
351 PyObject *values = NULL, *meta = NULL, *tmp;
352 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
353 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
354 "plugin", "host", "time", "interval", "meta", NULL};
356 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
357 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
358 NULL, &plugin, NULL, &host, &time, &interval, &meta))
359 return -1;
361 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
362 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
363 return -1;
364 }
366 sstrncpy(self->data.host, host, sizeof(self->data.host));
367 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
368 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
369 sstrncpy(self->data.type, type, sizeof(self->data.type));
370 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
371 self->data.time = time;
373 if (values == NULL) {
374 values = PyList_New(0);
375 PyErr_Clear();
376 } else {
377 Py_INCREF(values);
378 }
380 if (meta == NULL) {
381 meta = PyDict_New();
382 PyErr_Clear();
383 } else {
384 Py_INCREF(meta);
385 }
387 tmp = self->values;
388 self->values = values;
389 self->meta = meta;
390 Py_XDECREF(tmp);
392 self->interval = interval;
393 return 0;
394 }
396 static meta_data_t *cpy_build_meta(PyObject *meta) {
397 int i, s;
398 meta_data_t *m = NULL;
399 PyObject *l;
401 if (!meta)
402 return NULL;
404 m = meta_data_create();
405 l = PyDict_Items(meta);
406 s = PyList_Size(l);
407 for (i = 0; i < s; ++i) {
408 const char *string, *keystring;
409 PyObject *key, *value, *item, *tmp;
411 item = PyList_GET_ITEM(l, i);
412 key = PyTuple_GET_ITEM(item, 0);
413 Py_INCREF(key);
414 keystring = cpy_unicode_or_bytes_to_string(&key);
415 if (!keystring) {
416 PyErr_Clear();
417 Py_XDECREF(key);
418 continue;
419 }
420 value = PyTuple_GET_ITEM(item, 1);
421 Py_INCREF(value);
422 if (value == Py_True) {
423 meta_data_add_boolean(m, keystring, 1);
424 } else if (value == Py_False) {
425 meta_data_add_boolean(m, keystring, 0);
426 } else if (PyFloat_Check(value)) {
427 meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
428 } else if (PyObject_TypeCheck(value, &SignedType)) {
429 long long int lli;
430 lli = PyLong_AsLongLong(value);
431 if (!PyErr_Occurred() && (lli == (int64_t) lli))
432 meta_data_add_signed_int(m, keystring, lli);
433 } else if (PyObject_TypeCheck(value, &UnsignedType)) {
434 long long unsigned llu;
435 llu = PyLong_AsUnsignedLongLong(value);
436 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
437 meta_data_add_unsigned_int(m, keystring, llu);
438 } else if (PyNumber_Check(value)) {
439 long long int lli;
440 long long unsigned llu;
441 tmp = PyNumber_Long(value);
442 lli = PyLong_AsLongLong(tmp);
443 if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
444 meta_data_add_signed_int(m, keystring, lli);
445 } else {
446 PyErr_Clear();
447 llu = PyLong_AsUnsignedLongLong(tmp);
448 if (!PyErr_Occurred() && (llu == (uint64_t) llu))
449 meta_data_add_unsigned_int(m, keystring, llu);
450 }
451 Py_XDECREF(tmp);
452 } else {
453 string = cpy_unicode_or_bytes_to_string(&value);
454 if (string) {
455 meta_data_add_string(m, keystring, string);
456 } else {
457 PyErr_Clear();
458 tmp = PyObject_Str(value);
459 string = cpy_unicode_or_bytes_to_string(&tmp);
460 if (string)
461 meta_data_add_string(m, keystring, string);
462 Py_XDECREF(tmp);
463 }
464 }
465 if (PyErr_Occurred())
466 cpy_log_exception("building meta data");
467 Py_XDECREF(value);
468 Py_DECREF(keystring);
469 }
470 return m;
471 }
473 static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
474 int i, ret;
475 const data_set_t *ds;
476 int size;
477 value_t *value;
478 value_list_t value_list = VALUE_LIST_INIT;
479 PyObject *values = self->values, *meta = self->meta;
480 double time = self->data.time;
481 int interval = self->interval;
482 const char *host = self->data.host;
483 const char *plugin = self->data.plugin;
484 const char *plugin_instance = self->data.plugin_instance;
485 const char *type = self->data.type;
486 const char *type_instance = self->data.type_instance;
488 static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
489 "plugin", "host", "time", "interval", "meta", NULL};
490 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
491 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
492 NULL, &plugin, NULL, &host, &time, &interval, &meta))
493 return NULL;
495 if (type[0] == 0) {
496 PyErr_SetString(PyExc_RuntimeError, "type not set");
497 return NULL;
498 }
499 ds = plugin_get_ds(type);
500 if (ds == NULL) {
501 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
502 return NULL;
503 }
504 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
505 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
506 return NULL;
507 }
508 if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
509 PyErr_Format(PyExc_TypeError, "meta must be a dict");
510 return NULL;
511 }
512 size = (int) PySequence_Length(values);
513 if (size != ds->ds_num) {
514 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
515 return NULL;
516 }
517 value = malloc(size * sizeof(*value));
518 for (i = 0; i < size; ++i) {
519 PyObject *item, *num;
520 item = PySequence_GetItem(values, i);
521 if (ds->ds->type == DS_TYPE_COUNTER) {
522 num = PyNumber_Long(item);
523 if (num != NULL)
524 value[i].counter = PyLong_AsUnsignedLongLong(num);
525 } else if (ds->ds->type == DS_TYPE_GAUGE) {
526 num = PyNumber_Float(item);
527 if (num != NULL)
528 value[i].gauge = PyFloat_AsDouble(num);
529 } else if (ds->ds->type == DS_TYPE_DERIVE) {
530 /* This might overflow without raising an exception.
531 * Not much we can do about it */
532 num = PyNumber_Long(item);
533 if (num != NULL)
534 value[i].derive = PyLong_AsLongLong(num);
535 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
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].absolute = PyLong_AsUnsignedLongLong(num);
541 } else {
542 free(value);
543 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
544 return NULL;
545 }
546 if (PyErr_Occurred() != NULL) {
547 free(value);
548 return NULL;
549 }
550 }
551 value_list.values = value;
552 value_list.meta = cpy_build_meta(meta);
553 value_list.values_len = size;
554 value_list.time = time;
555 value_list.interval = interval;
556 sstrncpy(value_list.host, host, sizeof(value_list.host));
557 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
558 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
559 sstrncpy(value_list.type, type, sizeof(value_list.type));
560 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
561 if (value_list.host[0] == 0)
562 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
563 if (value_list.plugin[0] == 0)
564 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
565 Py_BEGIN_ALLOW_THREADS;
566 ret = plugin_dispatch_values(&value_list);
567 Py_END_ALLOW_THREADS;
568 if (ret != 0) {
569 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
570 return NULL;
571 }
572 free(value);
573 Py_RETURN_NONE;
574 }
576 static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
577 int i, ret;
578 const data_set_t *ds;
579 int size;
580 value_t *value;
581 value_list_t value_list = VALUE_LIST_INIT;
582 PyObject *values = self->values, *meta = self->meta;
583 double time = self->data.time;
584 int interval = self->interval;
585 const char *host = self->data.host;
586 const char *plugin = self->data.plugin;
587 const char *plugin_instance = self->data.plugin_instance;
588 const char *type = self->data.type;
589 const char *type_instance = self->data.type_instance;
590 const char *dest = NULL;
592 static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
593 "plugin", "host", "time", "interval", "meta", NULL};
594 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist,
595 NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance,
596 NULL, &plugin, NULL, &host, &time, &interval, &meta))
597 return NULL;
599 if (type[0] == 0) {
600 PyErr_SetString(PyExc_RuntimeError, "type not set");
601 return NULL;
602 }
603 ds = plugin_get_ds(type);
604 if (ds == NULL) {
605 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
606 return NULL;
607 }
608 if (values == NULL || (PyTuple_Check(values) == 0 && PyList_Check(values) == 0)) {
609 PyErr_Format(PyExc_TypeError, "values must be list or tuple");
610 return NULL;
611 }
612 size = (int) PySequence_Length(values);
613 if (size != ds->ds_num) {
614 PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
615 return NULL;
616 }
617 value = malloc(size * sizeof(*value));
618 for (i = 0; i < size; ++i) {
619 PyObject *item, *num;
620 item = PySequence_GetItem(values, i);
621 if (ds->ds->type == DS_TYPE_COUNTER) {
622 num = PyNumber_Long(item);
623 if (num != NULL)
624 value[i].counter = PyLong_AsUnsignedLongLong(num);
625 } else if (ds->ds->type == DS_TYPE_GAUGE) {
626 num = PyNumber_Float(item);
627 if (num != NULL)
628 value[i].gauge = PyFloat_AsDouble(num);
629 } else if (ds->ds->type == DS_TYPE_DERIVE) {
630 /* This might overflow without raising an exception.
631 * Not much we can do about it */
632 num = PyNumber_Long(item);
633 if (num != NULL)
634 value[i].derive = PyLong_AsLongLong(num);
635 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
636 /* This might overflow without raising an exception.
637 * Not much we can do about it */
638 num = PyNumber_Long(item);
639 if (num != NULL)
640 value[i].absolute = PyLong_AsUnsignedLongLong(num);
641 } else {
642 free(value);
643 PyErr_Format(PyExc_RuntimeError, "unknown data type %d for %s", ds->ds->type, type);
644 return NULL;
645 }
646 if (PyErr_Occurred() != NULL) {
647 free(value);
648 return NULL;
649 }
650 }
651 value_list.values = value;
652 value_list.values_len = size;
653 value_list.time = time;
654 value_list.interval = interval;
655 sstrncpy(value_list.host, host, sizeof(value_list.host));
656 sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
657 sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
658 sstrncpy(value_list.type, type, sizeof(value_list.type));
659 sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
660 value_list.meta = cpy_build_meta(meta);;
661 if (value_list.host[0] == 0)
662 sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
663 if (value_list.plugin[0] == 0)
664 sstrncpy(value_list.plugin, "python", sizeof(value_list.plugin));
665 Py_BEGIN_ALLOW_THREADS;
666 ret = plugin_write(dest, NULL, &value_list);
667 Py_END_ALLOW_THREADS;
668 if (ret != 0) {
669 PyErr_SetString(PyExc_RuntimeError, "error dispatching values, read the logs");
670 return NULL;
671 }
672 free(value);
673 Py_RETURN_NONE;
674 }
676 static PyObject *Values_repr(PyObject *s) {
677 PyObject *ret, *tmp;
678 static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
679 Values *self = (Values *) s;
681 if (l_interval == NULL)
682 l_interval = cpy_string_to_unicode_or_bytes(",interval=");
683 if (l_values == NULL)
684 l_values = cpy_string_to_unicode_or_bytes(",values=");
685 if (l_meta == NULL)
686 l_meta = cpy_string_to_unicode_or_bytes(",meta=");
687 if (l_closing == NULL)
688 l_closing = cpy_string_to_unicode_or_bytes(")");
690 if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
691 return NULL;
693 ret = cpy_common_repr(s);
694 if (self->interval != 0) {
695 CPY_STRCAT(&ret, l_interval);
696 tmp = PyInt_FromLong(self->interval);
697 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
698 CPY_STRCAT_AND_DEL(&ret, tmp);
699 }
700 if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
701 CPY_STRCAT(&ret, l_values);
702 tmp = PyObject_Repr(self->values);
703 CPY_STRCAT_AND_DEL(&ret, tmp);
704 }
705 if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
706 CPY_STRCAT(&ret, l_meta);
707 tmp = PyObject_Repr(self->meta);
708 CPY_STRCAT_AND_DEL(&ret, tmp);
709 }
710 CPY_STRCAT(&ret, l_closing);
711 return ret;
712 }
714 static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
715 Values *v = (Values *) self;
716 Py_VISIT(v->values);
717 Py_VISIT(v->meta);
718 return 0;
719 }
721 static int Values_clear(PyObject *self) {
722 Values *v = (Values *) self;
723 Py_CLEAR(v->values);
724 Py_CLEAR(v->meta);
725 return 0;
726 }
728 static void Values_dealloc(PyObject *self) {
729 Values_clear(self);
730 self->ob_type->tp_free(self);
731 }
733 static PyMemberDef Values_members[] = {
734 {"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
735 {"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
736 {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
737 {NULL}
738 };
740 static PyMethodDef Values_methods[] = {
741 {"dispatch", (PyCFunction) Values_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
742 {"write", (PyCFunction) Values_write, METH_VARARGS | METH_KEYWORDS, write_doc},
743 {NULL}
744 };
746 PyTypeObject ValuesType = {
747 CPY_INIT_TYPE
748 "collectd.Values", /* tp_name */
749 sizeof(Values), /* tp_basicsize */
750 0, /* Will be filled in later */
751 Values_dealloc, /* tp_dealloc */
752 0, /* tp_print */
753 0, /* tp_getattr */
754 0, /* tp_setattr */
755 0, /* tp_compare */
756 Values_repr, /* tp_repr */
757 0, /* tp_as_number */
758 0, /* tp_as_sequence */
759 0, /* tp_as_mapping */
760 0, /* tp_hash */
761 0, /* tp_call */
762 0, /* tp_str */
763 0, /* tp_getattro */
764 0, /* tp_setattro */
765 0, /* tp_as_buffer */
766 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
767 Values_doc, /* tp_doc */
768 Values_traverse, /* tp_traverse */
769 Values_clear, /* tp_clear */
770 0, /* tp_richcompare */
771 0, /* tp_weaklistoffset */
772 0, /* tp_iter */
773 0, /* tp_iternext */
774 Values_methods, /* tp_methods */
775 Values_members, /* tp_members */
776 0, /* tp_getset */
777 0, /* tp_base */
778 0, /* tp_dict */
779 0, /* tp_descr_get */
780 0, /* tp_descr_set */
781 0, /* tp_dictoffset */
782 Values_init, /* tp_init */
783 0, /* tp_alloc */
784 Values_new /* tp_new */
785 };
787 static char severity_doc[] = "The severity of this notification. Assign or compare to\n"
788 "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY.";
790 static char message_doc[] = "Some kind of description what's going on and why this Notification was generated.";
792 static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n"
793 "It can be used to notify other plugins about bad stuff happening. It works\n"
794 "similar to Values but has a severity and a message instead of interval\n"
795 "and time.\n"
796 "Notifications can be dispatched at any time and can be received with register_notification.";
798 static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
799 Notification *self = (Notification *) s;
800 int severity = 0;
801 double time = 0;
802 const char *message = "";
803 const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
804 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
805 "plugin", "host", "time", "severity", NULL};
807 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
808 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
809 NULL, &plugin, NULL, &host, &time, &severity))
810 return -1;
812 if (type[0] != 0 && plugin_get_ds(type) == NULL) {
813 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
814 return -1;
815 }
817 sstrncpy(self->data.host, host, sizeof(self->data.host));
818 sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
819 sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
820 sstrncpy(self->data.type, type, sizeof(self->data.type));
821 sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
822 self->data.time = time;
824 sstrncpy(self->message, message, sizeof(self->message));
825 self->severity = severity;
826 return 0;
827 }
829 static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) {
830 int ret;
831 const data_set_t *ds;
832 notification_t notification;
833 double t = self->data.time;
834 int severity = self->severity;
835 const char *host = self->data.host;
836 const char *plugin = self->data.plugin;
837 const char *plugin_instance = self->data.plugin_instance;
838 const char *type = self->data.type;
839 const char *type_instance = self->data.type_instance;
840 const char *message = self->message;
842 static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
843 "plugin", "host", "time", "severity", NULL};
844 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
845 NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
846 NULL, &plugin, NULL, &host, &t, &severity))
847 return NULL;
849 if (type[0] == 0) {
850 PyErr_SetString(PyExc_RuntimeError, "type not set");
851 return NULL;
852 }
853 ds = plugin_get_ds(type);
854 if (ds == NULL) {
855 PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
856 return NULL;
857 }
859 notification.time = t;
860 notification.severity = severity;
861 sstrncpy(notification.message, message, sizeof(notification.message));
862 sstrncpy(notification.host, host, sizeof(notification.host));
863 sstrncpy(notification.plugin, plugin, sizeof(notification.plugin));
864 sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance));
865 sstrncpy(notification.type, type, sizeof(notification.type));
866 sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
867 notification.meta = NULL;
868 if (notification.time < 1)
869 notification.time = time(0);
870 if (notification.host[0] == 0)
871 sstrncpy(notification.host, hostname_g, sizeof(notification.host));
872 if (notification.plugin[0] == 0)
873 sstrncpy(notification.plugin, "python", sizeof(notification.plugin));
874 Py_BEGIN_ALLOW_THREADS;
875 ret = plugin_dispatch_notification(¬ification);
876 Py_END_ALLOW_THREADS;
877 if (ret != 0) {
878 PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs");
879 return NULL;
880 }
881 Py_RETURN_NONE;
882 }
884 static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
885 Notification *self;
887 self = (Notification *) PluginData_new(type, args, kwds);
888 if (self == NULL)
889 return NULL;
891 self->message[0] = 0;
892 self->severity = 0;
893 return (PyObject *) self;
894 }
896 static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
897 char *old;
898 const char *new;
900 if (value == NULL) {
901 PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
902 return -1;
903 }
904 Py_INCREF(value);
905 new = cpy_unicode_or_bytes_to_string(&value);
906 if (new == NULL) {
907 Py_DECREF(value);
908 return -1;
909 }
910 old = ((char *) self) + (intptr_t) data;
911 sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
912 Py_DECREF(value);
913 return 0;
914 }
916 static PyObject *Notification_repr(PyObject *s) {
917 PyObject *ret, *tmp;
918 static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
919 Notification *self = (Notification *) s;
921 if (l_severity == NULL)
922 l_severity = cpy_string_to_unicode_or_bytes(",severity=");
923 if (l_message == NULL)
924 l_message = cpy_string_to_unicode_or_bytes(",message=");
925 if (l_closing == NULL)
926 l_closing = cpy_string_to_unicode_or_bytes(")");
928 if (l_severity == NULL || l_message == NULL || l_closing == NULL)
929 return NULL;
931 ret = cpy_common_repr(s);
932 if (self->severity != 0) {
933 CPY_STRCAT(&ret, l_severity);
934 tmp = PyInt_FromLong(self->severity);
935 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
936 CPY_STRCAT_AND_DEL(&ret, tmp);
937 }
938 if (self->message[0] != 0) {
939 CPY_STRCAT(&ret, l_message);
940 tmp = cpy_string_to_unicode_or_bytes(self->message);
941 CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
942 CPY_STRCAT_AND_DEL(&ret, tmp);
943 }
944 CPY_STRCAT(&ret, l_closing);
945 return ret;
946 }
948 static PyMethodDef Notification_methods[] = {
949 {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc},
950 {NULL}
951 };
953 static PyMemberDef Notification_members[] = {
954 {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc},
955 {NULL}
956 };
958 static PyGetSetDef Notification_getseters[] = {
959 {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)},
960 {NULL}
961 };
963 PyTypeObject NotificationType = {
964 CPY_INIT_TYPE
965 "collectd.Notification", /* tp_name */
966 sizeof(Notification), /* tp_basicsize */
967 0, /* Will be filled in later */
968 0, /* tp_dealloc */
969 0, /* tp_print */
970 0, /* tp_getattr */
971 0, /* tp_setattr */
972 0, /* tp_compare */
973 Notification_repr, /* tp_repr */
974 0, /* tp_as_number */
975 0, /* tp_as_sequence */
976 0, /* tp_as_mapping */
977 0, /* tp_hash */
978 0, /* tp_call */
979 0, /* tp_str */
980 0, /* tp_getattro */
981 0, /* tp_setattro */
982 0, /* tp_as_buffer */
983 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
984 Notification_doc, /* tp_doc */
985 0, /* tp_traverse */
986 0, /* tp_clear */
987 0, /* tp_richcompare */
988 0, /* tp_weaklistoffset */
989 0, /* tp_iter */
990 0, /* tp_iternext */
991 Notification_methods, /* tp_methods */
992 Notification_members, /* tp_members */
993 Notification_getseters, /* tp_getset */
994 0, /* tp_base */
995 0, /* tp_dict */
996 0, /* tp_descr_get */
997 0, /* tp_descr_set */
998 0, /* tp_dictoffset */
999 Notification_init, /* tp_init */
1000 0, /* tp_alloc */
1001 Notification_new /* tp_new */
1002 };
1004 PyTypeObject SignedType = {
1005 CPY_INIT_TYPE
1006 "collectd.Signed", /* tp_name */
1007 sizeof(Signed), /* tp_basicsize */
1008 0, /* Will be filled in later */
1009 0, /* tp_dealloc */
1010 0, /* tp_print */
1011 0, /* tp_getattr */
1012 0, /* tp_setattr */
1013 0, /* tp_compare */
1014 0, /* tp_repr */
1015 0, /* tp_as_number */
1016 0, /* tp_as_sequence */
1017 0, /* tp_as_mapping */
1018 0, /* tp_hash */
1019 0, /* tp_call */
1020 0, /* tp_str */
1021 0, /* tp_getattro */
1022 0, /* tp_setattro */
1023 0, /* tp_as_buffer */
1024 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1025 };
1027 PyTypeObject UnsignedType = {
1028 CPY_INIT_TYPE
1029 "collectd.Unsigned", /* tp_name */
1030 sizeof(Unsigned), /* tp_basicsize */
1031 0, /* Will be filled in later */
1032 0, /* tp_dealloc */
1033 0, /* tp_print */
1034 0, /* tp_getattr */
1035 0, /* tp_setattr */
1036 0, /* tp_compare */
1037 0, /* tp_repr */
1038 0, /* tp_as_number */
1039 0, /* tp_as_sequence */
1040 0, /* tp_as_mapping */
1041 0, /* tp_hash */
1042 0, /* tp_call */
1043 0, /* tp_str */
1044 0, /* tp_getattro */
1045 0, /* tp_setattro */
1046 0, /* tp_as_buffer */
1047 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1048 };