From a6ce5dbab98419162c5430f08261a4bc27bfe3a1 Mon Sep 17 00:00:00 2001 From: Sven Trenkel Date: Sat, 28 Nov 2009 14:17:50 +0100 Subject: [PATCH] Added notification support. --- src/cpython.h | 8 ++ src/python.c | 34 +++++++++ src/pyvalues.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 237 insertions(+), 3 deletions(-) diff --git a/src/cpython.h b/src/cpython.h index 11c06f2f..21ed0062 100644 --- a/src/cpython.h +++ b/src/cpython.h @@ -72,3 +72,11 @@ typedef struct { } Values; PyTypeObject ValuesType; + +typedef struct { + PluginData data; + int severity; + char message[NOTIF_MAX_MSG_LEN]; +} Notification; + +PyTypeObject NotificationType; diff --git a/src/python.c b/src/python.c index 193243bb..f1e7befc 100644 --- a/src/python.c +++ b/src/python.c @@ -200,6 +200,24 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li return 0; } +static int cpy_notification_callback(const notification_t *notification, user_data_t *data) { + cpy_callback_t *c = data->data; + PyObject *ret, *n; + + CPY_LOCK_THREADS + n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message, + notification->plugin_instance, notification->type_instance, notification->plugin, + notification->host, (double) notification->time, notification->severity); + ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */ + if (ret == NULL) { + cpy_log_exception("notification callback"); + } else { + Py_DECREF(ret); + } + CPY_RELEASE_THREADS + return 0; +} + static void cpy_log_callback(int severity, const char *message, user_data_t *data) { cpy_callback_t * c = data->data; PyObject *ret; @@ -358,6 +376,10 @@ static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kw return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0); } +static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) { + return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0); +} + static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) { return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1); } @@ -486,6 +508,10 @@ static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) { return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0); } +static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) { + return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0); +} + static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) { return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1); } @@ -506,6 +532,7 @@ static PyMethodDef cpy_methods[] = { {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, + {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"unregister_log", cpy_unregister_log, METH_O, "This is an unhelpful text."}, @@ -513,6 +540,7 @@ static PyMethodDef cpy_methods[] = { {"unregister_config", cpy_unregister_config, METH_O, "This is an unhelpful text."}, {"unregister_read", cpy_unregister_read, METH_O, "This is an unhelpful text."}, {"unregister_write", cpy_unregister_write, METH_O, "This is an unhelpful text."}, + {"unregister_notification", cpy_unregister_notification, METH_O, "This is an unhelpful text."}, {"unregister_flush", cpy_unregister_flush, METH_O, "This is an unhelpful text."}, {"unregister_shutdown", cpy_unregister_shutdown, METH_O, "This is an unhelpful text."}, {0, 0, 0, 0} @@ -623,6 +651,8 @@ static int cpy_config(oconfig_item_t *ci) { PyType_Ready(&PluginDataType); ValuesType.tp_base = &PluginDataType; PyType_Ready(&ValuesType); + NotificationType.tp_base = &PluginDataType; + PyType_Ready(&NotificationType); sys = PyImport_ImportModule("sys"); /* New reference. */ if (sys == NULL) { cpy_log_exception("python initialization"); @@ -637,11 +667,15 @@ static int cpy_config(oconfig_item_t *ci) { module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */ PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */ PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */ + PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */ PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG); PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO); PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE); PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING); PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR); + PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE); + PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING); + PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY); for (i = 0; i < ci->children_num; ++i) { oconfig_item_t *item = ci->children + i; diff --git a/src/pyvalues.c b/src/pyvalues.c index 08195f39..0d8c5eea 100644 --- a/src/pyvalues.c +++ b/src/pyvalues.c @@ -30,7 +30,7 @@ static char plugin_instance_doc[] = ""; static char PluginData_doc[] = "This is an internal class that is the base for Values\n" "and Notification. It is pretty useless by itself and was therefore not\n" - "not exported to the collectd module."; + "exported to the collectd module."; static PyObject *PluginData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PluginData *self; @@ -163,7 +163,7 @@ PyTypeObject PluginDataType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE /*| Py_TPFLAGS_HAVE_GC*/, /*tp_flags*/ - PluginData_doc , /* tp_doc */ + PluginData_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -205,7 +205,7 @@ static char values_doc[] = "These are the actual values that get dispatched to c static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]" "[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n" "\n" - "Dispatch this instance to the collectd process. A values object a members\n" + "Dispatch this instance to the collectd process. The object has members\n" "for each of the possible arguments for this method. For a detailed explanation\n" "of these parameters see the member of the same same.\n" "\n" @@ -451,3 +451,195 @@ PyTypeObject ValuesType = { 0, /* tp_alloc */ Values_new /* tp_new */ }; + +static char severity_doc[] = "The severity of this notification. Assign or compare to\n" + "NOTIF_FAILURE, NOTIF_WARNING or NOTIF_OKAY."; + +static char message_doc[] = "Some kind of description what's going on and why this Notification was generated."; + +static char Notification_doc[] = "The Notification class is a wrapper around the collectd notification.\n" + "It can be used to notify other plugins about bad stuff happening. It works\n" + "similar to Values but has a severity and a message instead of interval\n" + "and time.\n" + "Notifications can be dispatched at any time and can be received with register_notification."; + +static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) { + Notification *self = (Notification *) s; + PyObject *tmp; + int severity = 0, ret; + double time = 0; + const char *message = ""; + const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = ""; + static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance", + "plugin", "host", "time", "severity", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist, + &type, &message, &plugin_instance, &type_instance, + &plugin, &host, &time, &severity)) + return -1; + + tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time); + if (tmp == NULL) + return -1; + ret = PluginDataType.tp_init(s, tmp, NULL); + Py_DECREF(tmp); + if (ret != 0) + return -1; + + sstrncpy(self->message, message, sizeof(self->message)); + self->severity = severity; + return 0; +} + +static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObject *kwds) { + int ret; + const data_set_t *ds; + notification_t notification; + double t = self->data.time; + int severity = self->severity; + const char *host = self->data.host; + const char *plugin = self->data.plugin; + const char *plugin_instance = self->data.plugin_instance; + const char *type = self->data.type; + const char *type_instance = self->data.type_instance; + const char *message = self->message; + + static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance", + "plugin", "host", "time", "severity", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist, + &type, &message, &plugin_instance, &type_instance, + &plugin, &host, &t, &severity)) + return NULL; + + if (type[0] == 0) { + PyErr_SetString(PyExc_RuntimeError, "type not set"); + return NULL; + } + ds = plugin_get_ds(type); + if (ds == NULL) { + PyErr_Format(PyExc_TypeError, "Dataset %s not found", type); + return NULL; + } + + notification.time = t; + notification.severity = severity; + sstrncpy(notification.message, message, sizeof(notification.message)); + sstrncpy(notification.host, host, sizeof(notification.host)); + sstrncpy(notification.plugin, plugin, sizeof(notification.plugin)); + sstrncpy(notification.plugin_instance, plugin_instance, sizeof(notification.plugin_instance)); + sstrncpy(notification.type, type, sizeof(notification.type)); + sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance)); + notification.meta = NULL; + if (notification.time < 1) + notification.time = time(0); + if (notification.host[0] == 0) + sstrncpy(notification.host, hostname_g, sizeof(notification.host)); + if (notification.plugin[0] == 0) + sstrncpy(notification.plugin, "python", sizeof(notification.plugin)); + Py_BEGIN_ALLOW_THREADS; + ret = plugin_dispatch_notification(¬ification); + Py_END_ALLOW_THREADS; + if (ret != 0) { + PyErr_SetString(PyExc_RuntimeError, "error dispatching notification, read the logs"); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *Notification_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { + Notification *self; + + self = (Notification *) PluginData_new(type, args, kwds); + if (self == NULL) + return NULL; + + self->message[0] = 0; + self->severity = 0; + return (PyObject *) self; +} + +static int Notification_setstring(PyObject *self, PyObject *value, void *data) { + char *old; + const char *new; + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute"); + return -1; + } + new = PyString_AsString(value); + if (new == NULL) return -1; + old = ((char *) self) + (int) data; + sstrncpy(old, new, NOTIF_MAX_MSG_LEN); + return 0; +} + +static PyObject *Notification_repr(PyObject *s) { + PyObject *ret; + Notification *self = (Notification *) s; + + ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type, + *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance, + *self->data.plugin ? "',plugin='" : "", self->data.plugin, + *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance, + *self->data.host ? "',host='" : "", self->data.host, + *self->message ? "',message='" : "", self->message, + (long unsigned) self->data.time, self->severity); + return ret; +} + +static PyMethodDef Notification_methods[] = { + {"dispatch", (PyCFunction) Notification_dispatch, METH_VARARGS | METH_KEYWORDS, dispatch_doc}, + {NULL} +}; + +static PyMemberDef Notification_members[] = { + {"severity", T_INT, offsetof(Notification, severity), 0, severity_doc}, + {NULL} +}; + +static PyGetSetDef Notification_getseters[] = { + {"message", PluginData_getstring, Notification_setstring, message_doc, (void *) offsetof(Notification, message)}, + {NULL} +}; + +PyTypeObject NotificationType = { + PyObject_HEAD_INIT(NULL) + 0, /* Always 0 */ + "collectd.Notification", /* tp_name */ + sizeof(Notification), /* tp_basicsize */ + 0, /* Will be filled in later */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + Notification_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Notification_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Notification_methods, /* tp_methods */ + Notification_members, /* tp_members */ + Notification_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + Notification_init, /* tp_init */ + 0, /* tp_alloc */ + Notification_new /* tp_new */ +}; -- 2.30.2