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