Code

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