summaryrefslogtreecommitdiff
path: root/src/python/pmda.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/pmda.c')
-rw-r--r--src/python/pmda.c1098
1 files changed, 1098 insertions, 0 deletions
diff --git a/src/python/pmda.c b/src/python/pmda.c
new file mode 100644
index 0000000..9ff1201
--- /dev/null
+++ b/src/python/pmda.c
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2013-2014 Red Hat.
+ *
+ * This file is part of the "pcp" module, the python interfaces for the
+ * Performance Co-Pilot toolkit.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**************************************************************************\
+** **
+** This C extension module mainly serves the purpose of loading functions **
+** and macros needed to implement PMDAs in python. These are exported to **
+** python PMDAs via the pmda.py module, using ctypes. **
+** **
+\**************************************************************************/
+
+#include <Python.h>
+#include <pcp/pmapi.h>
+#include <pcp/pmda.h>
+#include <pcp/impl.h>
+
+#if PY_MAJOR_VERSION >= 3
+#define MOD_ERROR_VAL NULL
+#define MOD_SUCCESS_VAL(val) val
+#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
+#define MOD_DEF(ob, name, doc, methods) \
+ static struct PyModuleDef moduledef = { \
+ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
+ ob = PyModule_Create(&moduledef);
+#else
+#define MOD_ERROR_VAL
+#define MOD_SUCCESS_VAL(val)
+#define MOD_INIT(name) void init##name(void)
+#define MOD_DEF(ob, name, doc, methods) \
+ ob = Py_InitModule3(name, methods, doc);
+#endif
+
+static pmdaInterface dispatch;
+static __pmnsTree *pmns;
+static int need_refresh;
+static PyObject *pmns_dict; /* metric pmid:names dictionary */
+static PyObject *pmid_oneline_dict; /* metric pmid:short text */
+static PyObject *pmid_longtext_dict; /* metric pmid:long help */
+static PyObject *indom_oneline_dict; /* indom pmid:short text */
+static PyObject *indom_longtext_dict; /* indom pmid:long help */
+
+static PyObject *fetch_func;
+static PyObject *refresh_func;
+static PyObject *instance_func;
+static PyObject *store_cb_func;
+static PyObject *fetch_cb_func;
+
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION <= 5
+typedef int Py_ssize_t;
+#endif
+
+static void
+pmns_refresh(void)
+{
+ int sts, count = 0;
+ Py_ssize_t pos = 0;
+ PyObject *key, *value;
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmns_refresh: rebuilding namespace\n");
+
+ if (pmns)
+ __pmFreePMNS(pmns);
+
+ if ((sts = __pmNewPMNS(&pmns)) < 0) {
+ __pmNotifyErr(LOG_ERR, "failed to create namespace root: %s",
+ pmErrStr(sts));
+ return;
+ }
+
+ while (PyDict_Next(pmns_dict, &pos, &key, &value)) {
+ const char *name;
+ long pmid;
+
+ pmid = PyLong_AsLong(key);
+#if PY_MAJOR_VERSION >= 3
+ name = PyUnicode_AsUTF8(value);
+#else
+ name = PyString_AsString(value);
+#endif
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmns_refresh: adding metric %s(%s)\n",
+ name, pmIDStr(pmid));
+ if ((sts = __pmAddPMNSNode(pmns, pmid, name)) < 0) {
+ __pmNotifyErr(LOG_ERR,
+ "failed to add metric %s(%s) to namespace: %s",
+ name, pmIDStr(pmid), pmErrStr(sts));
+ } else {
+ count++;
+ }
+ }
+
+ pmdaTreeRebuildHash(pmns, count); /* for reverse (pmid->name) lookups */
+ Py_DECREF(pmns_dict);
+ need_refresh = 0;
+ pmns_dict = NULL;
+}
+
+static PyObject *
+namespace_refresh(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *keyword_list[] = {"metrics", NULL};
+
+ if (pmns_dict) {
+ Py_DECREF(pmns_dict);
+ pmns_dict = NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:namespace_refresh", keyword_list, &pmns_dict))
+ return NULL;
+ if (pmns_dict) {
+ if (!PyDict_Check(pmns_dict)) {
+ __pmNotifyErr(LOG_ERR,
+ "attempted to refresh namespace with non-dict type");
+ Py_DECREF(pmns_dict);
+ pmns_dict = NULL;
+ } else if (need_refresh) {
+ pmns_refresh();
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pmid_oneline_refresh(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *keyword_list[] = {"oneline", NULL};
+
+ if (pmid_oneline_dict) {
+ Py_DECREF(pmid_oneline_dict);
+ pmid_oneline_dict = NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:pmid_oneline_refresh",
+ keyword_list, &pmid_oneline_dict))
+ return NULL;
+
+ if (pmid_oneline_dict) {
+ if (!PyDict_Check(pmid_oneline_dict)) {
+ __pmNotifyErr(LOG_ERR,
+ "attempted to refresh pmid oneline help with non-dict type");
+ Py_DECREF(pmid_oneline_dict);
+ pmid_oneline_dict = NULL;
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pmid_longtext_refresh(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *keyword_list[] = {"longtext", NULL};
+
+ if (pmid_longtext_dict) {
+ Py_DECREF(pmid_longtext_dict);
+ pmid_longtext_dict = NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:pmid_longtext_refresh",
+ keyword_list, &pmid_longtext_dict))
+ return NULL;
+
+ if (pmid_longtext_dict) {
+ if (!PyDict_Check(pmid_longtext_dict)) {
+ __pmNotifyErr(LOG_ERR,
+ "attempted to refresh pmid long help with non-dict type");
+ Py_DECREF(pmid_longtext_dict);
+ pmid_longtext_dict = NULL;
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+indom_oneline_refresh(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *keyword_list[] = {"oneline", NULL};
+
+ if (indom_oneline_dict) {
+ Py_DECREF(indom_oneline_dict);
+ indom_oneline_dict = NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:indom_oneline_refresh",
+ keyword_list, &indom_oneline_dict))
+ return NULL;
+
+ if (indom_oneline_dict) {
+ if (!PyDict_Check(indom_oneline_dict)) {
+ __pmNotifyErr(LOG_ERR,
+ "attempted to refresh indom oneline help with non-dict type");
+ Py_DECREF(indom_oneline_dict);
+ indom_oneline_dict = NULL;
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+indom_longtext_refresh(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *keyword_list[] = {"longtext", NULL};
+
+ if (indom_longtext_dict) {
+ Py_DECREF(indom_longtext_dict);
+ indom_longtext_dict = NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:indom_longtext_refresh",
+ keyword_list, &indom_longtext_dict))
+ return NULL;
+
+ if (indom_longtext_dict) {
+ if (!PyDict_Check(indom_longtext_dict)) {
+ __pmNotifyErr(LOG_ERR,
+ "attempted to refresh indom long help with non-dict type");
+ Py_DECREF(indom_longtext_dict);
+ indom_longtext_dict = NULL;
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+int
+pmns_desc(pmID pmid, pmDesc *desc, pmdaExt *ep)
+{
+ if (need_refresh)
+ pmns_refresh();
+ return pmdaDesc(pmid, desc, ep);
+}
+
+int
+pmns_pmid(const char *name, pmID *pmid, pmdaExt *pmda)
+{
+ if (need_refresh)
+ pmns_refresh();
+ return pmdaTreePMID(pmns, name, pmid);
+}
+
+int
+pmns_name(pmID pmid, char ***nameset, pmdaExt *pmda)
+{
+ if (need_refresh)
+ pmns_refresh();
+ return pmdaTreeName(pmns, pmid, nameset);
+}
+
+int
+pmns_children(const char *name, int traverse, char ***kids, int **sts, pmdaExt *pmda)
+{
+ if (need_refresh)
+ pmns_refresh();
+ return pmdaTreeChildren(pmns, name, traverse, kids, sts);
+}
+
+static int
+prefetch(void)
+{
+ PyObject *arglist, *result;
+
+ arglist = Py_BuildValue("()");
+ if (arglist == NULL)
+ return -ENOMEM;
+ result = PyEval_CallObject(fetch_func, arglist);
+ Py_DECREF(arglist);
+ if (!result) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+static int
+refresh_cluster(int cluster)
+{
+ PyObject *arglist, *result;
+
+ arglist = Py_BuildValue("(i)", cluster);
+ if (arglist == NULL)
+ return -ENOMEM;
+ result = PyEval_CallObject(refresh_func, arglist);
+ Py_DECREF(arglist);
+ if (result == NULL) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+static int
+refresh(int numpmid, pmID *pmidlist)
+{
+ size_t need;
+ int *clusters = NULL;
+ int i, j, count = 0;
+ int sts = 0;
+
+ /*
+ * Invoke a callback once for each affected PMID cluster (and not for the
+ * unaffected clusters). This allows specific subsets of metric values to
+ * be refreshed, rather than just blindly fetching everything at the start
+ * of a fetch request. Accomplish this by building an array of the unique
+ * cluster numbers from the given PMID list.
+ */
+ need = sizeof(int) * numpmid; /* max cluster count */
+ if ((clusters = malloc(need)) == NULL)
+ return -ENOMEM;
+ for (i = 0; i < numpmid; i++) {
+ int cluster = pmid_cluster(pmidlist[i]);
+ for (j = 0; j < count; j++)
+ if (clusters[j] == cluster)
+ break;
+ if (j == count)
+ clusters[count++] = cluster;
+ }
+ for (j = 0; j < count; j++)
+ sts |= refresh_cluster(clusters[j]);
+ free(clusters);
+ return sts;
+}
+
+static int
+fetch(int numpmid, pmID *pmidlist, pmResult **rp, pmdaExt *pmda)
+{
+ int sts;
+
+ if (need_refresh)
+ pmns_refresh();
+ if (fetch_func && (sts = prefetch()) < 0)
+ return sts;
+ if (refresh_func && (sts = refresh(numpmid, pmidlist)) < 0)
+ return sts;
+ return pmdaFetch(numpmid, pmidlist, rp, pmda);
+}
+
+static int
+preinstance(pmInDom indom)
+{
+ PyObject *arglist, *result;
+
+ arglist = Py_BuildValue("(i)", pmInDom_serial(indom));
+ if (arglist == NULL)
+ return -ENOMEM;
+ result = PyEval_CallObject(instance_func, arglist);
+ Py_DECREF(arglist);
+ if (result == NULL) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ }
+ Py_DECREF(result);
+ return 0;
+}
+
+int
+instance(pmInDom indom, int a, char *b, __pmInResult **rp, pmdaExt *pmda)
+{
+ int sts;
+
+ if (need_refresh)
+ pmns_refresh();
+ if (instance_func && (sts = preinstance(indom)) < 0)
+ return sts;
+ return pmdaInstance(indom, a, b, rp, pmda);
+}
+
+int
+fetch_callback(pmdaMetric *metric, unsigned int inst, pmAtomValue *atom)
+{
+ char *s;
+ int rc, sts, code;
+ PyObject *arglist, *result;
+ __pmID_int *pmid = (__pmID_int *)&metric->m_desc.pmid;
+
+ if (fetch_cb_func == NULL)
+ return PM_ERR_VALUE;
+
+ arglist = Py_BuildValue("(iiI)", pmid->cluster, pmid->item, inst);
+ if (arglist == NULL) {
+ __pmNotifyErr(LOG_ERR, "fetch callback cannot alloc parameters");
+ return -EINVAL;
+ }
+ result = PyEval_CallObject(fetch_cb_func, arglist);
+ Py_DECREF(arglist);
+ if (result == NULL) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ } else if (PyTuple_Check(result)) {
+ __pmNotifyErr(LOG_ERR, "non-tuple returned from fetch callback");
+ Py_DECREF(result);
+ return -EINVAL;
+ }
+ rc = code = 0;
+ sts = PMDA_FETCH_STATIC;
+ switch (metric->m_desc.type) {
+ case PM_TYPE_32:
+ rc = PyArg_Parse(result, "(ii):fetch_cb_s32", &atom->l, &code);
+ break;
+ case PM_TYPE_U32:
+ rc = PyArg_Parse(result, "(Ii):fetch_cb_u32", &atom->ul, &code);
+ break;
+ case PM_TYPE_64:
+ rc = PyArg_Parse(result, "(Li):fetch_cb_s64", &atom->ll, &code);
+ break;
+ case PM_TYPE_U64:
+ rc = PyArg_Parse(result, "(Ki):fetch_cb_u64", &atom->ull, &code);
+ break;
+ case PM_TYPE_FLOAT:
+ rc = PyArg_Parse(result, "(fi):fetch_cb_float", &atom->f, &code);
+ break;
+ case PM_TYPE_DOUBLE:
+ rc = PyArg_Parse(result, "(di):fetch_cb_double", &atom->d, &code);
+ break;
+ case PM_TYPE_STRING:
+ s = NULL;
+ rc = PyArg_Parse(result, "(si):fetch_cb_string", &s, &code);
+ if (rc == 0)
+ break;
+ if (s == NULL)
+ sts = PM_ERR_VALUE;
+ else if ((atom->cp = strdup(s)) == NULL)
+ sts = -ENOMEM;
+ else
+ sts = PMDA_FETCH_DYNAMIC;
+ break;
+ default:
+ __pmNotifyErr(LOG_ERR, "unsupported metric type in fetch callback");
+ sts = -ENOTSUP;
+ }
+
+ if (!rc || !code) { /* tuple not parsed or atom contains bad value */
+ if (!PyArg_Parse(result, "(ii):fetch_cb_error", &sts, &code)) {
+ __pmNotifyErr(LOG_ERR, "extracting error code in fetch callback");
+ sts = -EINVAL;
+ }
+ }
+ Py_DECREF(result);
+ return sts;
+}
+
+int
+store_callback(__pmID_int *pmid, unsigned int inst, pmAtomValue av, int type)
+{
+ int rc, code;
+ int item = pmid->item;
+ int cluster = pmid->cluster;
+ PyObject *arglist, *result;
+
+ switch (type) {
+ case PM_TYPE_32:
+ arglist = Py_BuildValue("(iiIi)", cluster, item, inst, av.l);
+ break;
+ case PM_TYPE_U32:
+ arglist = Py_BuildValue("(iiII)", cluster, item, inst, av.ul);
+ break;
+ case PM_TYPE_64:
+ arglist = Py_BuildValue("(iiIL)", cluster, item, inst, av.ll);
+ break;
+ case PM_TYPE_U64:
+ arglist = Py_BuildValue("(iiIK)", cluster, item, inst, av.ull);
+ break;
+ case PM_TYPE_FLOAT:
+ arglist = Py_BuildValue("(iiIf)", cluster, item, inst, av.f);
+ break;
+ case PM_TYPE_DOUBLE:
+ arglist = Py_BuildValue("(iiId)", cluster, item, inst, av.d);
+ break;
+ case PM_TYPE_STRING:
+ arglist = Py_BuildValue("(iiIs)", cluster, item, inst, av.cp);
+ break;
+ default:
+ __pmNotifyErr(LOG_ERR, "unsupported type in store callback");
+ return -EINVAL;
+ }
+ result = PyEval_CallObject(store_cb_func, arglist);
+ Py_DECREF(arglist);
+ if (!result) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ }
+ rc = PyArg_Parse(result, "i:store_callback", &code);
+ Py_DECREF(result);
+ if (rc == 0) {
+ __pmNotifyErr(LOG_ERR, "store callback gave bad status (int expected)");
+ return -EINVAL;
+ }
+ return code;
+}
+
+static pmdaMetric *
+lookup_metric(__pmID_int *pmid, pmdaExt *pmda)
+{
+ int i;
+ pmdaMetric *mp;
+
+ for (i = 0; i < pmda->e_nmetrics; i++) {
+ mp = &pmda->e_metrics[i];
+ if (pmid->item != pmid_item(mp->m_desc.pmid))
+ continue;
+ if (pmid->cluster != pmid_cluster(mp->m_desc.pmid))
+ continue;
+ return mp;
+ }
+ return NULL;
+}
+
+int
+store(pmResult *result, pmdaExt *pmda)
+{
+ int i, j;
+ int type;
+ int sts;
+ pmAtomValue av;
+ pmdaMetric *mp;
+ pmValueSet *vsp;
+ __pmID_int *pmid;
+
+ if (need_refresh)
+ pmns_refresh();
+
+ if (store_cb_func == NULL)
+ return PM_ERR_PERMISSION;
+
+ for (i = 0; i < result->numpmid; i++) {
+ vsp = result->vset[i];
+ pmid = (__pmID_int *)&vsp->pmid;
+
+ /* find the type associated with this PMID */
+ if ((mp = lookup_metric(pmid, pmda)) == NULL)
+ return PM_ERR_PMID;
+ type = mp->m_desc.type;
+
+ for (j = 0; j < vsp->numval; j++) {
+ sts = pmExtractValue(vsp->valfmt, &vsp->vlist[j],type, &av, type);
+ if (sts < 0)
+ return sts;
+ sts = store_callback(pmid, vsp->vlist[j].inst, av, type);
+ if (sts < 0)
+ return sts;
+ }
+ }
+ return 0;
+}
+
+int
+text(int ident, int type, char **buffer, pmdaExt *pmda)
+{
+ PyObject *dict, *value, *key;
+
+ if (need_refresh)
+ pmns_refresh();
+
+ if ((type & PM_TEXT_PMID) != 0) {
+ if ((type & PM_TEXT_ONELINE) != 0)
+ dict = pmid_oneline_dict;
+ else
+ dict = pmid_longtext_dict;
+ } else {
+ if ((type & PM_TEXT_ONELINE) != 0)
+ dict = indom_oneline_dict;
+ else
+ dict = indom_longtext_dict;
+ }
+
+ key = PyLong_FromLong((long)ident);
+ if (!key)
+ return PM_ERR_TEXT;
+ value = PyDict_GetItem(dict, key);
+ Py_DECREF(key);
+ if (value == NULL)
+ return PM_ERR_TEXT;
+#if PY_MAJOR_VERSION >= 3
+ *buffer = PyUnicode_AsUTF8(value);
+#else
+ *buffer = PyString_AsString(value);
+#endif
+ /* "value" is a borrowed reference, do not decrement */
+ return 0;
+}
+
+int
+attribute(int ctx, int attr, const char *value, int length, pmdaExt *pmda)
+{
+ if (pmDebug & DBG_TRACE_AUTH) {
+ char buffer[256];
+
+ if (!__pmAttrStr_r(attr, value, buffer, sizeof(buffer))) {
+ __pmNotifyErr(LOG_ERR, "Bad Attribute: ctx=%d, attr=%d\n", ctx, attr);
+ } else {
+ buffer[sizeof(buffer)-1] = '\0';
+ __pmNotifyErr(LOG_INFO, "Attribute: ctx=%d %s", ctx, buffer);
+ }
+ }
+ /* handle connection attributes - need per-connection state code */
+ return 0;
+}
+
+/*
+ * Allocate a new PMDA dispatch structure and fill it
+ * in for the agent we have been asked to instantiate.
+ */
+
+static inline int
+pmda_generating_pmns(void) { return getenv("PCP_PYTHON_PMNS") != NULL; }
+
+static inline int
+pmda_generating_domain(void) { return getenv("PCP_PYTHON_DOMAIN") != NULL; }
+
+static PyObject *
+init_dispatch(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int domain;
+ char *p, *name, *help, *logfile, *pmdaname;
+ char *keyword_list[] = {"domain", "name", "log", "help", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "isss:init_dispatch", keyword_list,
+ &domain, &pmdaname, &logfile, &help))
+ return NULL;
+
+ name = strdup(pmdaname);
+ __pmSetProgname(name);
+ if ((p = getenv("PCP_PYTHON_DEBUG")) != NULL)
+ if ((pmDebug = __pmParseDebug(p)) < 0)
+ pmDebug = 0;
+
+ if (access(help, R_OK) != 0) {
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_6, name, domain, logfile, NULL);
+ dispatch.version.four.text = text;
+ } else {
+ p = strdup(help);
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_6, name, domain, logfile, p);
+ }
+ dispatch.version.six.fetch = fetch;
+ dispatch.version.six.store = store;
+ dispatch.version.six.instance = instance;
+ dispatch.version.six.desc = pmns_desc;
+ dispatch.version.six.pmid = pmns_pmid;
+ dispatch.version.six.name = pmns_name;
+ dispatch.version.six.children = pmns_children;
+ dispatch.version.six.attribute = attribute;
+ pmdaSetFetchCallBack(&dispatch, fetch_callback);
+
+ if (!pmda_generating_pmns() && !pmda_generating_domain())
+ pmdaOpenLog(&dispatch);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+connect_pmcd(void)
+{
+ /*
+ * Need to mimic the same special cases handled by run() in
+ * pcp/pmda.py that explicitly do NOT connect to pmcd and treat
+ * these as no-ops here.
+ *
+ * Otherwise call pmdaConnect() to complete the PMDA's IPC
+ * channel setup and complete the connection handshake with
+ * pmcd.
+ */
+ if (!pmda_generating_pmns() && !pmda_generating_domain()) {
+ /*
+ * On success pmdaConnect sets PMDA_EXT_CONNECTED in e_flags ...
+ * this used in the guard below to stop pmda_dispatch() calling
+ * pmdaConnect() again.
+ */
+ pmdaConnect(&dispatch);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef PyBUF_SIMPLE
+static PyObject *
+pmda_dispatch(PyObject *self, PyObject *args)
+{
+ int nindoms, nmetrics;
+ PyObject *ibuf, *mbuf;
+ pmdaMetric *metrics;
+ pmdaIndom *indoms;
+ Py_buffer mv, iv;
+
+ if (!PyArg_ParseTuple(args, "OiOi", &ibuf, &nindoms, &mbuf, &nmetrics))
+ return NULL;
+
+ if (!PyObject_CheckBuffer(ibuf)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch expected buffer 1st arg");
+ return NULL;
+ }
+ if (!PyObject_CheckBuffer(mbuf)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch expected buffer 3rd arg");
+ return NULL;
+ }
+
+ if (PyObject_GetBuffer(ibuf, &iv, PyBUF_SIMPLE)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch failed to get indoms");
+ return NULL;
+ }
+ if (PyObject_GetBuffer(mbuf, &mv, PyBUF_SIMPLE)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch failed to get metrics");
+ PyBuffer_Release(&iv);
+ return NULL;
+ }
+
+ if (iv.len != nindoms * sizeof(pmdaIndom)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch: invalid indom array");
+ PyBuffer_Release(&iv);
+ PyBuffer_Release(&mv);
+ return NULL;
+ }
+ if (mv.len != nmetrics * sizeof(pmdaMetric)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch: invalid metric array");
+ PyBuffer_Release(&iv);
+ PyBuffer_Release(&mv);
+ return NULL;
+ }
+
+ indoms = nindoms ? (pmdaIndom *)iv.buf : NULL;
+ metrics = nmetrics ? (pmdaMetric *)mv.buf : NULL;
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch pmdaInit for metrics/indoms\n");
+ pmdaInit(&dispatch, indoms, nindoms, metrics, nmetrics);
+ if ((dispatch.version.any.ext->e_flags & PMDA_EXT_CONNECTED) != PMDA_EXT_CONNECTED) {
+ /*
+ * connect_pmcd() not called before, so need pmdaConnect()
+ * here before falling into the PDU-driven pmdaMain() loop.
+ */
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch connect to pmcd\n");
+ pmdaConnect(&dispatch);
+ }
+
+ PyBuffer_Release(&iv);
+ PyBuffer_Release(&mv);
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch entering PDU loop\n");
+
+ pmdaMain(&dispatch);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#else /* old-school python */
+static PyObject *
+pmda_dispatch(PyObject *self, PyObject *args)
+{
+ int nindoms, nmetrics, size;
+ PyObject *ibuf, *mbuf, *iv, *mv;
+ pmdaMetric *metrics = NULL;
+ pmdaIndom *indoms = NULL;
+
+ if (!PyArg_ParseTuple(args, "OiOi", &ibuf, &nindoms, &mbuf, &nmetrics))
+ return NULL;
+
+ size = nindoms * sizeof(pmdaIndom);
+ if ((iv = PyBuffer_FromObject(ibuf, 0, size)) == NULL) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch indom extraction");
+ return NULL;
+ }
+ if (!PyBuffer_Check(iv)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch wants buffer 1st arg");
+ return NULL;
+ }
+ size = nmetrics * sizeof(pmdaMetric);
+ if ((mv = PyBuffer_FromObject(mbuf, 0, size)) == NULL) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch metric extraction");
+ return NULL;
+ }
+ if (!PyBuffer_Check(mv)) {
+ PyErr_SetString(PyExc_TypeError, "pmda_dispatch wants buffer 3rd arg");
+ return NULL;
+ }
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch pmdaInit for metrics/indoms\n");
+
+ PyBuffer_Type.tp_as_buffer->bf_getreadbuffer(iv, 0, (void *)&indoms);
+ PyBuffer_Type.tp_as_buffer->bf_getreadbuffer(mv, 0, (void *)&metrics);
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch pmdaInit for metrics/indoms\n");
+ pmdaInit(&dispatch, indoms, nindoms, metrics, nmetrics);
+ if ((dispatch.version.any.ext->e_flags & PMDA_EXT_CONNECTED) != PMDA_EXT_CONNECTED) {
+ /*
+ * connect_pmcd() not called before, so need pmdaConnect()
+ * here before falling into the PDU-driven pmdaMain() loop.
+ */
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch connect to pmcd\n");
+ pmdaConnect(&dispatch);
+ }
+
+ Py_DECREF(ibuf);
+ Py_DECREF(mbuf);
+
+ if (pmDebug & DBG_TRACE_LIBPMDA)
+ fprintf(stderr, "pmda_dispatch entering PDU loop\n");
+
+ pmdaMain(&dispatch);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#endif
+
+static PyObject *
+pmda_log(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *message;
+ char *keyword_list[] = {"message", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmda_log", keyword_list, &message))
+ return NULL;
+ __pmNotifyErr(LOG_INFO, "%s", message);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pmda_err(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *message;
+ char *keyword_list[] = {"message", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmda_err", keyword_list, &message))
+ return NULL;
+ __pmNotifyErr(LOG_ERR, "%s", message);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pmda_pmid(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int result;
+ int cluster, item;
+ char *keyword_list[] = {"item", "cluster", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "ii:pmda_pmid", keyword_list,
+ &item, &cluster))
+ return NULL;
+ result = pmid_build(dispatch.domain, item, cluster);
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *
+pmda_indom(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int result;
+ int serial;
+ char *keyword_list[] = {"serial", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:pmda_indom", keyword_list, &serial))
+ return NULL;
+ result = pmInDom_build(dispatch.domain, serial);
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *
+pmda_units(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int result;
+ int dim_time, dim_space, dim_count;
+ int scale_space, scale_time, scale_count;
+ char *keyword_list[] = {"dim_time", "dim_space", "dim_count",
+ "scale_space", "scale_time", "scale_count", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "iiiiii:pmda_units", keyword_list,
+ &dim_time, &dim_space, &dim_count,
+ &scale_space, &scale_time, &scale_count))
+ return NULL;
+ {
+ pmUnits units = PMDA_PMUNITS(dim_time, dim_space, dim_count,
+ scale_space, scale_time, scale_count);
+ memcpy(&result, &units, sizeof(result));
+ }
+ return Py_BuildValue("i", result);
+}
+
+static PyObject *
+pmda_uptime(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ static char s[32];
+ size_t sz = sizeof(s);
+ int now, days, hours, mins, secs;
+ char *keyword_list[] = {"seconds", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:pmda_uptime", keyword_list, &now))
+ return NULL;
+
+ days = now / (60 * 60 * 24);
+ now %= (60 * 60 * 24);
+ hours = now / (60 * 60);
+ now %= (60 * 60);
+ mins = now / 60;
+ now %= 60;
+ secs = now;
+
+ if (days > 1)
+ snprintf(s, sz, "%ddays %02d:%02d:%02d", days, hours, mins, secs);
+ else if (days == 1)
+ snprintf(s, sz, "%dday %02d:%02d:%02d", days, hours, mins, secs);
+ else
+ snprintf(s, sz, "%02d:%02d:%02d", hours, mins, secs);
+
+ return Py_BuildValue("s", s);
+}
+
+static PyObject *
+set_need_refresh(PyObject *self, PyObject *args)
+{
+ need_refresh = 1;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+set_callback(PyObject *self, PyObject *args, char *params, PyObject **callback)
+{
+ PyObject *func;
+
+ if (!PyArg_ParseTuple(args, params, &func))
+ return NULL;
+ if (!PyCallable_Check(func)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+ Py_XINCREF(func);
+ Py_XDECREF(*callback);
+ *callback = func;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+set_fetch(PyObject *self, PyObject *args)
+{
+ return set_callback(self, args, "O:set_fetch", &fetch_func);
+}
+
+static PyObject *
+set_refresh(PyObject *self, PyObject *args)
+{
+ return set_callback(self, args, "O:set_refresh", &refresh_func);
+}
+
+static PyObject *
+set_instance(PyObject *self, PyObject *args)
+{
+ return set_callback(self, args, "O:set_instance", &instance_func);
+}
+
+static PyObject *
+set_store_callback(PyObject *self, PyObject *args)
+{
+ return set_callback(self, args, "O:set_store_callback", &store_cb_func);
+}
+
+static PyObject *
+set_fetch_callback(PyObject *self, PyObject *args)
+{
+ return set_callback(self, args, "O:set_fetch_callback", &fetch_cb_func);
+}
+
+
+static PyMethodDef methods[] = {
+ { .ml_name = "pmda_pmid", .ml_meth = (PyCFunction)pmda_pmid,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_indom", .ml_meth = (PyCFunction)pmda_indom,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_units", .ml_meth = (PyCFunction)pmda_units,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_uptime", .ml_meth = (PyCFunction)pmda_uptime,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "init_dispatch", .ml_meth = (PyCFunction)init_dispatch,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_dispatch", .ml_meth = (PyCFunction)pmda_dispatch,
+ .ml_flags = METH_VARARGS },
+ { .ml_name = "connect_pmcd", .ml_meth = (PyCFunction)connect_pmcd,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmns_refresh", .ml_meth = (PyCFunction)namespace_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmid_oneline_refresh",
+ .ml_meth = (PyCFunction)pmid_oneline_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmid_longtext_refresh",
+ .ml_meth = (PyCFunction)pmid_longtext_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "indom_oneline_refresh",
+ .ml_meth = (PyCFunction)indom_oneline_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "indom_longtext_refresh",
+ .ml_meth = (PyCFunction)indom_longtext_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "set_need_refresh", .ml_meth = (PyCFunction)set_need_refresh,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "set_fetch", .ml_meth = (PyCFunction)set_fetch,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "set_refresh", .ml_meth = (PyCFunction)set_refresh,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "set_instance", .ml_meth = (PyCFunction)set_instance,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "set_store_callback", .ml_meth = (PyCFunction)set_store_callback,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "set_fetch_callback", .ml_meth = (PyCFunction)set_fetch_callback,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_log", .ml_meth = (PyCFunction)pmda_log,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { .ml_name = "pmda_err", .ml_meth = (PyCFunction)pmda_err,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS },
+ { NULL },
+};
+
+static void
+pmda_dict_add(PyObject *dict, char *sym, long val)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyVal = PyLong_FromLong(val);
+#else
+ PyObject *pyVal = PyInt_FromLong(val);
+#endif
+
+ PyDict_SetItemString(dict, sym, pyVal);
+ Py_XDECREF(pyVal);
+}
+
+/* called when the module is initialized. */
+MOD_INIT(cpmda)
+{
+ PyObject *module, *dict;
+
+ MOD_DEF(module, "cpmda", NULL, methods);
+ if (module == NULL)
+ return MOD_ERROR_VAL;
+
+ dict = PyModule_GetDict(module);
+
+ /* pmda.h - fetch callback return codes */
+ pmda_dict_add(dict, "PMDA_FETCH_NOVALUES", PMDA_FETCH_NOVALUES);
+ pmda_dict_add(dict, "PMDA_FETCH_STATIC", PMDA_FETCH_STATIC);
+ pmda_dict_add(dict, "PMDA_FETCH_DYNAMIC", PMDA_FETCH_DYNAMIC);
+
+ /* pmda.h - indom cache operation codes */
+ pmda_dict_add(dict, "PMDA_CACHE_LOAD", PMDA_CACHE_LOAD);
+ pmda_dict_add(dict, "PMDA_CACHE_ADD", PMDA_CACHE_ADD);
+ pmda_dict_add(dict, "PMDA_CACHE_HIDE", PMDA_CACHE_HIDE);
+ pmda_dict_add(dict, "PMDA_CACHE_CULL", PMDA_CACHE_CULL);
+ pmda_dict_add(dict, "PMDA_CACHE_EMPTY", PMDA_CACHE_EMPTY);
+ pmda_dict_add(dict, "PMDA_CACHE_SAVE", PMDA_CACHE_SAVE);
+ pmda_dict_add(dict, "PMDA_CACHE_ACTIVE", PMDA_CACHE_ACTIVE);
+ pmda_dict_add(dict, "PMDA_CACHE_INACTIVE", PMDA_CACHE_INACTIVE);
+ pmda_dict_add(dict, "PMDA_CACHE_SIZE", PMDA_CACHE_SIZE);
+ pmda_dict_add(dict, "PMDA_CACHE_SIZE_ACTIVE", PMDA_CACHE_SIZE_ACTIVE);
+ pmda_dict_add(dict, "PMDA_CACHE_SIZE_INACTIVE", PMDA_CACHE_SIZE_INACTIVE);
+ pmda_dict_add(dict, "PMDA_CACHE_REUSE", PMDA_CACHE_REUSE);
+ pmda_dict_add(dict, "PMDA_CACHE_WALK_REWIND", PMDA_CACHE_WALK_REWIND);
+ pmda_dict_add(dict, "PMDA_CACHE_WALK_NEXT", PMDA_CACHE_WALK_NEXT);
+ pmda_dict_add(dict, "PMDA_CACHE_CHECK", PMDA_CACHE_CHECK);
+ pmda_dict_add(dict, "PMDA_CACHE_REORG", PMDA_CACHE_REORG);
+ pmda_dict_add(dict, "PMDA_CACHE_SYNC", PMDA_CACHE_SYNC);
+ pmda_dict_add(dict, "PMDA_CACHE_DUMP", PMDA_CACHE_DUMP);
+ pmda_dict_add(dict, "PMDA_CACHE_DUMP_ALL", PMDA_CACHE_DUMP_ALL);
+
+ return MOD_SUCCESS_VAL(module);
+}