summaryrefslogtreecommitdiff
path: root/src/python
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/python
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/python')
-rw-r--r--src/python/GNUmakefile47
-rw-r--r--src/python/mmv.c92
-rw-r--r--src/python/pcp/GNUmakefile25
-rw-r--r--src/python/pcp/__init__.py1
-rw-r--r--src/python/pcp/mmv.py332
-rw-r--r--src/python/pcp/pmapi.py1866
-rw-r--r--src/python/pcp/pmcc.py620
-rw-r--r--src/python/pcp/pmda.py407
-rw-r--r--src/python/pcp/pmgui.py174
-rw-r--r--src/python/pcp/pmi.py312
-rw-r--r--src/python/pcp/pmsubsys.py355
-rw-r--r--src/python/pmapi.c1364
-rw-r--r--src/python/pmda.c1098
-rw-r--r--src/python/pmgui.c81
-rw-r--r--src/python/pmi.c115
-rw-r--r--src/python/setup.py61
16 files changed, 6950 insertions, 0 deletions
diff --git a/src/python/GNUmakefile b/src/python/GNUmakefile
new file mode 100644
index 0000000..3c2df66
--- /dev/null
+++ b/src/python/GNUmakefile
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2012-2013 Red Hat.
+#
+# 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.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+SUBDIRS = pcp
+CFILES = pmapi.c pmda.c pmgui.c pmi.c mmv.c
+LSRCFILES = setup.py
+LDIRDIRT = build dist
+LDIRT = build_python
+ifneq ($(CFLAGS_ABI),)
+ENV = CFLAGS=$(CFLAGS_ABI)
+else
+ENV =
+endif
+
+default default_pcp: build_python
+
+ifneq "$(PYTHON)" ""
+build_python: setup.py $(CFILES)
+ $(ENV) $(PYTHON) setup.py build_ext $(SETUP_PY_BUILD_OPTIONS)
+ $(ENV) $(PYTHON) setup.py build
+ touch build_python
+
+install_python: default
+ $(PYTHON) setup.py install $(SETUP_PY_INSTALL_OPTIONS)
+ $(PYTHON_INSTALL)
+else
+build_python:
+install_python:
+endif
+
+include $(BUILDRULES)
+
+install_pcp install: install_python
diff --git a/src/python/mmv.c b/src/python/mmv.c
new file mode 100644
index 0000000..220e7e8
--- /dev/null
+++ b/src/python/mmv.c
@@ -0,0 +1,92 @@
+/*
+ * 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 constants **
+** from PCP headers into the module dictionary. The MMV functions and **
+** data structures are wrapped in mmv.py using ctypes. **
+** **
+\**************************************************************************/
+
+#include <Python.h>
+#include <pcp/pmapi.h>
+#include <pcp/mmv_stats.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 void
+dict_add(PyObject *dict, char *symbol, long value)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyvalue = PyLong_FromLong(value);
+#else
+ PyObject *pyvalue = PyInt_FromLong(value);
+#endif
+ PyDict_SetItemString(dict, symbol, pyvalue);
+ Py_XDECREF(pyvalue);
+}
+
+static PyMethodDef methods[] = { { NULL } };
+
+/* called when the module is initialized. */
+MOD_INIT(cmmv)
+{
+ PyObject *module, *dict;
+
+ MOD_DEF(module, "cmmv", NULL, methods);
+ if (module == NULL)
+ return MOD_ERROR_VAL;
+
+ dict = PyModule_GetDict(module);
+
+ dict_add(dict, "MMV_NAMEMAX", MMV_NAMEMAX);
+ dict_add(dict, "MMV_STRINGMAX", MMV_STRINGMAX);
+
+ dict_add(dict, "MMV_TYPE_NOSUPPORT", MMV_TYPE_NOSUPPORT);
+ dict_add(dict, "MMV_TYPE_I32", MMV_TYPE_I32);
+ dict_add(dict, "MMV_TYPE_U32", MMV_TYPE_U32);
+ dict_add(dict, "MMV_TYPE_I64", MMV_TYPE_I64);
+ dict_add(dict, "MMV_TYPE_U64", MMV_TYPE_U64);
+ dict_add(dict, "MMV_TYPE_FLOAT", MMV_TYPE_FLOAT);
+ dict_add(dict, "MMV_TYPE_DOUBLE", MMV_TYPE_DOUBLE);
+ dict_add(dict, "MMV_TYPE_STRING", MMV_TYPE_STRING);
+ dict_add(dict, "MMV_TYPE_ELAPSED", MMV_TYPE_ELAPSED);
+
+ dict_add(dict, "MMV_SEM_COUNTER", MMV_SEM_COUNTER);
+ dict_add(dict, "MMV_SEM_INSTANT", MMV_SEM_INSTANT);
+ dict_add(dict, "MMV_SEM_DISCRETE", MMV_SEM_DISCRETE);
+
+ dict_add(dict, "MMV_FLAG_NOPREFIX", MMV_FLAG_NOPREFIX);
+ dict_add(dict, "MMV_FLAG_PROCESS", MMV_FLAG_PROCESS);
+
+ return MOD_SUCCESS_VAL(module);
+}
diff --git a/src/python/pcp/GNUmakefile b/src/python/pcp/GNUmakefile
new file mode 100644
index 0000000..492d6dd
--- /dev/null
+++ b/src/python/pcp/GNUmakefile
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2012-2013 Red Hat.
+#
+# 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.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+LSRCFILES = __init__.py pmapi.py pmcc.py pmda.py pmgui.py pmi.py mmv.py \
+ pmsubsys.py
+
+default default_pcp:
+
+include $(BUILDRULES)
+
+install_pcp install:
diff --git a/src/python/pcp/__init__.py b/src/python/pcp/__init__.py
new file mode 100644
index 0000000..708e08a
--- /dev/null
+++ b/src/python/pcp/__init__.py
@@ -0,0 +1 @@
+__all__ = ["pmapi", "pmcc", "pmda", "pmgui", "pmi", "pmsubsys", "mmv", "cpmapi", "cpmda", "cpmgui", "cpmi", "cmmv"]
diff --git a/src/python/pcp/mmv.py b/src/python/pcp/mmv.py
new file mode 100644
index 0000000..426c2d8
--- /dev/null
+++ b/src/python/pcp/mmv.py
@@ -0,0 +1,332 @@
+# pylint: disable=C0103
+"""Wrapper module for libpcp_mmv - PCP Memory Mapped Values library
+#
+# Copyright (C) 2013 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.
+#
+
+# Example use of this module for instrumenting a python application:
+
+ from pcp import mmv, pmapi
+ from cpmapi import PM_COUNT_ONE, PM_TIME_USEC
+
+ instances = [mmv.mmv_instance(0, "zero"), mmv.mmv_instance(1, "hero")]
+ indoms = [mmv.mmv_indom(serial = 1,
+ shorttext = "We can be heroes",
+ helptext = "Set of instances from zero to hero"),
+ indoms[0].set_instances(instances)
+ metrics = [mmv.mmv_metric(name = "counter",
+ item = 1,
+ typeof = mmv.MMV_TYPE_U32,
+ semantics = mmv.MMV_SEM_COUNTER,
+ dimension = pmapi.pmUnits(0,0,1,0,0,PM_COUNT_ONE),
+ shorttext = "Example counter metric",
+ helptext = "Yep, a test counter metric"),
+ mmv.mmv_metric(name = "instant",
+ item = 2,
+ typeof = mmv.MMV_TYPE_I32,
+ semantics = mmv.MMV_SEM_INSTANT,
+ dimension = pmapi.pmUnits(0,0,0,0,0,0),
+ shorttext = "Example instant metric",
+ helptext = "Yep, a test instantaneous metric"),
+ mmv.mmv_metric(name = "indom",
+ item = 3,
+ typeof = mmv.MMV_TYPE_U32,
+ semantics = mmv.MMV_SEM_DISCRETE,
+ dimension = pmapi.pmUnits(0,0,0,0,0,0),
+ indom = 1)]
+
+ values = mmv.MemoryMappedValues("demo")
+ values.add_indoms(indoms)
+ values.add_metrics(metrics)
+
+ values.start()
+ instant = values.lookup_mapping("instant", None)
+ values.set(instant, 41)
+ values.inc(instant)
+ values.stop()
+"""
+
+from pcp.pmapi import pmUnits, pmAtomValue
+from cmmv import MMV_NAMEMAX
+
+import ctypes
+from ctypes import Structure, POINTER
+from ctypes import c_int, c_uint, c_long, c_char, c_char_p, c_double, c_void_p
+
+# Performance Co-Pilot MMV library (C)
+LIBPCP_MMV = ctypes.CDLL(ctypes.util.find_library("pcp_mmv"))
+
+##############################################################################
+#
+# definition of structures used by libpcp, derived from <pcp/pmapi.h>
+#
+# This section defines the data structures for accessing and manuiplating
+# metric information and values. Detailed information about these data
+# structures can be found in the MMV(4) manual page.
+#
+
+class mmv_instance(Structure):
+ """ Maps internal to external instance identifiers, within an
+ instance domain.
+ """
+ _fields_ = [("internal", c_int),
+ ("external", c_char * MMV_NAMEMAX)]
+
+class mmv_indom(Structure):
+ """ Represents an instance domain (for set valued metrics)
+ Instance domains have associated instances - integer/string pairs.
+ Defines complete indom metadata (instances, count, text and so on)
+ """
+ _fields_ = [("serial", c_uint),
+ ("count", c_uint),
+ ("instances", POINTER(mmv_instance)),
+ ("shorttext", c_char_p),
+ ("helptext", c_char_p)]
+
+ def set_instances(self, instances):
+ """ Update the instances and counts fields for this indom """
+ self.count = len(instances)
+ instance_array = (mmv_instance * self.count)()
+ for i in xrange(self.count):
+ instance_array[i].internal = instances[i].internal
+ instance_array[i].external = instances[i].external
+ self.instances = instance_array
+
+class mmv_metric(Structure):
+ """ Represents an individual metric to be exported by pmdammv
+ Defines complete metric metadata (type, semantics, units and so on)
+ """
+ _fields_ = [("name", c_char * MMV_NAMEMAX),
+ ("item", c_int),
+ ("typeof", c_int),
+ ("semantics", c_int),
+ ("dimension", pmUnits),
+ ("indom", c_uint),
+ ("shorttext", c_char_p),
+ ("helptext", c_char_p)]
+
+##
+# PCP Memory Mapped Value Services
+
+LIBPCP_MMV.mmv_stats_init.restype = c_void_p
+LIBPCP_MMV.mmv_stats_init.argtypes = [
+ c_char_p, c_int, c_int,
+ POINTER(mmv_metric), c_int, POINTER(mmv_indom), c_int]
+
+LIBPCP_MMV.mmv_stats_stop.restype = None
+LIBPCP_MMV.mmv_stats_stop.argtypes = [c_char_p, c_void_p]
+
+LIBPCP_MMV.mmv_lookup_value_desc.restype = pmAtomValue
+LIBPCP_MMV.mmv_lookup_value_desc.argtypes = [c_void_p, c_char_p, c_char_p]
+
+LIBPCP_MMV.mmv_inc_value.restype = None
+LIBPCP_MMV.mmv_inc_value.argtypes = [c_void_p, POINTER(pmAtomValue), c_double]
+
+LIBPCP_MMV.mmv_set_value.restype = None
+LIBPCP_MMV.mmv_set_value.argtypes = [c_void_p, POINTER(pmAtomValue), c_double]
+
+LIBPCP_MMV.mmv_set_string.restype = None
+LIBPCP_MMV.mmv_set_string.argtypes = [
+ c_void_p, POINTER(pmAtomValue), c_char_p, c_int]
+
+LIBPCP_MMV.mmv_stats_add.restype = None
+LIBPCP_MMV.mmv_stats_add.argtypes = [c_void_p, c_char_p, c_char_p, c_double]
+
+LIBPCP_MMV.mmv_stats_inc.restype = None
+LIBPCP_MMV.mmv_stats_inc.argtypes = [c_void_p, c_char_p, c_char_p]
+
+LIBPCP_MMV.mmv_stats_set.restype = None
+LIBPCP_MMV.mmv_stats_set.argtypes = [c_void_p, c_char_p, c_char_p, c_double]
+
+LIBPCP_MMV.mmv_stats_add_fallback.restype = None
+LIBPCP_MMV.mmv_stats_add_fallback.argtypes = [
+ c_void_p, c_char_p, c_char_p, c_char_p, c_double]
+
+LIBPCP_MMV.mmv_stats_inc_fallback.restype = None
+LIBPCP_MMV.mmv_stats_inc_fallback.argtypes = [
+ c_void_p, c_char_p, c_char_p, c_char_p]
+
+LIBPCP_MMV.mmv_stats_interval_start.restype = POINTER(pmAtomValue)
+LIBPCP_MMV.mmv_stats_interval_start.argtypes = [
+ c_void_p, POINTER(pmAtomValue), c_char_p, c_char_p]
+
+LIBPCP_MMV.mmv_stats_interval_end.restype = None
+LIBPCP_MMV.mmv_stats_interval_end.argtypes = [c_void_p, POINTER(pmAtomValue)]
+
+LIBPCP_MMV.mmv_stats_set_strlen.restype = None
+LIBPCP_MMV.mmv_stats_set_strlen.argtypes = [
+ c_void_p, c_char_p, c_char_p, c_char_p, c_long]
+
+
+#
+# class MemoryMappedValues
+#
+# This class wraps the MMV (Memory Mapped Values) library functions
+#
+
+class MemoryMappedValues(object):
+ """ Defines a set of PCP Memory Mapped Value (MMV) metrics
+
+ Creates PCP metrics from an instrumented python script
+ via pmdammv (Performance Metrics Domain Agent for MMV)
+ """
+
+ def __init__(self, name, flags = 0, cluster = 42):
+ self._name = name
+ self._cluster = cluster # PMID cluster number (domain is MMV)
+ self._flags = flags # MMV_FLAGS_* flags
+ self._metrics = []
+ self._indoms = []
+ self._handle = None # pointer to the memory mapped area
+
+ def start(self):
+ """ Initialise the underlying library with metrics/instances.
+ On completion of this call, we're all visible to pmdammv.
+ """
+ count_metrics = len(self._metrics)
+ metrics = (mmv_metric * count_metrics)()
+ for i in xrange(count_metrics):
+ metrics[i] = self._metrics[i]
+ count_indoms = len(self._indoms)
+ indoms = (mmv_indom * count_indoms)()
+ for i in xrange(count_indoms):
+ indoms[i] = self._indoms[i]
+ self._handle = LIBPCP_MMV.mmv_stats_init(
+ self._name,
+ self._cluster,
+ self._flags,
+ metrics, count_metrics,
+ indoms, count_indoms)
+
+ def stop(self):
+ """ Shut down the underlying library with metrics/instances.
+ This closes the mmap file preventing any further updates.
+ """
+ if (self._handle != None):
+ LIBPCP_MMV.mmv_stats_stop(self._name, self._handle)
+ self._handle = None
+
+ def restart(self):
+ """ Cleanly stop-if-running and restart MMV export services. """
+ self.stop()
+ self.start()
+
+ def started(self):
+ """ Property flagging an active memory mapping """
+ if (self._handle == None):
+ return 0
+ return 1
+
+ def add_indoms(self, indoms):
+ """ Make a list of instance domains visible to the MMV export """
+ self._indoms = indoms
+ if (self.started()):
+ self.restart()
+
+ def add_indom(self, indom):
+ """ Make an additional instance domain visible to the MMV export """
+ self._indoms.append(indom)
+ self.add_indoms(self._indoms)
+
+ def add_metrics(self, metrics):
+ """ Make a list of metrics visible to the MMV export """
+ self._metrics = metrics
+ if (self.started()):
+ self.restart()
+
+ def add_metric(self, metric):
+ """ Make an additional metric visible to the MMV export """
+ self._metrics.append(metric)
+ self.add_metrics(self._metrics)
+
+
+ def lookup_mapping(self, name, inst):
+ """ Find the memory mapping for a given metric name and instance
+
+ This handle can be used to directly manipulate metric values
+ by other interfaces in this module. This is the *preferred*
+ technique for manipulating MMV values. It is more efficient
+ and the alternative (name/inst lookups) is made available as
+ a convenience only for situations where performance will not
+ be affected by repeated (linear) name/inst lookups.
+ """
+ return LIBPCP_MMV.mmv_lookup_value_desc(self._handle, name, inst)
+
+ def add(self, mapping, value):
+ """ Increment the mapped metric by a given value """
+ LIBPCP_MMV.mmv_inc_value(self._handle, mapping, value)
+
+ def inc(self, mapping):
+ """ Increment the mapped metric by one """
+ LIBPCP_MMV.mmv_inc_value(self._handle, mapping, 1)
+
+ def set(self, mapping, value):
+ """ Set the mapped metric to a given value """
+ LIBPCP_MMV.mmv_set_value(self._handle, mapping, value)
+
+ def set_string(self, mapping, value):
+ """ Set the string mapped metric to a given value """
+ LIBPCP_MMV.mmv_set_string(self._handle, mapping, value, len(value))
+
+ def interval_start(self, mapping):
+ """ Start a timed interval for the mapped metric
+ The opaque handle (mapping) returned is passed to interval_end().
+ """
+ return LIBPCP_MMV.mmv_stats_interval_start(self._handle, mapping, 0, 0)
+
+ def interval_end(self, mapping):
+ """ End a timed interval, the metrics time is increased by interval """
+ return LIBPCP_MMV.mmv_stats_interval_end(self._handle, mapping)
+
+
+ def lookup_add(self, name, inst, value):
+ """ Lookup the named metric[instance] and add a value to it """
+ LIBPCP_MMV.mmv_stats_add(self._handle, name, inst, value)
+
+ def lookup_inc(self, name, inst):
+ """ Lookup the named metric[instance] and add one to it """
+ LIBPCP_MMV.mmv_stats_inc(self._handle, name, inst)
+
+ def lookup_set(self, name, inst, value):
+ """ Lookup the named metric[instance] and set its value """
+ LIBPCP_MMV.mmv_stats_set(self._handle, name, inst, value)
+
+ def lookup_interval_start(self, name, inst):
+ """ Lookup the named metric[instance] and start an interval
+ The opaque handle returned is passed to interval_end().
+ """
+ return LIBPCP_MMV.mmv_stats_interval_start(self._handle,
+ None, name, inst)
+
+ def lookup_set_string(self, name, inst, s):
+ """ Lookup the named metric[instance] and set its string value """
+ LIBPCP_MMV.mmv_stats_set_strlen(self._handle, name, inst, s, len(s))
+
+ def lookup_add_fallback(self, name, inst, fall, value):
+ """ Lookup the named metric[instance] and set its value if found
+ If instance is not found, fallback to using a second instance
+ One example use is: add value to bucketN else use a catch-all
+ bucket such as "other"
+ """
+ LIBPCP_MMV.mmv_stats_add_fallback(self._handle, name, inst, fall, value)
+
+ def lookup_inc_fallback(self, name, inst, fallback):
+ """ Lookup the named metric[instance] and increment its value if found
+ If instance is not found, fallback to using a second instance
+ One sample use is: inc value of BucketA, else inc a catch-all
+ """
+ LIBPCP_MMV.mmv_stats_inc_fallback(self._handle, name, inst, fallback)
+
diff --git a/src/python/pcp/pmapi.py b/src/python/pcp/pmapi.py
new file mode 100644
index 0000000..b558f9d
--- /dev/null
+++ b/src/python/pcp/pmapi.py
@@ -0,0 +1,1866 @@
+# pylint: disable=C0103
+""" Wrapper module for LIBPCP - the core Performace Co-Pilot API """
+#
+# Copyright (C) 2012-2014 Red Hat
+# Copyright (C) 2009-2012 Michael T. Werner
+#
+# 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.
+#
+
+# Additional Information:
+#
+# Performance Co-Pilot Web Site
+# http://www.performancecopilot.org
+#
+# Performance Co-Pilot Programmer's Guide
+# cf. Chapter 3. PMAPI - The Performance Metrics API
+#
+# EXAMPLE
+"""
+ from pcp import pmapi
+ import cpmapi as c_api
+
+ # Create a pcp class
+ context = pmapi.pmContext(c_api.PM_CONTEXT_HOST, "local:")
+
+ # Get ids for number cpus and load metrics
+ metric_ids = context.pmLookupName(("hinv.ncpu","kernel.all.load"))
+ # Get the description of the metrics
+ descs = context.pmLookupDescs(metric_ids)
+ # Fetch the current value for number cpus
+ results = context.pmFetch(metric_ids)
+ # Extract the value into a scalar value
+ atom = context.pmExtractValue(results.contents.get_valfmt(0),
+ results.contents.get_vlist(0, 0),
+ descs[0].contents.type,
+ c_api.PM_TYPE_U32)
+ print "#cpus=", atom.ul
+
+ # Get the instance ids for kernel.all.load
+ inst1 = context.pmLookupInDom(descs[1], "1 minute")
+ inst5 = context.pmLookupInDom(descs[1], "5 minute")
+
+ # Loop through the metric ids
+ for i in xrange(results.contents.numpmid):
+ # Is this the kernel.all.load id?
+ if (results.contents.get_pmid(i) != metric_ids[1]):
+ continue
+ # Extract the kernel.all.load instance
+ for j in xrange(results.contents.get_numval(i) - 1):
+ atom = context.pmExtractValue(results.contents.get_valfmt(i),
+ results.contents.get_vlist(i, j),
+ descs[i].contents.type,
+ c_api.PM_TYPE_FLOAT)
+ value = atom.f
+ if results.contents.get_inst(i, j) == inst1:
+ print "load average 1=",atom.f
+ elif results.contents.get_inst(i, j) == inst5:
+ print "load average 5=",atom.f
+"""
+
+# for reporting on times from pmLocaltime function
+from time import mktime
+
+# constants adapted from C header file <pcp/pmapi.h>
+import cpmapi as c_api
+
+# for interfacing with LIBPCP - the client-side C API
+import ctypes
+from ctypes import c_char, c_int, c_uint, c_long, c_char_p, c_void_p
+from ctypes import c_longlong, c_ulonglong, c_float, c_double
+from ctypes import CDLL, POINTER, CFUNCTYPE, Structure, Union
+from ctypes import addressof, pointer, sizeof, cast, byref
+from ctypes import create_string_buffer, memmove
+from ctypes.util import find_library
+
+##############################################################################
+#
+# dynamic library loads
+#
+
+LIBPCP = CDLL(find_library("pcp"))
+LIBC = CDLL(find_library("c"))
+
+
+##############################################################################
+#
+# definition of exception classes
+#
+
+class pmErr(Exception):
+ def __str__(self):
+ errSym = None
+ try:
+ errSym = c_api.pmErrSymDict[self.args[0]]
+ except KeyError:
+ pass
+ if errSym == None:
+ return self.message()
+ return "%s %s" % (errSym, self.message())
+
+ def message(self):
+ errStr = create_string_buffer(c_api.PM_MAXERRMSGLEN)
+ errStr = LIBPCP.pmErrStr_r(self.args[0], errStr, c_api.PM_MAXERRMSGLEN)
+ for index in range(1, len(self.args)):
+ errStr += " " + str(self.args[index])
+ return errStr
+
+ def progname(self):
+ return c_char_p.in_dll(LIBPCP, "pmProgname").value
+
+class pmUsageErr(Exception):
+ def message(self):
+ return c_api.pmUsageMessage()
+
+
+##############################################################################
+#
+# definition of structures used by libpcp, derived from <pcp/pmapi.h>
+#
+# This section defines the data structures for accessing and manuiplating
+# metric information and values. Detailed information about these data
+# structures can be found in:
+#
+# Performance Co-Pilot Programmer's Guide
+# Section 3.4 - Performance Metric Descriptions
+# Section 3.5 - Performance Metric Values
+#
+
+# these hardcoded decls should be derived from <sys/time.h>
+class timeval(Structure):
+ _fields_ = [("tv_sec", c_long),
+ ("tv_usec", c_long)]
+
+ def __init__(self, sec = 0, usec = 0):
+ Structure.__init__(self)
+ self.tv_sec = sec
+ self.tv_usec = usec
+
+ @classmethod
+ def fromInterval(builder, interval):
+ """ Construct timeval from a string using pmParseInterval """
+ tvp = builder()
+ errmsg = c_char_p()
+ status = LIBPCP.pmParseInterval(interval, byref(tvp), byref(errmsg))
+ if status < 0:
+ raise pmErr(status, errmsg)
+ return tvp
+
+ def __str__(self):
+ return "%.3f" % c_api.pmtimevalToReal(self.tv_sec, self.tv_usec)
+
+ def __float__(self):
+ return float(c_api.pmtimevalToReal(self.tv_sec, self.tv_usec))
+
+ def __long__(self):
+ return long(self.tv_sec)
+
+ def __int__(self):
+ return int(self.tv_sec)
+
+ def sleep(self):
+ """ Delay for the amount of time specified by this timeval. """
+ c_api.pmtimevalSleep(self.tv_sec, self.tv_usec)
+ return None
+
+class tm(Structure):
+ _fields_ = [("tm_sec", c_int),
+ ("tm_min", c_int),
+ ("tm_hour", c_int),
+ ("tm_mday", c_int),
+ ("tm_mon", c_int),
+ ("tm_year", c_int),
+ ("tm_wday", c_int),
+ ("tm_yday", c_int),
+ ("tm_isdst", c_int),
+ ("tm_gmtoff", c_long), # glibc/bsd extension
+ ("tm_zone", c_char_p)] # glibc/bsd extension
+
+ def __str__(self):
+ timetuple = (self.tm_year+1900, self.tm_mon, self.tm_mday,
+ self.tm_hour, self.tm_min, self.tm_sec,
+ self.tm_wday, self.tm_yday, self.tm_isdst)
+ inseconds = 0.0
+ try:
+ inseconds = mktime(timetuple)
+ except:
+ pass
+ return "%s %s" % (inseconds.__str__(), timetuple)
+
+class pmAtomValue(Union):
+ """Union used for unpacking metric values according to type
+
+ Constants for specifying metric types are defined in module pmapi
+ """
+ _fields_ = [("l", c_int),
+ ("ul", c_uint),
+ ("ll", c_longlong),
+ ("ull", c_ulonglong),
+ ("f", c_float),
+ ("d", c_double),
+ ("cp", c_char_p),
+ ("vp", c_void_p)]
+
+ _atomDrefD = {c_api.PM_TYPE_32 : lambda x: x.l,
+ c_api.PM_TYPE_U32 : lambda x: x.ul,
+ c_api.PM_TYPE_64 : lambda x: x.ll,
+ c_api.PM_TYPE_U64 : lambda x: x.ull,
+ c_api.PM_TYPE_FLOAT : lambda x: x.f,
+ c_api.PM_TYPE_DOUBLE : lambda x: x.d,
+ c_api.PM_TYPE_STRING : lambda x: x.cp,
+ c_api.PM_TYPE_AGGREGATE : lambda x: None,
+ c_api.PM_TYPE_AGGREGATE_STATIC : lambda x: None,
+ c_api.PM_TYPE_NOSUPPORT : lambda x: None,
+ c_api.PM_TYPE_UNKNOWN : lambda x: None
+ }
+
+ def dref(self, typed):
+ return self._atomDrefD[typed](self)
+
+class pmUnits(Structure):
+ """
+ Compiler-specific bitfields specifying scale and dimension of metric values
+ Constants for specifying metric units are defined in module pmapi
+ IRIX => HAVE_BITFIELDS_LTOR, gcc => not so much
+ """
+ if c_api.HAVE_BITFIELDS_LTOR:
+ _fields_ = [("dimSpace", c_int, 4),
+ ("dimTime", c_int, 4),
+ ("dimCount", c_int, 4),
+ ("scaleSpace", c_int, 4),
+ ("scaleTime", c_int, 4),
+ ("scaleCount", c_int, 4),
+ ("pad", c_int, 8)]
+ else:
+ _fields_ = [("pad", c_int, 8),
+ ("scaleCount", c_int, 4),
+ ("scaleTime", c_int, 4),
+ ("scaleSpace", c_int, 4),
+ ("dimCount", c_int, 4),
+ ("dimTime", c_int, 4),
+ ("dimSpace", c_int, 4)]
+
+ def __init__(self, dimS=0, dimT=0, dimC=0, scaleS=0, scaleT=0, scaleC=0):
+ Structure.__init__(self)
+ self.dimSpace = dimS
+ self.dimTime = dimT
+ self.dimCount = dimC
+ self.scaleSpace = scaleS
+ self.scaleTime = scaleT
+ self.scaleCount = scaleC
+ self.pad = 0
+
+ def __str__(self):
+ unitstr = ctypes.create_string_buffer(64)
+ return str(LIBPCP.pmUnitsStr_r(self, unitstr, 64))
+
+
+class pmValueBlock(Structure):
+ """Value block bitfields for different compilers
+ A value block holds the value of an instance of a metric
+ pointed to by the pmValue structure, when that value is
+ too large (> 32 bits) to fit in the pmValue structure
+ """
+ if c_api.HAVE_BITFIELDS_LTOR: # IRIX
+ _fields_ = [("vtype", c_uint, 8),
+ ("vlen", c_uint, 24),
+ ("vbuf", c_char * 1)]
+ else: # Linux (gcc)
+ _fields_ = [("vlen", c_uint, 24),
+ ("vtype", c_uint, 8),
+ ("vbuf", c_char * 1)]
+
+class valueDref(Union):
+ """Union in pmValue for dereferencing the value of an instance of a metric
+
+ For small items, e.g. a 32-bit number, the union contains the actual value
+ For large items, e.g. a text string, the union points to a pmValueBlock
+ """
+ _fields_ = [("pval", POINTER(pmValueBlock)),
+ ("lval", c_int)]
+ def __str__(self):
+ return "value=%#lx" % (self.lval)
+
+class pmValue(Structure):
+ """Structure holding the value of a metric instance """
+ _fields_ = [("inst", c_int),
+ ("value", valueDref)]
+ def __str__(self):
+ vstr = str(self.value)
+ return "pmValue@%#lx inst=%d " % (addressof(self), self.inst) + vstr
+
+class pmValueSet(Structure):
+ """Structure holding a metric's list of instance values
+
+ A performance metric may contain one or more instance values, one for each
+ item that the metric concerns. For example, a metric measuring filesystem
+ free space would contain one instance value for each filesystem that exists
+ on the target machine. Whereas, a metric measuring free memory would have
+ only one instance value, representing the total amount of free memory on
+ the target system.
+ """
+ _fields_ = [("pmid", c_uint),
+ ("numval", c_int),
+ ("valfmt", c_int),
+ ("vlist", (pmValue * 1))]
+
+ def __str__(self):
+ if self.valfmt == 0:
+ vals = xrange(self.numval)
+ vstr = str([" %s" % str(self.vlist[i]) for i in vals])
+ vset = (addressof(self), self.pmid, self.numval, self.valfmt)
+ return "pmValueSet@%#lx id=%#lx numval=%d valfmt=%d" % vset + vstr
+ else:
+ return ""
+
+ def vlist_read(self):
+ return pointer(self._vlist[0])
+
+ vlist = property(vlist_read, None, None, None)
+
+
+pmValueSetPtr = POINTER(pmValueSet)
+pmValueSetPtr.pmid = property(lambda x: x.contents.pmid, None, None, None)
+pmValueSetPtr.numval = property(lambda x: x.contents.numval, None, None, None)
+pmValueSetPtr.valfmt = property(lambda x: x.contents.valfmt, None, None, None)
+pmValueSetPtr.vlist = property(lambda x: x.contents.vlist, None, None, None)
+
+
+class pmResult(Structure):
+ """Structure returned by pmFetch, with a value set for each metric queried
+
+ The vset is defined with a "fake" array bounds of 1, which can give runtime
+ array bounds complaints. The getter methods are array bounds agnostic.
+ """
+ _fields_ = [ ("timestamp", timeval),
+ ("numpmid", c_int),
+ # array N of pointer to pmValueSet
+ ("vset", (POINTER(pmValueSet)) * 1) ]
+ def __init__(self):
+ Structure.__init__(self)
+ self.numpmid = 0
+
+ def __str__(self):
+ vals = xrange(self.numpmid)
+ vstr = str([" %s" % str(self.vset[i].contents) for i in vals])
+ return "pmResult@%#lx id#=%d " % (addressof(self), self.numpmid) + vstr
+
+ def get_pmid(self, vset_idx):
+ """ Return the pmid of vset[vset_idx] """
+ vsetptr = cast(self.vset, POINTER(pmValueSetPtr))
+ return vsetptr[vset_idx].contents.pmid
+
+ def get_valfmt(self, vset_idx):
+ """ Return the valfmt of vset[vset_idx] """
+ vsetptr = cast(self.vset, POINTER(POINTER(pmValueSet)))
+ return vsetptr[vset_idx].contents.valfmt
+
+ def get_numval(self, vset_idx):
+ """ Return the numval of vset[vset_idx] """
+ vsetptr = cast(self.vset, POINTER(POINTER(pmValueSet)))
+ return vsetptr[vset_idx].contents.numval
+
+ def get_vset(self, vset_idx):
+ """ Return the vset[vset_idx] """
+ vsetptr = cast(self.vset, POINTER(POINTER(pmValueSet)))
+ return vsetptr[vset_idx]
+
+ def get_vlist(self, vset_idx, vlist_idx):
+ """ Return the vlist[vlist_idx] of vset[vset_idx] """
+ listptr = cast(self.get_vset(vset_idx).contents.vlist, POINTER(pmValue))
+ return listptr[vlist_idx]
+
+ def get_inst(self, vset_idx, vlist_idx):
+ """ Return the inst for vlist[vlist_idx] of vset[vset_idx] """
+ return self.get_vlist(vset_idx, vlist_idx).inst
+
+pmID = c_uint
+pmInDom = c_uint
+
+class pmDesc(Structure):
+ """Structure describing a metric
+ """
+ _fields_ = [("pmid", c_uint),
+ ("type", c_int),
+ ("indom", c_uint),
+ ("sem", c_int),
+ ("units", pmUnits) ]
+ def __str__(self):
+ fields = (addressof(self), self.pmid, self.type)
+ return "pmDesc@%#lx id=%#lx type=%d" % fields
+
+pmDescPtr = POINTER(pmDesc)
+pmDescPtr.sem = property(lambda x: x.contents.sem, None, None, None)
+pmDescPtr.type = property(lambda x: x.contents.type, None, None, None)
+
+
+def get_indom(pmdesc):
+ """Internal function to extract an indom from a pmdesc
+
+ Allow functions requiring an indom to be passed a pmDesc* instead
+ """
+ class Value(Union):
+ _fields_ = [ ("pval", POINTER(pmDesc)),
+ ("lval", c_uint) ]
+ if type(pmdesc) == POINTER(pmDesc):
+ return pmdesc.contents.indom
+ else: # raw indom
+ # Goodness, there must be a simpler way to do this
+ value = Value()
+ value.pval = pmdesc
+ return value.lval
+
+class pmMetricSpec(Structure):
+ """Structure describing a metric's specification
+ """
+ _fields_ = [ ("isarch", c_int),
+ ("source", c_char_p),
+ ("metric", c_char_p),
+ ("ninst", c_int),
+ ("inst", POINTER(c_char_p)) ]
+ def __str__(self):
+ insts = map(lambda x: str(self.inst[x]), range(self.ninst))
+ fields = (addressof(self), self.isarch, self.source, insts)
+ return "pmMetricSpec@%#lx src=%s metric=%s insts=" % fields
+
+ @classmethod
+ def fromString(builder, string, isarch = 0, source = ''):
+ result = POINTER(builder)()
+ errmsg = c_char_p()
+ status = LIBPCP.pmParseMetricSpec(string, isarch, source,
+ byref(result), byref(errmsg))
+ if status < 0:
+ raise pmErr(status, errmsg)
+ return result
+
+class pmLogLabel(Structure):
+ """Label record at the start of every log file
+ """
+ _fields_ = [ ("magic", c_int),
+ ("pid_t", c_int),
+ ("start", timeval),
+ ("hostname", c_char * c_api.PM_LOG_MAXHOSTLEN),
+ ("tz", c_char * c_api.PM_TZ_MAXLEN) ]
+
+
+##############################################################################
+#
+# PMAPI function prototypes
+#
+
+##
+# PMAPI Name Space Services
+
+LIBPCP.pmGetChildren.restype = c_int
+LIBPCP.pmGetChildren.argtypes = [c_char_p, POINTER(POINTER(c_char_p))]
+
+LIBPCP.pmGetChildrenStatus.restype = c_int
+LIBPCP.pmGetChildrenStatus.argtypes = [
+ c_char_p, POINTER(POINTER(c_char_p)), POINTER(POINTER(c_int))]
+
+LIBPCP.pmGetPMNSLocation.restype = c_int
+LIBPCP.pmGetPMNSLocation.argtypes = []
+
+LIBPCP.pmLoadNameSpace.restype = c_int
+LIBPCP.pmLoadNameSpace.argtypes = [c_char_p]
+
+LIBPCP.pmLookupName.restype = c_int
+LIBPCP.pmLookupName.argtypes = [c_int, (c_char_p * 1), POINTER(c_uint)]
+
+LIBPCP.pmNameAll.restype = c_int
+LIBPCP.pmNameAll.argtypes = [c_int, POINTER(POINTER(c_char_p))]
+
+LIBPCP.pmNameID.restype = c_int
+LIBPCP.pmNameID.argtypes = [c_int, POINTER(c_char_p)]
+
+traverseCB_type = CFUNCTYPE(None, c_char_p)
+LIBPCP.pmTraversePMNS.restype = c_int
+LIBPCP.pmTraversePMNS.argtypes = [c_char_p, traverseCB_type]
+
+LIBPCP.pmUnloadNameSpace.restype = c_int
+LIBPCP.pmUnloadNameSpace.argtypes = []
+
+LIBPCP.pmRegisterDerived.restype = c_int
+LIBPCP.pmRegisterDerived.argtypes = [c_char_p, c_char_p]
+
+LIBPCP.pmLoadDerivedConfig.restype = c_int
+LIBPCP.pmLoadDerivedConfig.argtypes = [c_char_p]
+
+LIBPCP.pmDerivedErrStr.restype = c_char_p
+LIBPCP.pmDerivedErrStr.argtypes = []
+
+##
+# PMAPI Metrics Description Services
+
+LIBPCP.pmLookupDesc.restype = c_int
+LIBPCP.pmLookupDesc.argtypes = [c_uint, POINTER(pmDesc)]
+
+LIBPCP.pmLookupInDomText.restype = c_int
+LIBPCP.pmLookupInDomText.argtypes = [c_uint, c_int, POINTER(c_char_p)]
+
+LIBPCP.pmLookupText.restype = c_int
+LIBPCP.pmLookupText.argtypes = [c_uint, c_int, POINTER(c_char_p)]
+
+
+##
+# PMAPI Instance Domain Services
+
+LIBPCP.pmGetInDom.restype = c_int
+LIBPCP.pmGetInDom.argtypes = [
+ c_uint, POINTER(POINTER(c_int)), POINTER(POINTER(c_char_p))]
+
+LIBPCP.pmLookupInDom.restype = c_int
+LIBPCP.pmLookupInDom.argtypes = [c_uint, c_char_p]
+
+LIBPCP.pmNameInDom.restype = c_int
+LIBPCP.pmNameInDom.argtypes = [c_uint, c_uint, POINTER(c_char_p)]
+
+
+##
+# PMAPI Context Services
+
+LIBPCP.pmNewContext.restype = c_int
+LIBPCP.pmNewContext.argtypes = [c_int, c_char_p]
+
+LIBPCP.pmDestroyContext.restype = c_int
+LIBPCP.pmDestroyContext.argtypes = [c_int]
+
+LIBPCP.pmDupContext.restype = c_int
+LIBPCP.pmDupContext.argtypes = []
+
+LIBPCP.pmUseContext.restype = c_int
+LIBPCP.pmUseContext.argtypes = [c_int]
+
+LIBPCP.pmWhichContext.restype = c_int
+LIBPCP.pmWhichContext.argtypes = []
+
+LIBPCP.pmAddProfile.restype = c_int
+LIBPCP.pmAddProfile.argtypes = [c_uint, c_int, POINTER(c_int)]
+
+LIBPCP.pmDelProfile.restype = c_int
+LIBPCP.pmDelProfile.argtypes = [c_uint, c_int, POINTER(c_int)]
+
+LIBPCP.pmSetMode.restype = c_int
+LIBPCP.pmSetMode.argtypes = [c_int, POINTER(timeval), c_int]
+
+LIBPCP.pmReconnectContext.restype = c_int
+LIBPCP.pmReconnectContext.argtypes = [c_int]
+
+LIBPCP.pmGetContextHostName_r.restype = c_char_p
+LIBPCP.pmGetContextHostName_r.argtypes = [c_int, c_char_p, c_int]
+
+
+##
+# PMAPI Timezone Services
+
+LIBPCP.pmNewContextZone.restype = c_int
+LIBPCP.pmNewContextZone.argtypes = []
+
+LIBPCP.pmNewZone.restype = c_int
+LIBPCP.pmNewZone.argtypes = [c_char_p]
+
+LIBPCP.pmUseZone.restype = c_int
+LIBPCP.pmUseZone.argtypes = [c_int]
+
+LIBPCP.pmWhichZone.restype = c_int
+LIBPCP.pmWhichZone.argtypes = [POINTER(c_char_p)]
+
+LIBPCP.pmLocaltime.restype = POINTER(tm)
+LIBPCP.pmLocaltime.argtypes = [POINTER(c_long), POINTER(tm)]
+
+LIBPCP.pmCtime.restype = c_char_p
+LIBPCP.pmCtime.argtypes = [POINTER(c_long), c_char_p]
+
+
+##
+# PMAPI Metrics Services
+
+LIBPCP.pmFetch.restype = c_int
+LIBPCP.pmFetch.argtypes = [c_int, POINTER(c_uint), POINTER(POINTER(pmResult))]
+
+LIBPCP.pmFreeResult.restype = None
+LIBPCP.pmFreeResult.argtypes = [POINTER(pmResult)]
+
+LIBPCP.pmStore.restype = c_int
+LIBPCP.pmStore.argtypes = [POINTER(pmResult)]
+
+
+##
+# PMAPI Archive-Specific Services
+
+LIBPCP.pmGetArchiveLabel.restype = c_int
+LIBPCP.pmGetArchiveLabel.argtypes = [POINTER(pmLogLabel)]
+
+LIBPCP.pmGetArchiveEnd.restype = c_int
+LIBPCP.pmGetArchiveEnd.argtypes = [POINTER(timeval)]
+
+LIBPCP.pmGetInDomArchive.restype = c_int
+LIBPCP.pmGetInDomArchive.argtypes = [
+ c_uint, POINTER(POINTER(c_int)), POINTER(POINTER(c_char_p)) ]
+
+LIBPCP.pmLookupInDomArchive.restype = c_int
+LIBPCP.pmLookupInDom.argtypes = [c_uint, c_char_p]
+LIBPCP.pmLookupInDomArchive.argtypes = [pmInDom, c_char_p]
+
+LIBPCP.pmNameInDomArchive.restype = c_int
+LIBPCP.pmNameInDomArchive.argtypes = [pmInDom, c_int]
+
+LIBPCP.pmFetchArchive.restype = c_int
+LIBPCP.pmFetchArchive.argtypes = [POINTER(POINTER(pmResult))]
+
+
+##
+# PMAPI Ancilliary Support Services
+
+
+LIBPCP.pmGetConfig.restype = c_char_p
+LIBPCP.pmGetConfig.argtypes = [c_char_p]
+
+LIBPCP.pmErrStr_r.restype = c_char_p
+LIBPCP.pmErrStr_r.argtypes = [c_int, c_char_p, c_int]
+
+LIBPCP.pmExtractValue.restype = c_int
+LIBPCP.pmExtractValue.argtypes = [
+ c_int, POINTER(pmValue), c_int, POINTER(pmAtomValue), c_int ]
+
+LIBPCP.pmConvScale.restype = c_int
+LIBPCP.pmConvScale.argtypes = [
+ c_int, POINTER(pmAtomValue), POINTER(pmUnits), POINTER(pmAtomValue),
+ POINTER(pmUnits)]
+
+LIBPCP.pmUnitsStr_r.restype = c_char_p
+LIBPCP.pmUnitsStr_r.argtypes = [POINTER(pmUnits), c_char_p, c_int]
+
+LIBPCP.pmNumberStr_r.restype = c_char_p
+LIBPCP.pmNumberStr_r.argtypes = [c_double, c_char_p, c_int]
+
+LIBPCP.pmIDStr_r.restype = c_char_p
+LIBPCP.pmIDStr_r.argtypes = [c_uint, c_char_p, c_int]
+
+LIBPCP.pmInDomStr_r.restype = c_char_p
+LIBPCP.pmInDomStr_r.argtypes = [c_uint, c_char_p, c_int]
+
+LIBPCP.pmTypeStr_r.restype = c_char_p
+LIBPCP.pmTypeStr_r.argtypes = [c_int, c_char_p, c_int]
+
+LIBPCP.pmAtomStr_r.restype = c_char_p
+LIBPCP.pmAtomStr_r.argtypes = [POINTER(pmAtomValue), c_int, c_char_p, c_int]
+
+LIBPCP.pmPrintValue.restype = None
+LIBPCP.pmPrintValue.argtypes = [c_void_p, c_int, c_int, POINTER(pmValue), c_int]
+
+LIBPCP.pmParseInterval.restype = c_int
+LIBPCP.pmParseInterval.argtypes = [c_char_p, POINTER(timeval),
+ POINTER(c_char_p)]
+
+LIBPCP.pmParseMetricSpec.restype = c_int
+LIBPCP.pmParseMetricSpec.argtypes = [c_char_p, c_int, c_char_p,
+ POINTER(POINTER(pmMetricSpec)), POINTER(c_char_p)]
+
+LIBPCP.pmflush.restype = c_int
+LIBPCP.pmflush.argtypes = []
+
+LIBPCP.pmprintf.restype = c_int
+LIBPCP.pmprintf.argtypes = [c_char_p]
+
+LIBPCP.pmSortInstances.restype = None
+LIBPCP.pmSortInstances.argtypes = [POINTER(pmResult)]
+
+ctypes.pythonapi.PyFile_AsFile.restype = ctypes.c_void_p
+ctypes.pythonapi.PyFile_AsFile.argtypes = [ctypes.py_object]
+
+
+##############################################################################
+#
+# class pmOptions
+#
+# This class wraps the PMAPI pmGetOptions functionality and can be used
+# to assist with automatic construction of a PMAPI context based on the
+# command line options used.
+#
+
+class pmOptions(object):
+ """ Command line option parsing for short and long form arguments
+ Passed into pmGetOptions, pmGetContextOptions, pmUsageMessage.
+ """
+ ##
+ # property read methods
+
+ def _R_mode(self):
+ return self._mode
+ def _R_delta(self):
+ return self._delta
+
+ ##
+ # property definitions
+
+ mode = property(_R_mode, None, None, None)
+ delta = property(_R_delta, None, None, None)
+
+ ##
+ # creation and destruction
+
+ def __init__(self, short_options = None, short_usage = None, flags = 0):
+ c_api.pmResetAllOptions()
+ if (short_options != None):
+ c_api.pmSetShortOptions(short_options)
+ if short_usage != None:
+ c_api.pmSetShortUsage(short_usage)
+ if flags != 0:
+ c_api.pmSetOptionFlags(flags)
+ else: # good default for scripts - always evaluating log bounds
+ c_api.pmSetOptionFlags(c_api.PM_OPTFLAG_BOUNDARIES)
+ self._delta = 1 # default archive pmSetMode delta
+ self._mode = c_api.PM_MODE_INTERP # default pmSetMode access mode
+
+ def __del__(self):
+ c_api.pmResetAllOptions()
+
+ ##
+ # general command line option access and manipulation
+
+ def pmGetOptionFlags(self):
+ return c_api.pmGetOptionFlags()
+
+ def pmSetOptionFlags(self, flags):
+ return c_api.pmSetOptionFlags(flags)
+
+ def pmGetOptionErrors(self):
+ return c_api.pmGetOptionErrors()
+
+ def pmSetOptionErrors(self):
+ errors = c_api.pmGetOptionErrors() + 1
+ return c_api.pmSetOptionErrors(errors)
+
+ def pmSetShortUsage(self, short_usage):
+ return c_api.pmSetShortUsage(short_usage)
+
+ def pmSetShortOptions(self, short_options):
+ return c_api.pmSetShortOptions(short_options)
+
+ def pmSetOptionSamples(self, count):
+ """ Set sample count (converts string to integer) """
+ return c_api.pmSetOptionSamples(count)
+
+ def pmSetOptionInterval(self, interval):
+ """ Set sampling interval (pmParseInterval string) """
+ return c_api.pmSetOptionInterval(interval)
+
+ def pmNonOptionsFromList(self, argv):
+ return c_api.pmGetNonOptionsFromList(argv)
+
+ def pmSetCallbackObject(self, them):
+ """ When options are being parsed from within an object, the
+ caller will want the "self" of the other object ("them")
+ passed as the first parameter to the callback function.
+ """
+ return c_api.pmSetCallbackObject(them)
+
+ def pmSetOptionCallback(self, func):
+ """ Handle individual command line options, outside of the PCP
+ "standard" set (or overridden).
+
+ For every non-standard or overridden option, this callback
+ will be called with the short option character (as an int)
+ or zero for long-option only, and the usual getopts global
+ state (optind, opterr, optopt, optarg, and index - all int
+ except optarg which is a str).
+ """
+ return c_api.pmSetOptionCallback(func)
+
+ def pmSetOverrideCallback(self, func):
+ """ Allow a "standard" PCP option to be overridden.
+
+ For every option parsed, this callback is called and it may
+ return zero, meaning continue with processing the option in
+ the standard way, or non-zero, meaning the caller wishes to
+ override and interpret the option differently.
+ Callback input: int, output: int
+ """
+ return c_api.pmSetOverrideCallback(func)
+
+ def pmSetLongOption(self, long_opt, has_arg, short_opt, argname, message):
+ """ Add long option into the set of supported long options
+
+ Pass in the option name (str), whether it takes an argument (int),
+ its short option form (int), and two usage message hints (argname
+ (str) and message (str) - see pmGetOptions(3) for details).
+ """
+ return c_api.pmSetLongOption(long_opt, has_arg, short_opt, argname, message)
+
+ def pmSetLongOptionHeader(self, heading):
+ """ Add a new section heading into the long option usage message """
+ return c_api.pmSetLongOptionHeader(heading)
+
+ def pmSetLongOptionText(self, text):
+ """ Add some descriptive text into the long option usage message """
+ return c_api.pmSetLongOptionText(text)
+
+ def pmSetLongOptionAlign(self):
+ """ Add support for -A/--align into PMAPI monitor tool """
+ return c_api.pmSetLongOptionAlign()
+
+ def pmSetLongOptionArchive(self):
+ """ Add support for -a/--archive into PMAPI monitor tool """
+ return c_api.pmSetLongOptionArchive()
+
+ def pmSetLongOptionDebug(self):
+ """ Add support for -D/--debug into PMAPI monitor tool """
+ return c_api.pmSetLongOptionDebug()
+
+ def pmSetLongOptionGuiMode(self):
+ """ Add support for -g/--guimode into PMAPI monitor tool """
+ return c_api.pmSetLongOptionGuiMode()
+
+ def pmSetLongOptionHost(self):
+ """ Add support for -h/--host into PMAPI monitor tool """
+ return c_api.pmSetLongOptionHost()
+
+ def pmSetLongOptionHostsFile(self):
+ """ Add support for -H/--hostsfile into PMAPI monitor tool """
+ return c_api.pmSetLongOptionHostsFile()
+
+ def pmSetLongOptionSpecLocal(self):
+ """ Add support for -K/--spec-local into PMAPI monitor tool """
+ return c_api.pmSetLongOptionSpecLocal()
+
+ def pmSetLongOptionLocalPMDA(self):
+ """ Add support for -L/--local-PMDA into PMAPI monitor tool """
+ return c_api.pmSetLongOptionLocalPMDA()
+
+ def pmSetLongOptionOrigin(self):
+ """ Add support for -O/--origin into PMAPI monitor tool """
+ return c_api.pmSetLongOptionOrigin()
+
+ def pmSetLongOptionGuiPort(self):
+ """ Add support for -p/--guiport into PMAPI monitor tool """
+ return c_api.pmSetLongOptionGuiPort()
+
+ def pmSetLongOptionStart(self):
+ """ Add support for -S/--start into PMAPI monitor tool """
+ return c_api.pmSetLongOptionStart()
+
+ def pmSetLongOptionSamples(self):
+ """ Add support for -s/--samples into PMAPI monitor tool """
+ return c_api.pmSetLongOptionSamples()
+
+ def pmSetLongOptionFinish(self):
+ """ Add support for -T/--finish into PMAPI monitor tool """
+ return c_api.pmSetLongOptionFinish()
+
+ def pmSetLongOptionInterval(self):
+ """ Add support for -t/--interval into PMAPI monitor tool """
+ return c_api.pmSetLongOptionInterval()
+
+ def pmSetLongOptionVersion(self):
+ """ Add support for -V/--version into PMAPI monitor tool """
+ return c_api.pmSetLongOptionVersion()
+
+ def pmSetLongOptionTimeZone(self):
+ """ Add support for -Z/--timezone into PMAPI monitor tool """
+ return c_api.pmSetLongOptionTimeZone()
+
+ def pmSetLongOptionHostZone(self):
+ """ Add support for -z/--hostzone into PMAPI monitor tool """
+ return c_api.pmSetLongOptionHostZone()
+
+ def pmSetLongOptionHelp(self):
+ """ Add support for -?/--help into PMAPI monitor tool """
+ return c_api.pmSetLongOptionHelp()
+
+ def pmSetLongOptionArchiveList(self):
+ """ Add support for --archive-list into PMAPI monitor tool """
+ return c_api.pmSetLongOptionArchiveList()
+
+ def pmSetLongOptionArchiveFolio(self):
+ """ Add support for --archive-folio into PMAPI monitor tool """
+ return c_api.pmSetLongOptionArchiveFolio()
+
+ def pmSetLongOptionHostList(self):
+ """ Add support for --host-list into PMAPI monitor tool """
+ return c_api.pmSetLongOptionHostList()
+
+ def pmGetOptionContext(self): # int (typed)
+ return c_api.pmGetOptionContext()
+
+ def pmGetOptionHosts(self): # str list
+ return c_api.pmGetOptionHosts()
+
+ def pmGetOptionArchives(self): # str list
+ return c_api.pmGetOptionArchives()
+
+ def pmGetOptionAlignment(self): # timeval
+ sec = c_api.pmGetOptionAlignment_sec()
+ if sec == None:
+ return None
+ return timeval(sec, c_api.pmGetOptionAlignment_sec())
+
+ def pmGetOptionStart(self): # timeval
+ sec = c_api.pmGetOptionStart_sec()
+ if sec == None:
+ return None
+ return timeval(sec, c_api.pmGetOptionStart_usec())
+
+ def pmGetOptionFinishOptarg(self): # string
+ return c_api.pmGetOptionFinish_optarg()
+
+ def pmGetOptionFinish(self): # timeval
+ sec = c_api.pmGetOptionFinish_sec()
+ if sec == None:
+ return None
+ return timeval(sec, c_api.pmGetOptionFinish_usec())
+
+ def pmGetOptionOrigin(self): # timeval
+ sec = c_api.pmGetOptionOrigin_sec()
+ if sec == None:
+ return None
+ return timeval(sec, c_api.pmGetOptionOrigin_usec())
+
+ def pmGetOptionInterval(self): # timeval
+ sec = c_api.pmGetOptionInterval_sec()
+ if sec == None:
+ return None
+ return timeval(sec, c_api.pmGetOptionInterval_usec())
+
+ def pmGetOptionSamples(self): # int
+ return c_api.pmGetOptionSamples()
+
+ def pmGetOptionTimezone(self): # str
+ return c_api.pmGetOptionTimezone()
+
+ def pmSetOptionArchiveList(self, archives): # str
+ return c_api.pmSetOptionArchiveList(archives)
+
+ def pmSetOptionArchiveFolio(self, folio): # str
+ return c_api.pmSetOptionArchiveFolio(folio)
+
+ def pmSetOptionHostList(self, hosts): # str
+ return c_api.pmSetOptionHostList(hosts)
+
+
+##############################################################################
+#
+# class pmContext
+#
+# This class wraps the PMAPI library functions
+#
+
+class pmContext(object):
+ """Defines a metrics source context (e.g. host, archive, etc) to operate on
+
+ pmContext(c_api.PM_CONTEXT_HOST,"local:")
+ pmContext(c_api.PM_CONTEXT_ARCHIVE,"FILENAME")
+
+ This object defines a PMAPI context, and its methods wrap calls to PMAPI
+ library functions. Detailled information about those C library functions
+ can be found in the following document.
+
+ SGI Document: 007-3434-005
+ Performance Co-Pilot Programmer's Guide
+ Section 3.7 - PMAPI Procedural Interface, pp. 67
+
+ Detailed information about the underlying data structures can be found
+ in the same document.
+
+ Section 3.4 - Performance Metric Descriptions, pp. 59
+ Section 3.5 - Performance Metric Values, pp. 62
+ """
+
+ ##
+ # class attributes
+
+ ##
+ # property read methods
+
+ def _R_type(self):
+ return self._type
+ def _R_target(self):
+ return self._target
+ def _R_ctx(self):
+ return self._ctx
+
+ ##
+ # property definitions
+
+ type = property(_R_type, None, None, None)
+ target = property(_R_target, None, None, None)
+ ctx = property(_R_ctx, None, None, None)
+
+ ##
+ # creation and destruction
+
+ def __init__(self, typed = c_api.PM_CONTEXT_HOST, target = "local:"):
+ self._type = typed # the context type
+ self._target = target # the context target
+ self._ctx = c_api.PM_ERR_NOCONTEXT # init'd pre-connect
+ self._ctx = LIBPCP.pmNewContext(typed, target) # the context handle
+ if self._ctx < 0:
+ raise pmErr(self._ctx, [target])
+
+ def __del__(self):
+ if LIBPCP and self._ctx != c_api.PM_ERR_NOCONTEXT:
+ LIBPCP.pmDestroyContext(self._ctx)
+
+ @classmethod
+ def fromOptions(builder, options, argv, typed = 0, index = 0):
+ """ Helper interface, simple PCP monitor argument parsing.
+
+ Take argv list, create a context using pmGetOptions(3)
+ and standard options default values like local: etc
+ based on the contents of the list.
+
+ Caller should have already registered any options of
+ interest using the option family of interfaces, i.e.
+ pmSetShortOptions, pmSetLongOption*, pmSetOptionFlags,
+ pmSetOptionCallback, and pmSetOptionOverrideCallback.
+
+ When the MULTI/MIXED pmGetOptions flags are being used,
+ the typed/index parameters can be used to setup several
+ contexts based on the given command line parameters.
+ """
+ if (typed <= 0):
+ if c_api.pmGetOptionsFromList(argv):
+ raise pmUsageErr
+ typed = options.pmGetOptionContext()
+
+ if typed == c_api.PM_CONTEXT_ARCHIVE:
+ archives = options.pmGetOptionArchives()
+ source = archives[index]
+ elif typed == c_api.PM_CONTEXT_HOST:
+ hosts = options.pmGetOptionHosts()
+ source = hosts[index]
+ elif typed == c_api.PM_CONTEXT_LOCAL:
+ source = None
+ else:
+ typed = c_api.PM_CONTEXT_HOST
+ source = "local:"
+
+ # core work done here - constructs the new pmContext
+ context = builder(typed, source)
+
+ # finish time windows, timezones, archive access mode
+ if c_api.pmSetContextOptions(context.ctx, options.mode, options.delta):
+ raise pmUsageErr
+
+ return context
+
+ ##
+ # PMAPI Name Space Services
+ #
+
+ def pmGetChildren(self, name):
+ """PMAPI - Return names of children of the given PMNS node NAME
+ tuple names = pmGetChildren("kernel")
+ """
+ offspring = POINTER(c_char_p)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetChildren(name, byref(offspring))
+ if status < 0:
+ raise pmErr(status)
+ if status > 0:
+ childL = map(lambda x: str(offspring[x]), range(status))
+ LIBC.free(offspring)
+ else:
+ return None
+ return childL
+
+ def pmGetChildrenStatus(self, name):
+ """PMAPI - Return names and status of children of the given metric NAME
+ (tuple names,tuple status) = pmGetChildrenStatus("kernel")
+ """
+ offspring = POINTER(c_char_p)()
+ childstat = POINTER(c_int)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetChildrenStatus(name,
+ byref(offspring), byref(childstat))
+ if status < 0:
+ raise pmErr(status)
+ if status > 0:
+ childL = map(lambda x: str(offspring[x]), range(status))
+ statL = map(lambda x: int(childstat[x]), range(status))
+ LIBC.free(offspring)
+ LIBC.free(childstat)
+ else:
+ return None, None
+ return childL, statL
+
+ def pmGetPMNSLocation(self):
+ """PMAPI - Return the namespace location type
+ loc = pmGetPMNSLocation()
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetPMNSLocation()
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmLoadNameSpace(self, filename):
+ """PMAPI - Load a local namespace
+ status = pmLoadNameSpace("filename")
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmLoadNameSpace(filename)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmLookupName(self, nameA, relaxed = 0):
+ """PMAPI - Lookup pmIDs from a list of metric names nameA
+
+ c_uint pmid [] = pmLookupName("MetricName")
+ c_uint pmid [] = pmLookupName(("MetricName1", "MetricName2", ...))
+ """
+ if type(nameA) == type(""):
+ n = 1
+ else:
+ n = len(nameA)
+ names = (c_char_p * n)()
+ if type(nameA) == type(""):
+ names[0] = c_char_p(nameA)
+ else:
+ for i in xrange(len(nameA)):
+ names[i] = c_char_p(nameA[i])
+
+ pmidA = (c_uint * n)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ LIBPCP.pmLookupName.argtypes = [c_int, (c_char_p * n), POINTER(c_uint)]
+ status = LIBPCP.pmLookupName(n, names, pmidA)
+ if status < 0:
+ raise pmErr(status, pmidA)
+ elif relaxed == 0 and status != n:
+ badL = [name for (name, pmid) in zip(nameA, pmidA) \
+ if pmid == c_api.PM_ID_NULL]
+ raise pmErr(c_api.PM_ERR_NAME, badL)
+ return pmidA
+
+ def pmNameAll(self, pmid):
+ """PMAPI - Return list of all metric names having this identical PMID
+ tuple names = pmNameAll(metric_id)
+ """
+ nameA_p = POINTER(c_char_p)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmNameAll(pmid, byref(nameA_p))
+ if status < 0:
+ raise pmErr(status)
+ nameL = map(lambda x: str(nameA_p[x]), range(status))
+ LIBC.free( nameA_p )
+ return nameL
+
+ def pmNameID(self, pmid):
+ """PMAPI - Return a metric name from a PMID
+ name = pmNameID(self.metric_id)
+ """
+ k = c_char_p()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmNameID(pmid, byref(k))
+ if status < 0:
+ raise pmErr(status)
+ name = k.value
+ LIBC.free( k )
+ return name
+
+ def pmTraversePMNS(self, name, callback):
+ """PMAPI - Scan namespace, depth first, run CALLBACK at each node
+ status = pmTraversePMNS("kernel", traverse_callback)
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ cb = traverseCB_type(callback)
+ status = LIBPCP.pmTraversePMNS(name, cb)
+ if status < 0:
+ raise pmErr(status)
+
+ def pmUnLoadNameSpace(self):
+ """PMAPI - Unloads a local PMNS, if one was previously loaded
+ pm.pmUnLoadNameSpace("NameSpace")
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmUnloadNameSpace()
+ if status < 0:
+ raise pmErr(status)
+
+ def pmRegisterDerived(self, name, expr):
+ """PMAPI - Register a derived metric name and definition
+ pm.pmRegisterDerived("MetricName", "MetricName Expression")
+ """
+ status = LIBPCP.pmRegisterDerived(name, expr)
+ if status != 0:
+ raise pmErr(status)
+ status = LIBPCP.pmReconnectContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+
+ def pmLoadDerivedConfig(self, f):
+ """PMAPI - Register derived metric names and definitions from a file
+ pm.pmLoadDerivedConfig("FileName")
+ """
+ status = LIBPCP.pmLoadDerivedConfig(f)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmReconnectContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+
+ @staticmethod
+ def pmDerivedErrStr():
+ """PMAPI - Return an error message if the pmRegisterDerived metric
+ definition cannot be parsed
+ pm.pmRegisterDerived()
+ """
+ return str(LIBPCP.pmDerivedErrStr())
+
+ ##
+ # PMAPI Metrics Description Services
+
+ def pmLookupDesc(self, pmid_p):
+
+ """PMAPI - Lookup a metric description structure from a pmID
+
+ pmDesc* pmdesc = pmLookupDesc(c_uint pmid)
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+
+ descbuf = create_string_buffer(sizeof(pmDesc))
+ desc = cast(descbuf, POINTER(pmDesc))
+ pmid = c_uint(pmid_p)
+ status = LIBPCP.pmLookupDesc(pmid, desc)
+ if status < 0:
+ raise pmErr(status)
+ return desc
+
+ def pmLookupDescs(self, pmids_p):
+
+ """PMAPI - Lookup metric description structures from pmIDs
+
+ (pmDesc* pmdesc)[] = pmLookupDesc(c_uint pmid[N])
+ (pmDesc* pmdesc)[] = pmLookupDesc(c_uint pmid)
+ """
+ if type(pmids_p) == type(int(0)) or type(pmids_p) == type(long(0)):
+ n = 1
+ else:
+ n = len(pmids_p)
+
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+
+ desc = (POINTER(pmDesc) * n)()
+
+ for i in xrange(n):
+ descbuf = create_string_buffer(sizeof(pmDesc))
+ desc[i] = cast(descbuf, POINTER(pmDesc))
+ if type(pmids_p) == type(int()) or type(pmids_p) == type(long()):
+ pmids = c_uint(pmids_p)
+ else:
+ pmids = c_uint(pmids_p[i])
+
+ status = LIBPCP.pmLookupDesc(pmids, desc[i])
+ if status < 0:
+ raise pmErr(status)
+ return desc
+
+ def pmLookupInDomText(self, pmdesc, kind = c_api.PM_TEXT_ONELINE):
+ """PMAPI - Lookup the description of a metric's instance domain
+
+ "instance" = pmLookupInDomText(pmDesc pmdesc)
+ """
+ buf = c_char_p()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+
+ status = LIBPCP.pmLookupInDomText(get_indom(pmdesc), kind, byref(buf))
+ if status < 0:
+ raise pmErr(status)
+ text = str(buf.value)
+ LIBC.free(buf)
+ return text
+
+ def pmLookupText(self, pmid, kind = c_api.PM_TEXT_ONELINE):
+ """PMAPI - Lookup the description of a metric from its pmID
+ "desc" = pmLookupText(pmid)
+ """
+ buf = c_char_p()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmLookupText(pmid, kind, byref(buf))
+ if status < 0:
+ raise pmErr(status)
+ text = buf.value
+ LIBC.free(buf)
+ return text
+
+ ##
+ # PMAPI Instance Domain Services
+
+ def pmGetInDom(self, pmdescp):
+ """PMAPI - Lookup the list of instances from an instance domain PMDESCP
+
+ ([instance1, instance2...] [name1, name2...]) pmGetInDom(pmDesc pmdesc)
+ """
+ instA_p = POINTER(c_int)()
+ nameA_p = POINTER(c_char_p)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetInDom(get_indom(pmdescp),
+ byref(instA_p), byref(nameA_p))
+ if status < 0:
+ raise pmErr(status)
+ if status > 0:
+ nameL = map(lambda x: str(nameA_p[x]), range(status))
+ instL = map(lambda x: int(instA_p[x]), range(status))
+ LIBC.free(instA_p)
+ LIBC.free(nameA_p)
+ else:
+ instL = None
+ nameL = None
+ return instL, nameL
+
+ def pmLookupInDom(self, pmdesc, name):
+ """PMAPI - Lookup the instance id with the given NAME in the indom
+
+ c_uint instid = pmLookupInDom(pmDesc pmdesc, "Instance")
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmLookupInDom(get_indom(pmdesc), name)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmNameInDom(self, pmdesc, instval):
+ """PMAPI - Lookup the text name of an instance in an instance domain
+
+ "string" = pmNameInDom(pmDesc pmdesc, c_uint instid)
+ """
+ if instval == c_api.PM_IN_NULL:
+ return "PM_IN_NULL"
+ name_p = c_char_p()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmNameInDom(get_indom(pmdesc), instval, byref(name_p))
+ if status < 0:
+ raise pmErr(status)
+ outName = str(name_p.value)
+ LIBC.free(name_p)
+ return outName
+
+ ##
+ # PMAPI Context Services
+
+ def pmNewContext(self, typed, name):
+ """PMAPI - NOOP - Establish a new PMAPI context (done in constructor)
+
+ This is unimplemented. A new context is established when a pmContext
+ object is created.
+ """
+ pass
+
+ def pmDestroyContext(self, handle):
+ """PMAPI - NOOP - Destroy a PMAPI context (done in destructor)
+
+ This is unimplemented. The context is destroyed when the pmContext
+ object is destroyed.
+ """
+ pass
+
+ def pmDupContext(self):
+ """PMAPI - Duplicate the current PMAPI Context
+
+ This supports copying a pmContext object
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmDupContext()
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmUseContext(self, handle):
+ """PMAPI - NOOP - Set the PMAPI context to that identified by handle
+
+ This is unimplemented. Context changes are handled by the individual
+ methods in a pmContext class instance.
+ """
+ pass
+
+ @staticmethod
+ def pmWhichContext():
+ """PMAPI - Returns the handle of the current PMAPI context
+ context = pmWhichContext()
+ """
+ status = LIBPCP.pmWhichContext()
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmAddProfile(self, pmdesc, instL):
+ """PMAPI - add instances to list that will be collected from indom
+
+ status = pmAddProfile(pmDesc pmdesc, c_uint instid)
+ """
+ if type(instL) == type(0):
+ numinst = 1
+ instA = (c_int * numinst)()
+ instA[0] = instL
+ elif instL == None or len(instL) == 0:
+ numinst = 0
+ instA = POINTER(c_int)()
+ else:
+ numinst = len(instL)
+ instA = (c_int * numinst)()
+ for index, value in enumerate(instL):
+ instA[index] = value
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmAddProfile(get_indom(pmdesc), numinst, instA)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmDelProfile(self, pmdesc, instL):
+ """PMAPI - delete instances from list to be collected from indom
+
+ status = pmDelProfile(pmDesc pmdesc, c_uint inst)
+ status = pmDelProfile(pmDesc pmdesc, [c_uint inst])
+ """
+ if instL == None or len(instL) == 0:
+ numinst = 0
+ instA = POINTER(c_int)()
+ else:
+ numinst = len(instL)
+ instA = (c_int * numinst)()
+ for index, value in enumerate(instL):
+ instA[index] = value
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmDelProfile(get_indom(pmdesc), numinst, instA)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmSetMode(self, mode, timeVal, delta):
+ """PMAPI - set interpolation mode for reading archive files
+ code = pmSetMode(c_api.PM_MODE_INTERP, timeval, 0)
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmSetMode(mode, pointer(timeVal), delta)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmReconnectContext(self):
+ """PMAPI - Reestablish the context connection
+
+ Unlike the underlying PMAPI function, this method takes no parameter.
+ This method simply attempts to reestablish the the context belonging
+ to its pmContext instance object.
+ """
+ status = LIBPCP.pmReconnectContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmGetContextHostName(self):
+ """PMAPI - Lookup the hostname for the given context
+
+ This method simply returns the hostname for the context belonging to
+ its pmContext instance object.
+
+ "hostname" = pmGetContextHostName()
+ """
+ buflen = c_api.PM_LOG_MAXHOSTLEN
+ buffer = ctypes.create_string_buffer(buflen)
+ return str(LIBPCP.pmGetContextHostName_r(self.ctx, buffer, buflen))
+
+ ##
+ # PMAPI Timezone Services
+
+ def pmNewContextZone(self):
+ """PMAPI - Query and set the current reporting timezone """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmNewContextZone()
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ @staticmethod
+ def pmNewZone(tz):
+ """PMAPI - Create new zone handle and set reporting timezone """
+ status = LIBPCP.pmNewZone(tz)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ @staticmethod
+ def pmUseZone(tz_handle):
+ """PMAPI - Sets the current reporting timezone """
+ status = LIBPCP.pmUseZone(tz_handle)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ @staticmethod
+ def pmWhichZone():
+ """PMAPI - Query the current reporting timezone """
+ tz_p = c_char_p()
+ status = LIBPCP.pmWhichZone(byref(tz_p))
+ if status < 0:
+ raise pmErr(status)
+ tz = str(tz_p.value)
+ LIBC.free(tz_p)
+ return tz
+
+ def pmLocaltime(self, seconds):
+ """PMAPI - convert the date and time for a reporting timezone """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ result = (tm)()
+ timetp = c_long(long(seconds))
+ LIBPCP.pmLocaltime(byref(timetp), byref(result))
+ return result
+
+ def pmCtime(self, seconds):
+ """PMAPI - format the date and time for a reporting timezone """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ result = ctypes.create_string_buffer(32)
+ timetp = c_long(long(seconds))
+ LIBPCP.pmCtime(byref(timetp), result)
+ return str(result.value)
+
+ ##
+ # PMAPI Metrics Services
+
+ def pmFetch(self, pmidA):
+ """PMAPI - Fetch pmResult from the target source
+
+ pmResult* pmresult = pmFetch(c_uint pmid[])
+ """
+ result_p = POINTER(pmResult)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmFetch(len(pmidA), pmidA, byref(result_p))
+ if status < 0:
+ raise pmErr(status)
+ return result_p
+
+ @staticmethod
+ def pmFreeResult(result_p):
+ """PMAPI - Free a result previously allocated by pmFetch
+ pmFreeResult(pmResult* pmresult)
+ """
+ LIBPCP.pmFreeResult(result_p)
+
+ def pmStore(self, result):
+ """PMAPI - Set values on target source, inverse of pmFetch
+ pmresult = pmStore(pmResult* pmresult)
+ """
+ LIBPCP.pmStore.argtypes = [(type(result))]
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmStore(result)
+ if status < 0:
+ raise pmErr(status)
+ return result
+
+
+ ##
+ # PMAPI Archive-Specific Services
+
+ def pmGetArchiveLabel(self):
+ """PMAPI - Get the label record from the archive
+ loglabel = pmGetArchiveLabel()
+ """
+ loglabel = pmLogLabel()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetArchiveLabel(byref(loglabel))
+ if status < 0:
+ raise pmErr(status)
+ return loglabel
+
+ def pmGetArchiveEnd(self):
+ """PMAPI - Get the last recorded timestamp from the archive
+ """
+ tvp = timeval()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmGetArchiveEnd(byref(tvp))
+ if status < 0:
+ raise pmErr(status)
+ return tvp
+
+ def pmGetInDomArchive(self, pmdescp):
+ """PMAPI - Get the instance IDs and names for an instance domain
+
+ ((instance1, instance2...) (name1, name2...)) pmGetInDom(pmDesc pmdesc)
+ """
+ instA_p = POINTER(c_int)()
+ nameA_p = POINTER(c_char_p)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ indom = get_indom(pmdescp)
+ status = LIBPCP.pmGetInDomArchive(indom, byref(instA_p), byref(nameA_p))
+ if status < 0:
+ raise pmErr(status)
+ if status > 0:
+ nameL = map(lambda x: str(nameA_p[x]), range(status))
+ instL = map(lambda x: int(instA_p[x]), range(status))
+ LIBC.free(instA_p)
+ LIBC.free(nameA_p)
+ else:
+ instL = None
+ nameL = None
+ return instL, nameL
+
+ def pmLookupInDomArchive(self, pmdesc, name):
+ """PMAPI - Lookup the instance id with the given name in the indom
+
+ c_uint instid = pmLookupInDomArchive(pmDesc pmdesc, "Instance")
+ """
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmLookupInDomArchive(get_indom(pmdesc), name)
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ def pmNameInDomArchive(self, pmdesc, inst):
+ """PMAPI - Lookup the text name of an instance in an instance domain
+
+ "string" = pmNameInDomArchive(pmDesc pmdesc, c_uint instid)
+ """
+ name_p = c_char_p()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ indom = get_indom(pmdesc)
+ status = LIBPCP.pmNameInDomArchive(indom, inst, byref(name_p))
+ if status < 0:
+ raise pmErr(status)
+ outName = str(name_p.value)
+ LIBC.free(name_p)
+ return outName
+
+ def pmFetchArchive(self):
+ """PMAPI - Fetch measurements from the target source
+
+ pmResult* pmresult = pmFetch()
+ """
+ result_p = POINTER(pmResult)()
+ status = LIBPCP.pmUseContext(self.ctx)
+ if status < 0:
+ raise pmErr(status)
+ status = LIBPCP.pmFetchArchive(byref(result_p))
+ if status < 0:
+ raise pmErr(status)
+ return result_p
+
+
+ ##
+ # PMAPI Ancilliary Support Services
+
+ @staticmethod
+ def pmGetConfig(variable):
+ """PMAPI - Return value from environment or pcp config file """
+ return str(LIBPCP.pmGetConfig(variable))
+
+ @staticmethod
+ def pmErrStr(code):
+ """PMAPI - Return value from environment or pcp config file """
+ errstr = ctypes.create_string_buffer(c_api.PM_MAXERRMSGLEN)
+ return str(LIBPCP.pmErrStr_r(code, errstr, c_api.PM_MAXERRMSGLEN))
+
+ @staticmethod
+ def pmExtractValue(valfmt, vlist, intype, outtype):
+ """PMAPI - Extract a value from a pmValue struct and convert its type
+
+ pmAtomValue = pmExtractValue(results.contents.get_valfmt(i),
+ results.contents.get_vlist(i, 0),
+ descs[i].contents.type,
+ c_api.PM_TYPE_FLOAT)
+ """
+ outAtom = pmAtomValue()
+ status = LIBPCP.pmExtractValue(valfmt, vlist, intype,
+ byref(outAtom), outtype)
+ if status < 0:
+ raise pmErr(status)
+
+ if outtype == c_api.PM_TYPE_STRING:
+ # Get pointer to C string
+ c_str = c_char_p()
+ memmove(byref(c_str), addressof(outAtom) + pmAtomValue.cp.offset,
+ sizeof(c_char_p))
+ # Convert to a python string and have result point to it
+ outAtom.cp = outAtom.cp
+ # Free the C string
+ LIBC.free(c_str)
+ return outAtom
+
+ @staticmethod
+ def pmConvScale(inType, inAtom, desc, metric_idx, outUnits):
+ """PMAPI - Convert a value to a different scale
+
+ pmAtomValue = pmConvScale(c_api.PM_TYPE_FLOAT, pmAtomValue,
+ pmDesc*, 3, c_api.PM_SPACE_MBYTE)
+ """
+ if type(outUnits) == type(int()):
+ pmunits = pmUnits()
+ pmunits.dimSpace = 1
+ pmunits.scaleSpace = outUnits
+ else:
+ pmunits = outUnits
+ outAtom = pmAtomValue()
+ status = LIBPCP.pmConvScale(inType, byref(inAtom),
+ byref(desc[metric_idx].contents.units),
+ byref(outAtom), byref(pmunits))
+ if status < 0:
+ raise pmErr(status)
+ return outAtom
+
+ @staticmethod
+ def pmUnitsStr(units):
+ """PMAPI - Convert units struct to a readable string """
+ unitstr = ctypes.create_string_buffer(64)
+ return str(LIBPCP.pmUnitsStr_r(units, unitstr, 64))
+
+ @staticmethod
+ def pmNumberStr(value):
+ """PMAPI - Convert double value to fixed-width string """
+ numstr = ctypes.create_string_buffer(8)
+ return str(LIBPCP.pmNumberStr_r(value, numstr, 8))
+
+ @staticmethod
+ def pmIDStr(pmid):
+ """PMAPI - Convert a pmID to a readable string """
+ pmidstr = ctypes.create_string_buffer(32)
+ return str(LIBPCP.pmIDStr_r(pmid, pmidstr, 32))
+
+ @staticmethod
+ def pmInDomStr(pmdescp):
+ """PMAPI - Convert an instance domain ID to a readable string
+ "indom" = pmGetInDom(pmDesc pmdesc)
+ """
+ indomstr = ctypes.create_string_buffer(32)
+ return str(LIBPCP.pmInDomStr_r(get_indom(pmdescp), indomstr, 32))
+
+ @staticmethod
+ def pmTypeStr(typed):
+ """PMAPI - Convert a performance metric type to a readable string
+ "type" = pmTypeStr(c_api.PM_TYPE_FLOAT)
+ """
+ typestr = ctypes.create_string_buffer(32)
+ return str(LIBPCP.pmTypeStr_r(typed, typestr, 32))
+
+ @staticmethod
+ def pmAtomStr(atom, typed):
+ """PMAPI - Convert a value atom to a readable string
+ "value" = pmAtomStr(atom, c_api.PM_TYPE_U32)
+ """
+ atomstr = ctypes.create_string_buffer(96)
+ return str(LIBPCP.pmAtomStr(byref(atom), typed, atomstr, 96))
+
+ @staticmethod
+ def pmPrintValue(fileObj, result, ptype, vset_idx, vlist_idx, min_width):
+ """PMAPI - Print the value of a metric
+ pmPrintValue(file, value, pmdesc, vset_index, vlist_index, min_width)
+ """
+ LIBPCP.pmPrintValue(ctypes.pythonapi.PyFile_AsFile(fileObj),
+ c_int(result.contents.vset[vset_idx].contents.valfmt),
+ c_int(ptype.contents.type),
+ byref(result.contents.vset[vset_idx].contents.vlist[vlist_idx]),
+ min_width)
+
+ @staticmethod
+ def pmflush():
+ """PMAPI - flush the internal buffer shared with pmprintf """
+ status = LIBPCP.pmflush()
+ if status < 0:
+ raise pmErr(status)
+ return status
+
+ @staticmethod
+ def pmprintf(fmt, *args):
+ """PMAPI - append message to internal buffer for later printing """
+ status = LIBPCP.pmprintf(fmt, *args)
+ if status < 0:
+ raise pmErr(status)
+
+ @staticmethod
+ def pmSortInstances(result_p):
+ """PMAPI - sort all metric instances in result returned by pmFetch """
+ LIBPCP.pmSortInstances(result_p)
+ return None
+
+ @staticmethod
+ def pmParseInterval(interval):
+ """PMAPI - parse a textual time interval into a timeval struct
+ (timeval_ctype, '') = pmParseInterval("time string")
+ """
+ return (timeval.fromInterval(interval), '')
+
+ @staticmethod
+ def pmParseMetricSpec(string, isarch = 0, source = ''):
+ """PMAPI - parse a textual metric specification into a struct
+ (result, '') = pmParseMetricSpec("hinv.ncpu", 0, "localhost")
+ """
+ return (pmMetricSpec.fromString(string, isarch, source), '')
+
+ @staticmethod
+ def pmtimevalSleep(tvp):
+ """ Delay for a specified amount of time (timeval).
+ Useful for implementing tools that do metric sampling.
+ Single arg is timeval in tuple returned from pmParseInterval().
+ """
+ return tvp.sleep()
+
diff --git a/src/python/pcp/pmcc.py b/src/python/pcp/pmcc.py
new file mode 100644
index 0000000..8189767
--- /dev/null
+++ b/src/python/pcp/pmcc.py
@@ -0,0 +1,620 @@
+""" Convenience Classes building on the base PMAPI extension module """
+#
+# pmcc.py
+#
+# Copyright (C) 2013-2014 Red Hat
+# Copyright (C) 2009-2012 Michael T. Werner
+#
+# 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.
+#
+
+from sys import stderr
+from ctypes import c_int, c_uint, c_char_p, cast, POINTER
+from pcp.pmapi import (pmContext, pmResult, pmValueSet, pmValue, pmDesc,
+ pmErr, pmOptions, timeval)
+from cpmapi import (PM_CONTEXT_HOST, PM_CONTEXT_ARCHIVE, PM_INDOM_NULL,
+ PM_IN_NULL, PM_ID_NULL, PM_SEM_COUNTER, PM_ERR_EOL, PM_TYPE_DOUBLE)
+
+
+class MetricCore(object):
+ """
+ Core metric information that can be queried from the PMAPI
+ PMAPI metrics are unique by name, and MetricCores should be also
+ rarely, some PMAPI metrics with different names might have identical PMIDs
+ PMAPI metrics are unique by (name) and by (name,pmid) - _usually_ by (pmid)
+ too.
+ """
+
+ def __init__(self, ctx, name, pmid):
+ self.ctx = ctx
+ self.name = name
+ self.pmid = pmid
+ self.desc = None
+ self.text = None
+ self.help = None
+
+
+class Metric(object):
+ """
+ Additional metric information, such as conversion factors and values
+ several instances of Metric may share a MetricCore instance
+ """
+
+ ##
+ # constructor
+
+ def __init__(self, core):
+ self._core = core # MetricCore
+ self._vset = None # pmValueSet member
+ self._values = None
+ self._prevvset = None
+ self._prevValues = None
+ self._convType = core.desc.contents.type
+ self._convUnits = None
+ self._errorStatus = None
+ self._netValues = None # (instance, name, value)
+ self._netPrevValues = None # (instance, name, value)
+ self._netConvertedValues = None # (instance, name, value)
+
+ ##
+ # core property read methods
+
+ def _R_ctx(self):
+ return self._core.ctx
+ def _R_name(self):
+ return self._core.name
+ def _R_pmid(self):
+ return self._core.pmid
+ def _R_desc(self):
+ return self._core.desc
+ def _R_text(self):
+ return self._core.text
+ def _R_help(self):
+ return self._core.help
+
+ def get_vlist(self, vset, vlist_idx):
+ """ Return the vlist[vlist_idx] of vset[vset_idx] """
+ listptr = cast(vset.contents.vlist, POINTER(pmValue))
+ return listptr[vlist_idx]
+
+ def get_inst(self, vset, vlist_idx):
+ """ Return the inst for vlist[vlist_idx] of vset[vset_idx] """
+ return self.get_vlist(vset, vset_idx, vlist_idx).inst
+
+ def computeValues(self, inValues):
+ """ Extract the value for a singleton or list of instances
+ as a triple (inst, name, val)
+ """
+ vset = inValues
+ ctx = self.ctx
+ instD = ctx.mcGetInstD(self.desc.contents.indom)
+ valL = []
+ for i in range(vset.numval):
+ instval = self.get_vlist(vset, i)
+ try:
+ name = instD[instval.inst]
+ except KeyError:
+ name = ""
+ outAtom = self.ctx.pmExtractValue(
+ vset.valfmt, instval, self.desc.type, self._convType)
+ if self._convUnits:
+ desc = (POINTER(pmDesc) * 1)()
+ desc[0] = self.desc
+ outAtom = self.ctx.pmConvScale(
+ self._convType, outAtom, desc, 0, self._convUnits)
+ value = outAtom.dref(self._convType)
+ valL.append((instval, name, value))
+ return valL
+
+ def _find_previous_instval(self, index, inst, pvset):
+ """ Find a metric instance in the previous resultset """
+ if index <= pvset.numval:
+ pinstval = self.get_vlist(pvset, index)
+ if inst == pinstval.inst:
+ return pinstval
+ for pi in range(pvset.numval):
+ pinstval = self.get_vlist(pvset, pi)
+ if inst == pinstval.inst:
+ return pinstval
+ return None
+
+ def convertValues(self, values, prevValues, delta):
+ """ Extract the value for a singleton or list of instances as a
+ triple (inst, name, val) for COUNTER metrics with the value
+ delta calculation applied (for rate conversion).
+ """
+ if self.desc.sem != PM_SEM_COUNTER:
+ return self.computeValues(values)
+ if prevValues == None:
+ return None
+ pvset = prevValues
+ vset = values
+ ctx = self.ctx
+ instD = ctx.mcGetInstD(self.desc.contents.indom)
+ valL = []
+ for i in range(vset.numval):
+ instval = self.get_vlist(vset, i)
+ pinstval = self._find_previous_instval(i, instval.inst, pvset)
+ if pinstval == None:
+ continue
+ try:
+ name = instD[instval.inst]
+ except KeyError:
+ name = ""
+ outAtom = self.ctx.pmExtractValue(vset.valfmt,
+ instval, self.desc.type, PM_TYPE_DOUBLE)
+ poutAtom = self.ctx.pmExtractValue(pvset.valfmt,
+ pinstval, self.desc.type, PM_TYPE_DOUBLE)
+ if self._convUnits:
+ desc = (POINTER(pmDesc) * 1)()
+ desc[0] = self.desc
+ outAtom = self.ctx.pmConvScale(
+ PM_TYPE_DOUBLE, outAtom, desc, 0, self._convUnits)
+ poutAtom = self.ctx.pmConvScale(
+ PM_TYPE_DOUBLE, poutAtom, desc, 0, self._convUnits)
+ value = outAtom.dref(PM_TYPE_DOUBLE)
+ pvalue = poutAtom.dref(PM_TYPE_DOUBLE)
+ if (value >= pvalue):
+ valL.append((instval, name, (value - pvalue) / delta))
+ return valL
+
+ def _R_values(self):
+ return self._values
+ def _R_prevValues(self):
+ return self._prevValues
+ def _R_convType(self):
+ return self._convType
+ def _R_convUnits(self):
+ return self._convUnits
+
+ def _R_errorStatus(self):
+ return self._errorStatus
+
+ def _R_netConvValues(self):
+ return self._netConvValues
+
+ def _R_netPrevValues(self):
+ if not self._prevvset:
+ return None
+ self._netPrevValues = self.computeValues(self._prevvset)
+ return self._netPrevValues
+
+ def _R_netValues(self):
+ if not self._vset:
+ return None
+ self._netValues = self.computeValues(self._vset)
+ return self._netValues
+
+ def _W_values(self, values):
+ self._prev = self._values
+ self._values = values
+ self._netPrev = self._netValue
+ self._netValue = None
+
+ def _W_convType(self, value):
+ self._convType = value
+ def _W_convUnits(self, value):
+ self._convUnits = value
+
+ # interface to properties in MetricCore
+ ctx = property(_R_ctx, None, None, None)
+ name = property(_R_name, None, None, None)
+ pmid = property(_R_pmid, None, None, None)
+ desc = property(_R_desc, None, None, None)
+ text = property(_R_text, None, None, None)
+ help = property(_R_help, None, None, None)
+
+ # properties specific to this instance
+ values = property(_R_values, _W_values, None, None)
+ prevValues = property(_R_prevValues, None, None, None)
+ convType = property(_R_convType, _W_convType, None, None)
+ convUnits = property(_R_convUnits, _W_convUnits, None, None)
+ errorStatus = property(_R_errorStatus, None, None, None)
+ netValues = property(_R_netValues, None, None, None)
+ netPrevValues = property(_R_netPrevValues, None, None, None)
+ netConvValues = property(_R_netConvValues, None, None, None)
+
+ def metricPrint(self):
+ indomstr = self.ctx.pmInDomStr(self.desc.indom)
+ print(" ", "indom:", indomstr)
+ instD = self.ctx.mcGetInstD(self.desc.indom)
+ for inst, name, val in self.netValues:
+ print(" ", name, val)
+
+ def metricConvert(self, delta):
+ convertedList = self.convertValues(self._vset, self._prevvset, delta)
+ self._netConvValues = convertedList
+ return self._netConvValues
+
+
+class MetricCache(pmContext):
+ """
+ A cache of MetricCores is kept to reduce calls into the PMAPI library
+ this also slightly reduces the memory footprint of Metric instances
+ that share a common MetricCore
+ a cache of instance domain information is also kept, which further
+ reduces calls into the PMAPI and reduces the memory footprint of
+ Metric objects that share a common instance domain
+ """
+
+ ##
+ # overloads
+
+ def __init__(self, typed = PM_CONTEXT_HOST, target = "local:"):
+ pmContext.__init__(self, typed, target)
+ self._mcIndomD = {}
+ self._mcByNameD = {}
+ self._mcByPmidD = {}
+
+ ##
+ # methods
+
+ def mcGetInstD(self, indom):
+ """ Query the instance : instance_list dictionary """
+ return self._mcIndomD[indom]
+
+ def _mcAdd(self, core):
+ """ Update the dictionary """
+ indom = core.desc.contents.indom
+ if not self._mcIndomD.has_key(indom):
+ if c_int(indom).value == c_int(PM_INDOM_NULL).value:
+ instmap = { PM_IN_NULL : "PM_IN_NULL" }
+ else:
+ if self._type == PM_CONTEXT_ARCHIVE:
+ instL, nameL = self.pmGetInDomArchive(core.desc)
+ else:
+ instL, nameL = self.pmGetInDom(core.desc)
+ if instL != None and nameL != None:
+ instmap = dict(zip(instL, nameL))
+ else:
+ instmap = {}
+ self._mcIndomD.update({indom: instmap})
+
+ self._mcByNameD.update({core.name: core})
+ self._mcByPmidD.update({core.pmid: core})
+
+ def mcGetCoresByName(self, nameL):
+ """ Update the core (metric id, description,...) list """
+ coreL = []
+ missD = None
+ errL = None
+ # lookup names in cache
+ for index, name in enumerate(nameL):
+ # lookup metric core in cache
+ core = self._mcByNameD.get(name)
+ if not core:
+ # cache miss
+ if not missD:
+ missD = {}
+ missD.update({name: index})
+ coreL.append(core)
+
+ # some cache lookups missed, fetch pmids and build missing MetricCores
+ if missD:
+ idL, errL = self.mcFetchPmids(missD.keys())
+ for name, pmid in idL:
+ if pmid == PM_ID_NULL:
+ # fetch failed for the given metric name
+ if not errL:
+ errL = []
+ errL.append(name)
+ else:
+ # create core pmDesc
+ newcore = self._mcCreateCore(name, pmid)
+ # update core ref in return list
+ coreL[missD[name]] = newcore
+
+ return coreL, errL
+
+ def _mcCreateCore(self, name, pmid):
+ """ Update the core description """
+ newcore = MetricCore(self, name, pmid)
+ try:
+ newcore.desc = self.pmLookupDesc(pmid)
+ except pmErr as error:
+ fail = "%s: pmLookupDesc: %s" % (error.progname(), error.message())
+ print >> stderr, fail
+ raise SystemExit(1)
+
+ # insert core into cache
+ self._mcAdd(newcore)
+ return newcore
+
+ def mcFetchPmids(self, nameL):
+ """ Update the core metric ids. note: some names have identical pmids """
+ errL = None
+ nameA = (c_char_p * len(nameL))()
+ for index, name in enumerate(nameL):
+ nameA[index] = c_char_p(name)
+ try:
+ pmidArray = self.pmLookupName(nameA)
+ if len(pmidArray) < len(nameA):
+ missing = "%d of %d metric names" % (len(pmidArray), len(nameA))
+ print >> stderr, "Cannot resolve", missing
+ raise SystemExit(1)
+ except pmErr as error:
+ fail = "%s: pmLookupName: %s" % (error.progname(), error.message())
+ print >> stderr, fail
+ raise SystemExit(1)
+
+ return zip(nameA, pmidArray), errL
+
+
+class MetricGroup(dict):
+ """
+ Manages a group of metrics for fetching the values of
+ a MetricGroup is a dictionary of Metric objects, for which data can
+ be fetched from a target system using a single call to pmFetch
+ the Metric objects are indexed by the metric name
+ pmFetch fetches data for a list of pmIDs, so there is also a shadow
+ dictionary keyed by pmID, along with a shadow list of pmIDs
+ """
+
+ ##
+ # property read methods
+
+ def _R_contextCache(self):
+ return self._ctx
+ def _R_pmidArray(self):
+ return self._pmidArray
+ def _R_timestamp(self):
+ return self._result.contents.timestamp
+ def _R_result(self):
+ return self._result
+ def _R_prevTimestamp(self):
+ return self._prev.contents.timestamp
+ def _R_prev(self):
+ return self._prev
+
+ ##
+ # property write methods
+
+ def _W_result(self, pmresult):
+ self._prev = self._result
+ self._result = pmresult
+
+ ##
+ # property definitions
+
+ contextCache = property(_R_contextCache, None, None, None)
+ pmidArray = property(_R_pmidArray, None, None, None)
+ result = property(_R_result, _W_result, None, None)
+ timestamp = property(_R_timestamp, None, None, None)
+ prev = property(_R_prev, None, None, None)
+ prevTimestamp = property(_R_prevTimestamp, None, None, None)
+
+ ##
+ # overloads
+
+ def __init__(self, contextCache, inL = []):
+ dict.__init__(self)
+ self._ctx = contextCache
+ self._pmidArray = None
+ self._result = None
+ self._prev = None
+ self._altD = {}
+ self.mgAdd(inL)
+
+ ##
+ # methods
+
+ def mgAdd(self, nameL):
+ """ Create the list of Metric(s) """
+ coreL, errL = self._ctx.mcGetCoresByName(nameL)
+ for core in coreL:
+ metric = Metric(core)
+ self.update({metric.name: metric})
+ self._altD.update({metric.pmid: metric})
+ n = len(self)
+ self._pmidArray = (c_uint * n)()
+ for x, key in enumerate(self.keys()):
+ self._pmidArray[x] = c_uint(self[key].pmid)
+
+ def mgFetch(self):
+ """ Fetch the list of Metric values. Save the old value. """
+ try:
+ self.result = self._ctx.pmFetch(self._pmidArray)
+ # update the result entries in each metric
+ result = self.result.contents
+ for i in range(self.result.contents.numpmid):
+ pmid = self.result.contents.get_pmid(i)
+ vset = self.result.contents.get_vset(i)
+ self._altD[pmid]._prevvset = self._altD[pmid]._vset
+ self._altD[pmid]._vset = vset
+ except pmErr as error:
+ if error.args[0] == PM_ERR_EOL:
+ raise SystemExit(0)
+ fail = "%s: pmFetch: %s" % (error.progname(), error.message())
+ print >> stderr, fail
+ raise SystemExit(1)
+
+ def mgDelta(self):
+ """
+ Sample delta - used for rate conversion calculations, which
+ requires timestamps from successive samples.
+ """
+ if self._prev != None:
+ prevTimestamp = float(self.prevTimestamp)
+ else:
+ prevTimestamp = 0.0
+ return float(self.timestamp) - prevTimestamp
+
+
+class MetricGroupPrinter(object):
+ """
+ Handles reporting of MetricGroups within a GroupManager.
+ This object is called upon at the end of each fetch when
+ new values are available. It is also responsible for
+ producing any initial (or on-going) header information
+ that the tool may wish to report.
+ """
+ def report(self, manager):
+ """ Base implementation, all tools should override """
+ for group_name in manager.keys():
+ group = manager[group_name]
+ for metric_name in group.keys():
+ group[metric_name].metricPrint()
+
+ def convert(self, manager):
+ """ Do conversion for all metrics across all groups """
+ for group_name in manager.keys():
+ group = manager[group_name]
+ delta = group.mgDelta()
+ for metric_name in group.keys():
+ group[metric_name].metricConvert(delta)
+
+
+class MetricGroupManager(dict, MetricCache):
+ """
+ Manages a dictionary of MetricGroups which can be pmFetch'ed
+ inherits from MetricCache, which inherits from pmContext
+ """
+
+ ##
+ # property access methods
+
+ def _R_options(self): # command line option object
+ return self._options
+
+ def _W_options(self, options):
+ self._options = options
+
+ def _R_default_delta(self): # default interval unless command line set
+ return self._default_delta
+ def _W_default_delta(self, delta):
+ self._default_delta = delta
+ def _R_default_pause(self): # default reporting delay (archives only)
+ return self._default_pause
+ def _W_default_pause(self, pause):
+ self._default_pause = pause
+
+ def _W_printer(self, printer): # helper class for reporting
+ self._printer = printer
+ def _R_counter(self): # fetch iteration count, useful for printer
+ return self._counter
+
+ ##
+ # property definitions
+
+ options = property(_R_options, _W_options, None, None)
+ default_delta = property(_R_default_delta, _W_default_delta, None, None)
+ default_pause = property(_R_default_pause, _W_default_pause, None, None)
+
+ printer = property(None, _W_printer, None, None)
+ counter = property(_R_counter, None, None, None)
+
+ ##
+ # overloads
+
+ def __init__(self, typed = PM_CONTEXT_HOST, target = "local:"):
+ dict.__init__(self)
+ MetricCache.__init__(self, typed, target)
+ self._options = None
+ self._default_delta = timeval(1, 0)
+ self._default_pause = None
+ self._printer = None
+ self._counter = 0
+
+ def __setitem__(self, attr, value = []):
+ if self.has_key(attr):
+ raise KeyError("metric group with that key already exists")
+ else:
+ dict.__setitem__(self, attr, MetricGroup(self, inL = value))
+
+ @classmethod
+ def builder(build, options, argv):
+ """ Helper interface, simple PCP monitor argument parsing. """
+ manager = build.fromOptions(options, argv)
+ manager._default_delta = timeval(options.delta, 0)
+ manager._options = options
+ return manager
+
+ ##
+ # methods
+
+ def _computeSamples(self):
+ """ Calculate the number of samples we are to take.
+ This is based on command line options --samples but also
+ must consider --start, --finish and --interval. If none
+ of these were presented, a zero return means "infinite".
+ """
+ if self._options == None:
+ return 0 # loop until interrupted or PM_ERR_EOL
+ samples = self._options.pmGetOptionSamples()
+ if samples != None:
+ return samples
+ if self._options.pmGetOptionFinishOptarg() == None:
+ return 0 # loop until interrupted or PM_ERR_EOL
+ origin = self._options.pmGetOptionOrigin()
+ finish = self._options.pmGetOptionFinish()
+ delta = self._options.pmGetOptionInterval()
+ if delta == None:
+ delta = self._default_delta
+ period = (delta.tv_sec * 1.0e6 + delta.tv_usec) / 1e6
+ window = float(finish.tv_sec - origin.tv_sec)
+ window += float((finish.tv_usec - origin.tv_usec) / 1e6)
+ window /= period
+ return int(window + 0.5) # roundup to positive number
+
+ def _computePauseTime(self):
+ """ Figure out how long to sleep between samples.
+ This needs to take into account whether we were explicitly
+ asked for a delay (independent of context type, --pause),
+ whether this is an archive or live context, and the sampling
+ --interval (including the default value, if none requested).
+ """
+ if self._default_pause != None:
+ return self._default_pause
+ if self.type == PM_CONTEXT_ARCHIVE:
+ self._default_pause = timeval(0, 0)
+ elif self._options != None:
+ pause = self._options.pmGetOptionInterval()
+ if pause != None:
+ self._default_pause = pause
+ else:
+ self._default_pause = self._default_delta
+ else:
+ self._default_pause = self._default_delta
+ return self._default_pause
+
+ def fetch(self):
+ """ Perform fetch operation on all of the groups. """
+ for group in self.keys():
+ self[group].mgFetch()
+
+ def run(self):
+ """ Using options specification, loop fetching and reporting,
+ pausing for the requested time interval between updates.
+ Transparently handles archive/live mode differences.
+ Note that this can be different to the sampling interval
+ in archive mode, but is usually the same as the sampling
+ interval in live mode.
+ """
+ samples = self._computeSamples()
+ timer = self._computePauseTime()
+ try:
+ self.fetch()
+ while True:
+ self._counter += 1
+ if samples == 0 or self._counter <= samples:
+ self._printer.report(self)
+ if self._counter == samples:
+ break
+ timer.sleep()
+ self.fetch()
+ except SystemExit as code:
+ return code
+ except KeyboardInterrupt:
+ pass
+ return 0
diff --git a/src/python/pcp/pmda.py b/src/python/pcp/pmda.py
new file mode 100644
index 0000000..03d9739
--- /dev/null
+++ b/src/python/pcp/pmda.py
@@ -0,0 +1,407 @@
+# pylint: disable=C0103
+"""Wrapper module for libpcp_pmda - Performace Co-Pilot Domain Agent API
+#
+# 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.
+#
+
+# See pmdasimple.py for an example use of this module.
+"""
+
+import os
+
+import cpmapi
+import cpmda
+from pcp.pmapi import pmContext as PCP
+from pcp.pmapi import pmInDom, pmDesc, pmUnits
+
+from ctypes.util import find_library
+from ctypes import CDLL, c_int, c_long, c_char_p, c_void_p, cast, byref
+from ctypes import addressof, sizeof, POINTER, Structure, create_string_buffer
+
+## Performance Co-Pilot PMDA library (C)
+LIBPCP_PMDA = CDLL(find_library("pcp_pmda"))
+
+
+###
+## PMDA Indom Cache Services
+#
+LIBPCP_PMDA.pmdaCacheStore.restype = c_int
+LIBPCP_PMDA.pmdaCacheStore.argtypes = [pmInDom, c_int, c_char_p, c_void_p]
+LIBPCP_PMDA.pmdaCacheStoreKey.restype = c_int
+LIBPCP_PMDA.pmdaCacheStoreKey.argtypes = [
+ pmInDom, c_int, c_char_p, c_int, c_void_p]
+LIBPCP_PMDA.pmdaCacheLookup.restype = c_int
+LIBPCP_PMDA.pmdaCacheLookup.argtypes = [
+ pmInDom, c_int, POINTER(c_char_p), POINTER(c_void_p)]
+LIBPCP_PMDA.pmdaCacheLookupName.restype = c_int
+LIBPCP_PMDA.pmdaCacheLookupName.argtypes = [
+ pmInDom, c_char_p, POINTER(c_int), POINTER(c_void_p)]
+LIBPCP_PMDA.pmdaCacheLookupKey.restype = c_int
+LIBPCP_PMDA.pmdaCacheLookupKey.argtypes = [
+ pmInDom, c_char_p, c_int, c_void_p, POINTER(c_char_p),
+ POINTER(c_int), POINTER(c_void_p)]
+LIBPCP_PMDA.pmdaCacheOp.restype = c_int
+LIBPCP_PMDA.pmdaCacheOp.argtypes = [pmInDom, c_long]
+
+
+##
+# Definition of structures used by C library libpcp_pmda, derived from <pcp/pmda.h>
+#
+
+class pmdaMetric(Structure):
+ """ Structure describing a metric definition for a PMDA """
+ _fields_ = [("m_user", c_void_p),
+ ("m_desc", pmDesc)]
+
+ def __init__(self, pmid, typeof, indom, sem, units):
+ Structure.__init__(self)
+ self.m_user = None
+ self.m_desc.pmid = pmid
+ self.m_desc.type = typeof
+ self.m_desc.indom = indom
+ self.m_desc.sem = sem
+ self.m_desc.units = units
+
+ def __str__(self):
+ return "pmdaMetric@%#lx pmid=%#lx type=%d" % (addressof(self), self.m_desc.pmid, self.m_desc.type)
+
+class pmdaInstid(Structure):
+ """ Structure describing an instance (index/name) a PMDA """
+ _fields_ = [("i_inst", c_int),
+ ("i_name", c_char_p)]
+
+ def __init__(self, instid, name):
+ Structure.__init__(self)
+ self.i_inst = instid
+ self.i_name = name
+
+ def __str__(self):
+ return "pmdaInstid@%#lx index=%d name=%s" % (addressof(self), self.i_inst, self.i_name)
+
+class pmdaIndom(Structure):
+ """ Structure describing an instance domain within a PMDA """
+ _fields_ = [("it_indom", pmInDom),
+ ("it_numinst", c_int),
+ ("it_set", POINTER(pmdaInstid))]
+
+ def __init__(self, indom, insts):
+ Structure.__init__(self)
+ self.it_numinst = 0
+ self.it_set = None
+ self.it_indom = indom
+ self.set_instances(indom, insts)
+
+ def set_list_instances(self, insts):
+ instance_count = len(insts)
+ if (instance_count == 0):
+ return
+ instance_array = (pmdaInstid * instance_count)()
+ for i in xrange(instance_count):
+ instance_array[i].i_inst = insts[i].i_inst
+ instance_array[i].i_name = insts[i].i_name
+ self.it_set = instance_array
+
+ def set_dict_instances(self, indom, insts):
+ LIBPCP_PMDA.pmdaCacheOp(indom, cpmda.PMDA_CACHE_INACTIVE)
+ for key in insts.keys():
+ LIBPCP_PMDA.pmdaCacheStore(indom, cpmda.PMDA_CACHE_ADD, key, byref(insts[key]))
+ LIBPCP_PMDA.pmdaCacheOp(indom, cpmda.PMDA_CACHE_SAVE)
+
+ def set_instances(self, indom, insts):
+ if (insts == None):
+ self.it_numinst = 0 # not yet known if cache indom or not
+ elif (isinstance(insts, dict)):
+ self.it_numinst = -1 # signifies cache indom (no it_set)
+ self.set_dict_instances(indom, insts)
+ else:
+ self.it_numinst = len(insts) # signifies an old-school array indom
+ self.set_list_instances(insts)
+
+ def __str__(self):
+ return "pmdaIndom@%#lx indom=%#lx num=%d" % (addressof(self), self.it_indom, self.it_numinst)
+
+class pmdaUnits(pmUnits):
+ """ Wrapper class for PMDAs defining their metrics (avoids pmapi import) """
+ def __init__(self, dimS, dimT, dimC, scaleS, scaleT, scaleC):
+ pmUnits.__init__(self, dimS, dimT, dimC, scaleS, scaleT, scaleC)
+
+
+###
+## Convenience Classes for PMDAs
+#
+
+class MetricDispatch(object):
+ """ Helper for PMDA class which manages metric dispatch
+ table, metric namespace, descriptors and help text.
+
+ Overall strategy is to interface to the C code in
+ python/pmda.c here, using a void* handle to the PMDA
+ dispatch structure (allocated and managed in C code).
+
+ In addition, several dictionaries for metric related
+ strings are managed here (names, help text) - cached
+ for quick lookups in callback routines.
+ """
+
+ ##
+ # overloads
+
+ def __init__(self, domain, name, logfile, helpfile):
+ self.clear_indoms()
+ self.clear_metrics()
+ cpmda.init_dispatch(domain, name, logfile, helpfile)
+
+ def clear_indoms(self):
+ self._indomtable = []
+ self._indoms = {}
+ self._indom_oneline = {}
+ self._indom_helptext = {}
+
+ def clear_metrics(self):
+ self._metrictable = []
+ self._metrics = {}
+ self._metric_names = {}
+ self._metric_oneline = {}
+ self._metric_helptext = {}
+
+ def reset_metrics(self):
+ self.clear_metrics()
+ cpmda.set_need_refresh()
+
+ ##
+ # general PMDA class methods
+
+ def pmns_refresh(self):
+ cpmda.pmns_refresh(self._metric_names)
+
+ def connect_pmcd(self):
+ cpmda.connect_pmcd()
+
+ def add_metric(self, name, metric, oneline = '', text = ''):
+ pmid = metric.m_desc.pmid
+ if (pmid in self._metric_names):
+ raise KeyError('attempt to add_metric with an existing name')
+ if (pmid in self._metrics):
+ raise KeyError('attempt to add_metric with an existing PMID')
+
+ self._metrictable.append(metric)
+ self._metrics[pmid] = metric
+ self._metric_names[pmid] = name
+ self._metric_oneline[pmid] = oneline
+ self._metric_helptext[pmid] = text
+ cpmda.set_need_refresh()
+
+ def add_indom(self, indom, oneline = '', text = ''):
+ indomid = indom.it_indom
+ for entry in self._indomtable:
+ if (entry.it_indom == indomid):
+ raise KeyError('attempt to add_indom with an existing ID')
+ self._indomtable.append(indom)
+ self._indoms[indomid] = indom
+ self._indom_oneline[indomid] = oneline
+ self._indom_helptext[indomid] = text
+
+ def replace_indom(self, indom, insts):
+ replacement = pmdaIndom(indom, insts)
+ # list indoms need to keep the table up-to-date for libpcp_pmda
+ if (isinstance(insts, list)):
+ for entry in self._indomtable:
+ if (entry.it_indom == indom):
+ entry = replacement
+ self._indoms[indom] = replacement
+
+ def inst_lookup(self, indom, instance):
+ """
+ Lookup the value associated with an (internal) instance ID
+ within a specific instance domain (only valid with indoms
+ of cache type - array indom will always return None).
+ """
+ entry = self._indoms[indom]
+ if (entry.it_numinst < 0):
+ value = (c_void_p)()
+ sts = LIBPCP_PMDA.pmdaCacheLookup(indom, instance, None, byref(value))
+ if (sts == cpmda.PMDA_CACHE_ACTIVE):
+ return value
+ return None
+
+ def inst_name_lookup(self, indom, instance):
+ """
+ Lookup the name associated with an (internal) instance ID within
+ a specific instance domain.
+ """
+ entry = self._indoms[indom]
+ if (entry.it_numinst < 0):
+ name = (c_char_p)()
+ sts = LIBPCP_PMDA.pmdaCacheLookup(indom, instance, byref(name), None)
+ if (sts == cpmda.PMDA_CACHE_ACTIVE):
+ return name.value
+ elif (entry.it_numinst > 0 and entry.it_indom == indom):
+ for inst in entry.it_set:
+ if (inst.i_inst == instance):
+ return inst.i_name
+ return None
+
+
+class PMDA(MetricDispatch):
+ """ Defines a PCP performance metrics domain agent
+ Used to add new metrics into the PCP toolkit.
+ """
+
+ ##
+ # property read methods
+
+ def read_name(self):
+ """ Property for name of this PMDA """
+ return self._name
+
+ def read_domain(self):
+ """ Property for unique domain number of this PMDA """
+ return self._domain
+
+ ##
+ # property definitions
+
+ name = property(read_name, None, None, None)
+ domain = property(read_domain, None, None, None)
+
+ ##
+ # overloads
+
+ def __init__(self, name, domain):
+ self._name = name
+ self._domain = domain
+ logfile = name + '.log'
+ pmdaname = 'pmda' + name
+ helpfile = '%s/%s/help' % (PCP.pmGetConfig('PCP_PMDAS_DIR'), name)
+ MetricDispatch.__init__(self, domain, pmdaname, logfile, helpfile)
+
+
+ ##
+ # general PMDA class methods
+
+ def domain_write(self):
+ """
+ Write out the domain.h file (used during installation)
+ """
+ print('#define %s %d' % (self._name.upper(), self._domain))
+
+ def pmns_write(self, root):
+ """
+ Write out the namespace file (used during installation)
+ """
+ pmns = self._metric_names
+ prefixes = set([pmns[key].split('.')[0] for key in pmns])
+ indent = (root == 'root')
+ lead = ''
+ if indent:
+ lead = '\t'
+ print('root {')
+ for prefix in prefixes:
+ print('%s%s\t%d:*:*' % (lead, prefix, self._domain))
+ if indent:
+ print('}')
+
+ def run(self):
+ """
+ All the real work happens herein; we can be called in one of three
+ situations, determined by environment variables. First couple are
+ during the agent Install process, where the domain.h and namespace
+ files need to be created. The third case is the real mccoy, where
+ an agent is actually being started by pmcd/dbpmda and makes use of
+ libpcp_pmda to talk PCP protocol.
+ """
+ if ('PCP_PYTHON_DOMAIN' in os.environ):
+ self.domain_write()
+ elif ('PCP_PYTHON_PMNS' in os.environ):
+ self.pmns_write(os.environ['PCP_PYTHON_PMNS'])
+ else:
+ self.pmns_refresh()
+ cpmda.pmid_oneline_refresh(self._metric_oneline)
+ cpmda.pmid_longtext_refresh(self._metric_helptext)
+ cpmda.indom_oneline_refresh(self._indom_oneline)
+ cpmda.indom_longtext_refresh(self._indom_helptext)
+ numindoms = len(self._indomtable)
+ ibuf = create_string_buffer(numindoms * sizeof(pmdaIndom))
+ indoms = cast(ibuf, POINTER(pmdaIndom))
+ for i in xrange(numindoms):
+ indoms[i] = self._indomtable[i]
+ nummetrics = len(self._metrictable)
+ mbuf = create_string_buffer(nummetrics * sizeof(pmdaMetric))
+ metrics = cast(mbuf, POINTER(pmdaMetric))
+ for i in xrange(nummetrics):
+ metrics[i] = self._metrictable[i]
+ cpmda.pmda_dispatch(ibuf.raw, numindoms, mbuf.raw, nummetrics)
+
+ @staticmethod
+ def set_fetch(fetch):
+ return cpmda.set_fetch(fetch)
+
+ @staticmethod
+ def set_refresh(refresh):
+ return cpmda.set_refresh(refresh)
+
+ @staticmethod
+ def set_instance(instance):
+ return cpmda.set_instance(instance)
+
+ @staticmethod
+ def set_fetch_callback(fetch_callback):
+ return cpmda.set_fetch_callback(fetch_callback)
+
+ @staticmethod
+ def set_store_callback(store_callback):
+ return cpmda.set_store_callback(store_callback)
+
+ @staticmethod
+ def set_user(username):
+ return cpmapi.pmSetProcessIdentity(username)
+
+ @staticmethod
+ def pmid(cluster, item):
+ return cpmda.pmda_pmid(cluster, item)
+
+ @staticmethod
+ def indom(serial):
+ return cpmda.pmda_indom(serial)
+
+ @staticmethod
+ def units(dim_space, dim_time, dim_count, scale_space, scale_time, scale_count):
+ return cpmda.pmda_units(dim_space, dim_time, dim_count, scale_space, scale_time, scale_count)
+
+ @staticmethod
+ def uptime(now):
+ return cpmda.pmda_uptime(now)
+
+ @staticmethod
+ def log(message):
+ return cpmda.pmda_log(message)
+
+ @staticmethod
+ def err(message):
+ return cpmda.pmda_err(message)
+
+# Other methods perl API provides:
+# add_timer()
+# add_pipe()
+# add_tail()
+# add_sock()
+# put_sock()
+# set_inet_socket
+# set_ipv6_socket
+# set_unix_socket
+# pmda_pmid_name(cluster,item)
+# pmda_pmid_text(cluster,item)
+#
diff --git a/src/python/pcp/pmgui.py b/src/python/pcp/pmgui.py
new file mode 100644
index 0000000..a9b35b0
--- /dev/null
+++ b/src/python/pcp/pmgui.py
@@ -0,0 +1,174 @@
+# pylint: disable=C0103
+""" Wrapper module for libpcp_gui - PCP Graphical User Interface clients """
+#
+# Copyright (C) 2012-2013 Red Hat Inc.
+# Copyright (C) 2009-2012 Michael T. Werner
+#
+# 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.
+#
+
+
+##############################################################################
+#
+# imports
+#
+
+# constants adapted from C header file <pcp/pmapi.h>
+from pmapi import pmErr
+from cpmapi import PM_ERR_IPC
+
+# for interfacing with libpcp - the client-side C API
+from ctypes import CDLL, Structure, POINTER, cast, byref
+from ctypes import c_void_p, c_char_p, c_int, c_long
+from ctypes.util import find_library
+
+
+##############################################################################
+#
+# dynamic library loads
+#
+
+LIBPCP_GUI = CDLL(find_library("pcp_gui"))
+LIBC = CDLL(find_library("c"))
+
+
+##############################################################################
+#
+# definition of structures used by C library libpcp, derived from <pcp/pmafm.h>
+#
+
+class pmRecordHost(Structure):
+ """state information between the recording session and the pmlogger """
+ _fields_ = [("f_config", c_void_p),
+ ("fd_ipc", c_int),
+ ("logfile", c_char_p),
+ ("pid", c_int),
+ ("status", c_int)]
+
+
+##############################################################################
+#
+# GUI API function prototypes
+#
+
+##
+# PMAPI Record-Mode Services
+
+LIBPCP_GUI.pmRecordSetup.restype = c_long
+LIBPCP_GUI.pmRecordSetup.argtypes = [c_char_p, c_char_p, c_int]
+
+LIBPCP_GUI.pmRecordAddHost.restype = c_int
+LIBPCP_GUI.pmRecordAddHost.argtypes = [
+ c_char_p, c_int, POINTER(POINTER(pmRecordHost))]
+
+LIBPCP_GUI.pmRecordControl.restype = c_int
+LIBPCP_GUI.pmRecordControl.argtypes = [POINTER(pmRecordHost), c_int, c_char_p]
+
+
+
+#LIBPCP_GUI.pmTimeConnect.restype = c_int
+#LIBPCP_GUI.pmTimeConnect.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeDisconnect.restype = c_int
+#LIBPCP_GUI.pmTimeDisconnect.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeGetPort.restype = c_int
+#LIBPCP_GUI.pmTimeGetPort.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeRecv.restype = c_int
+#LIBPCP_GUI.pmTimeRecv.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeSendAck.restype = c_int
+#LIBPCP_GUI.pmTimeSendAck.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeSendBounds.restype = c_int
+#LIBPCP_GUI.pmTimeSendBounds.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeSendMode.restype = c_int
+#LIBPCP_GUI.pmTimeSendMode.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeSendPosition.restype = c_int
+#LIBPCP_GUI.pmTimeSendPosition.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeSendTimezone.restype = c_int
+#LIBPCP_GUI.pmTimeSendTimezone.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeShowDialog.restype = c_int
+#LIBPCP_GUI.pmTimeShowDialog.argtypes = [ ]
+
+#LIBPCP_GUI.pmTimeGetStatePixmap.restype = c_int
+#LIBPCP_GUI.pmTimeGetStatePixmap.argtypes = [ ]
+
+
+
+##############################################################################
+#
+# class GuiClient
+#
+# This class wraps the GUI API library functions
+#
+
+class GuiClient(object):
+ """ Provides metric recording and time control interfaces
+ """
+
+ ##
+ # Record-Mode Services
+
+ @staticmethod
+ def pmRecordSetup(folio, creator, replay):
+ """ GUI API - Setup an archive recording session
+ File* file = pmRecordSetup("folio", "creator", 0)
+ """
+ file_result = LIBPCP_GUI.pmRecordSetup(
+ c_char_p(folio), c_char_p(creator), replay)
+ if (file_result == 0):
+ raise pmErr(file_result)
+ return file_result
+
+ @staticmethod
+ def pmRecordAddHost(host, isdefault, config):
+ """ GUI API - Adds host to an archive recording session
+ (status, recordhost) = pmRecordAddHost("host", 1, "configuration")
+ """
+ rhp = POINTER(pmRecordHost)()
+ status = LIBPCP_GUI.pmRecordAddHost(
+ c_char_p(host), isdefault, byref(rhp))
+ if status < 0:
+ raise pmErr(status)
+ status = LIBC.fputs(c_char_p(config), c_long(rhp.contents.f_config))
+ if (status < 0):
+ LIBC.perror(c_char_p(""))
+ raise pmErr(status)
+ return status, rhp
+
+ @staticmethod
+ def pmRecordControl(rhp, request, options):
+ """PMAPI - Control an archive recording session
+ status = pmRecordControl(0, cpmgui.PM_RCSETARG, "args")
+ status = pmRecordControl(0, cpmgui.PM_REC_ON)
+ status = pmRecordControl(0, cpmgui.PM_REC_OFF)
+ """
+ status = LIBPCP_GUI.pmRecordControl(
+ cast(rhp, POINTER(pmRecordHost)),
+ request, c_char_p(options))
+ if status < 0 and status != PM_ERR_IPC:
+ raise pmErr(status)
+ return status
+
+ ##
+ # GUI API Time Control Services
+ # (Not Yet Implemented)
+
+
diff --git a/src/python/pcp/pmi.py b/src/python/pcp/pmi.py
new file mode 100644
index 0000000..178bff9
--- /dev/null
+++ b/src/python/pcp/pmi.py
@@ -0,0 +1,312 @@
+# pylint: disable=C0103
+"""Wrapper module for libpcp_import - Performace Co-Pilot Log Import API
+#
+# Copyright (C) 2012-2013 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.
+#
+
+# Example use of this module for creating a PCP archive:
+
+ import math
+ import time
+ import pmapi
+ from pcp import pmi
+
+ # Create a new archive
+ log = pmi.pmiLogImport("loadtest")
+ log.pmiSetHostname("www.abc.com")
+ log.pmiSetTimezone("EST-10")
+
+ # Add a metric with an instance domain
+ domain = 60 # Linux kernel
+ pmid = log.pmiID(domain, 2, 0)
+ indom = log.pmiInDom(domain, 2)
+ units = log.pmiUnits(0, 0, 0, 0, 0, 0)
+ log.pmiAddMetric("kernel.all.load", pmid, pmapi.PM_TYPE_FLOAT,
+ indom, pmapi.PM_SEM_INSTANT, units)
+ log.pmiAddInstance(indom, "1 minute", 1)
+ log.pmiAddInstance(indom, "5 minute", 5)
+ log.pmiAddInstance(indom, "15 minute", 15)
+
+ # Create a record with a timestamp
+ log.pmiPutValue("kernel.all.load", "1 minute", "%f" % 0.01)
+ log.pmiPutValue("kernel.all.load", "5 minute", "%f" % 0.05)
+ log.pmiPutValue("kernel.all.load", "15 minute", "%f" % 0.15)
+ timetuple = math.modf(time.time())
+ useconds = int(timetuple[0] * 1000000)
+ seconds = int(timetuple[1])
+ log.pmiWrite(seconds, useconds)
+ del log
+"""
+
+from pcp.pmapi import pmID, pmInDom, pmUnits, pmResult
+from cpmi import pmiErrSymDict, PMI_MAXERRMSGLEN
+
+import ctypes
+from ctypes import cast, c_int, c_char_p, POINTER
+
+# Performance Co-Pilot PMI library (C)
+LIBPCP_IMPORT = ctypes.CDLL(ctypes.util.find_library("pcp_import"))
+
+##
+# PMI Log Import Services
+
+LIBPCP_IMPORT.pmiDump.restype = None
+LIBPCP_IMPORT.pmiDump.argtypes = None
+
+LIBPCP_IMPORT.pmiID.restype = pmID
+LIBPCP_IMPORT.pmiID.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
+
+LIBPCP_IMPORT.pmiInDom.restype = pmInDom
+LIBPCP_IMPORT.pmiInDom.argtypes = [ctypes.c_int, ctypes.c_int]
+
+LIBPCP_IMPORT.pmiUnits.restype = pmUnits
+LIBPCP_IMPORT.pmiUnits.argtypes = [
+ ctypes.c_int, ctypes.c_int, ctypes.c_int,
+ ctypes.c_int, ctypes.c_int, ctypes.c_int]
+
+LIBPCP_IMPORT.pmiErrStr_r.restype = c_char_p
+LIBPCP_IMPORT.pmiErrStr_r.argtypes = [c_int, c_char_p, c_int]
+
+LIBPCP_IMPORT.pmiStart.restype = c_int
+LIBPCP_IMPORT.pmiStart.argtypes = [c_char_p, c_int]
+
+LIBPCP_IMPORT.pmiUseContext.restype = c_int
+LIBPCP_IMPORT.pmiUseContext.argtypes = [c_int]
+
+LIBPCP_IMPORT.pmiEnd.restype = c_int
+LIBPCP_IMPORT.pmiEnd.argtypes = None
+
+LIBPCP_IMPORT.pmiSetHostname.restype = c_int
+LIBPCP_IMPORT.pmiSetHostname.argtypes = [c_char_p]
+
+LIBPCP_IMPORT.pmiSetTimezone.restype = c_int
+LIBPCP_IMPORT.pmiSetTimezone.argtypes = [c_char_p]
+
+LIBPCP_IMPORT.pmiAddMetric.restype = c_int
+LIBPCP_IMPORT.pmiAddMetric.argtypes = [
+ c_char_p, pmID, c_int, pmInDom, c_int, pmUnits]
+
+LIBPCP_IMPORT.pmiAddInstance.restype = c_int
+LIBPCP_IMPORT.pmiAddInstance.argtypes = [pmInDom, c_char_p, c_int]
+
+LIBPCP_IMPORT.pmiPutValue.restype = c_int
+LIBPCP_IMPORT.pmiPutValue.argtypes = [c_char_p, c_char_p, c_char_p]
+
+LIBPCP_IMPORT.pmiGetHandle.restype = c_int
+LIBPCP_IMPORT.pmiGetHandle.argtypes = [c_char_p, c_char_p]
+
+LIBPCP_IMPORT.pmiPutValueHandle.restype = c_int
+LIBPCP_IMPORT.pmiPutValueHandle.argtypes = [c_int, c_char_p]
+
+LIBPCP_IMPORT.pmiWrite.restype = c_int
+LIBPCP_IMPORT.pmiWrite.argtypes = [c_int, c_int]
+
+LIBPCP_IMPORT.pmiPutResult.restype = c_int
+LIBPCP_IMPORT.pmiPutResult.argtypes = [POINTER(pmResult)]
+
+#
+# definition of exception classes
+#
+
+class pmiErr(Exception):
+ '''
+ Encapsulation for PMI interface error code
+ '''
+ def __str__(self):
+ error_code = self.args[0]
+ try:
+ error_symbol = pmiErrSymDict[error_code]
+ error_string = ctypes.create_string_buffer(PMI_MAXERRMSGLEN)
+ error_string = LIBPCP_IMPORT.pmiErrStr_r(error_code,
+ error_string, PMI_MAXERRMSGLEN)
+ except KeyError:
+ error_symbol = error_string = ""
+ return "%s %s" % (error_symbol, error_string)
+
+
+#
+# class LogImport
+#
+# This class wraps the PMI (Log Import) library functions
+#
+
+class pmiLogImport(object):
+ """Defines a PCP Log Import archive context
+ This is used to create a PCP archive from an external source
+ """
+
+ ##
+ # property read methods
+
+ def read_path(self):
+ """ Property for archive path """
+ return self._path
+ def read_ctx(self):
+ """ Property for log import context """
+ return self._ctx
+
+ ##
+ # property definitions
+
+ path = property(read_path, None, None, None)
+ ctx = property(read_ctx, None, None, None)
+
+ ##
+ # overloads
+
+ def __init__(self, path, inherit = 0):
+ self._path = path # the archive path (file name)
+ self._ctx = LIBPCP_IMPORT.pmiStart(c_char_p(path), inherit)
+ if self._ctx < 0:
+ raise pmiErr(self._ctx)
+
+ def __del__(self):
+ if LIBPCP_IMPORT:
+ LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ LIBPCP_IMPORT.pmiEnd()
+ self._ctx = -1
+
+ ##
+ # PMI Log Import Services
+
+ def pmiSetHostname(self, hostname):
+ """PMI - set the source host name for a Log Import archive """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiSetHostname(c_char_p(hostname))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiSetTimezone(self, timezone):
+ """PMI - set the source timezone for a Log Import archive
+ """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiSetTimezone(c_char_p(timezone))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ @staticmethod
+ def pmiID(domain, cluster, item):
+ """PMI - construct a pmID data structure (helper routine) """
+ return LIBPCP_IMPORT.pmiID(domain, cluster, item)
+
+ @staticmethod
+ def pmiInDom(domain, serial):
+ """PMI - construct a pmInDom data structure (helper routine) """
+ return LIBPCP_IMPORT.pmiInDom(domain, serial)
+
+ @staticmethod
+ def pmiUnits(dim_space, dim_time, dim_count,
+ scale_space, scale_time, scale_count):
+ # pylint: disable=R0913
+ """PMI - construct a pmiUnits data structure (helper routine) """
+ return LIBPCP_IMPORT.pmiUnits(dim_space, dim_time, dim_count,
+ scale_space, scale_time, scale_count)
+
+ def pmiAddMetric(self, name, pmid, typed, indom, sem, units):
+ # pylint: disable=R0913
+ """PMI - add a new metric definition to a Log Import context """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiAddMetric(c_char_p(name),
+ pmid, typed, indom, sem, units)
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiAddInstance(self, indom, instance, instid):
+ """PMI - add element to an instance domain in a Log Import context """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiAddInstance(indom, c_char_p(instance), instid)
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiPutValue(self, name, inst, value):
+ """PMI - add a value for a metric-instance pair """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiPutValue(c_char_p(name),
+ c_char_p(inst), c_char_p(value))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiGetHandle(self, name, inst):
+ """PMI - define a handle for a metric-instance pair """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiGetHandle(c_char_p(name), c_char_p(inst))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiPutValueHandle(self, handle, value):
+ """PMI - add a value for a metric-instance pair via a handle """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiPutValueHandle(handle, c_char_p(value))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def pmiWrite(self, sec, usec):
+ """PMI - flush data to a Log Import archive """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiWrite(sec, usec)
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ def put_result(self, result):
+ """PMI - add a data record to a Log Import archive """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiPutResult(cast(result, POINTER(pmResult)))
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
+ @staticmethod
+ def pmiDump():
+ """PMI - dump the current Log Import contexts (diagnostic) """
+ LIBPCP_IMPORT.pmiDump()
+
+ def pmiEnd(self):
+ """PMI - close current context and finish a Log Import archive """
+ status = LIBPCP_IMPORT.pmiUseContext(self._ctx)
+ if status < 0:
+ raise pmiErr(status)
+ status = LIBPCP_IMPORT.pmiEnd()
+ self._ctx = -1
+ if status < 0:
+ raise pmiErr(status)
+ return status
+
diff --git a/src/python/pcp/pmsubsys.py b/src/python/pcp/pmsubsys.py
new file mode 100644
index 0000000..0bab017
--- /dev/null
+++ b/src/python/pcp/pmsubsys.py
@@ -0,0 +1,355 @@
+#
+# Performance Co-Pilot subsystem classes
+#
+# Copyright (C) 2013 Red Hat Inc.
+#
+# 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.
+#
+
+"""Advanced System & Process Monitor using the libpcp Wrapper module
+
+Additional Information:
+
+Performance Co-Pilot Web Site
+http://www.performancecopilot.org
+"""
+
+# ignore line too long, missing docstring, method could be a function,
+# too many public methods
+# pylint: disable=C0301
+# pylint: disable=C0111
+# pylint: disable=R0201
+# pylint: disable=R0904
+
+import copy
+import cpmapi as c_api
+from pcp.pmapi import pmErr
+from ctypes import c_char_p
+
+
+# Subsystem ---------------------------------------------------------------
+
+
+class Subsystem(object):
+ def __init__(self):
+ self.metrics = []
+ self.diff_metrics = []
+ self.metric_pmids = []
+ self.metric_descs = []
+ self.metric_values = []
+ self.metrics_dict = {}
+ self.old_metric_values = []
+
+ def setup_metrics(self, pcp):
+ # remove any unsupported metrics
+ name_pattern = self.metrics[0].split(".")[0] + ".*"
+ for j in range(len(self.metrics)-1, -1, -1):
+ try:
+ self.metric_pmids = pcp.pmLookupName(self.metrics[j])
+ except pmErr as e:
+ self.metrics.remove(self.metrics[j])
+
+ if (len(self.metrics) == 0):
+ raise pmErr(c_api.PM_ERR_NAME, "", name_pattern)
+ self.metrics_dict = dict((i, self.metrics.index(i)) for i in self.metrics)
+ self.metric_pmids = pcp.pmLookupName(self.metrics)
+ self.metric_descs = pcp.pmLookupDescs(self.metric_pmids)
+ self.metric_values = [0 for i in range(len(self.metrics))]
+ self.old_metric_values = [0 for i in range(len(self.metrics))]
+
+ def dump_metrics(self):
+ metrics_string = ""
+ for i in xrange(len(self.metrics)):
+ metrics_string += self.metrics[i]
+ metrics_string += " "
+ return metrics_string
+
+ def get_scalar_value(self, var, idx):
+ if type(var) == type(str()):
+ value = self.get_metric_value(var)
+ else:
+ value = self.metric_values[var]
+ if type(value) != type(int()) and type(value) != type(long()):
+ return value[idx]
+ else:
+ return value
+
+ def get_metric_value(self, idx):
+ if idx in self.metrics:
+ return self.metric_values[self.metrics_dict[idx]]
+ else:
+ return 0
+
+ def get_old_scalar_value(self, var, idx):
+ aidx = 0
+ if var in self.metrics:
+ aidx = self.metrics_dict[var]
+ aval = self.old_metric_values[aidx]
+ else:
+ return 0
+ val = self.get_atom_value(aval[idx], None, self.metric_descs[aidx], False)
+ if type(val) == type(int()) or type(val) == type(long()):
+ return val
+ else:
+ return val[idx]
+
+ def get_len(self, var):
+ if type(var) != type(int()) and type(var) != type(long()):
+ return len(var)
+ else:
+ return 1
+
+ def get_atom_value(self, atom1, atom2, desc, want_diff): # pylint: disable-msg=R0913
+ # value conversion and diff, if required
+ atom_type = desc.type
+ if atom2 == None:
+ want_diff = False
+ if atom_type == c_api.PM_TYPE_32:
+ if want_diff:
+ return atom1.l - atom2.l
+ else:
+ return atom1.l
+ elif atom_type == c_api.PM_TYPE_U32:
+ if want_diff:
+ return atom1.ul - atom2.ul
+ else:
+ return atom1.ul
+ elif atom_type == c_api.PM_TYPE_64:
+ if want_diff:
+ return atom1.ll - atom2.ll
+ else:
+ return atom1.ll
+ elif atom_type == c_api.PM_TYPE_U64:
+ if want_diff:
+ return atom1.ull - atom2.ull
+ else:
+ return atom1.ull
+ elif atom_type == c_api.PM_TYPE_FLOAT:
+ if want_diff:
+ return atom1.f - atom2.f
+ else:
+ return atom1.f
+ elif atom_type == c_api.PM_TYPE_DOUBLE:
+ if want_diff:
+ return atom1.d - atom2.d
+ else:
+ return atom1.d
+ elif atom_type == c_api.PM_TYPE_STRING:
+ atom_str = c_char_p(atom1.cp)
+ return str(atom_str.value)
+ else:
+ return 0
+
+ def get_stats(self, pcp):
+ if len(self.metrics) <= 0:
+ raise pmErr
+
+ list_type = type([])
+
+ metric_result = pcp.pmFetch(self.metric_pmids)
+
+ if max(self.old_metric_values) == 0:
+ first = True
+ else:
+ first = False
+
+ # list of metric names
+ for i in xrange(len(self.metrics)):
+ # list of metric results, one per metric name
+ for j in xrange(metric_result.contents.numpmid):
+ if (metric_result.contents.get_pmid(j) != self.metric_pmids[i]):
+ continue
+ atomlist = []
+ # list of instances, one or more per metric. e.g. there are many
+ # instances for network metrics, one per network interface
+ for k in xrange(metric_result.contents.get_numval(j)):
+ atom = pcp.pmExtractValue(metric_result.contents.get_valfmt(j), metric_result.contents.get_vlist(j, k), self.metric_descs[j].type, self.metric_descs[j].type)
+ atomlist.append(atom)
+
+ value = []
+ # metric may require a diff to get a per interval value
+ for k in xrange(metric_result.contents.get_numval(j)):
+ if type(self.old_metric_values[j]) == list_type:
+ try:
+ old_val = self.old_metric_values[j][k]
+ except IndexError:
+ old_val = None
+ else:
+ old_val = self.old_metric_values[j]
+ if first:
+ want_diff = False
+ elif self.metrics[j] in self.diff_metrics:
+ want_diff = True
+ elif (self.metric_descs[j].sem == c_api.PM_SEM_DISCRETE
+ or self.metric_descs[j].sem == c_api.PM_SEM_INSTANT) :
+ want_diff = False
+ else:
+ want_diff = True
+ value.append(self.get_atom_value(atomlist[k], old_val, self.metric_descs[j], want_diff))
+
+ self.old_metric_values[j] = copy.copy(atomlist)
+ if metric_result.contents.get_numval(j) == 1:
+ if len(value) == 1:
+ self.metric_values[j] = copy.copy(value[0])
+ else:
+ self.metric_values[j] = 0
+ elif metric_result.contents.get_numval(j) > 1:
+ self.metric_values[j] = copy.copy(value)
+
+
+# Processor --------------------------------------------------------------
+
+ def init_processor_metrics(self):
+ self.cpu_total = 0
+ self.metrics += ['hinv.ncpu', 'hinv.cpu.clock',
+ 'kernel.all.cpu.idle', 'kernel.all.cpu.intr',
+ 'kernel.all.cpu.irq.hard', 'kernel.all.cpu.irq.soft',
+ 'kernel.all.cpu.nice', 'kernel.all.cpu.steal',
+ 'kernel.all.cpu.sys', 'kernel.all.cpu.user',
+ 'kernel.all.cpu.wait.total', 'kernel.all.intr',
+ 'kernel.all.load', 'kernel.all.pswitch',
+ 'kernel.all.uptime', 'kernel.percpu.cpu.nice',
+ 'kernel.percpu.cpu.user', 'kernel.percpu.cpu.intr',
+ 'kernel.percpu.cpu.sys', 'kernel.percpu.cpu.steal',
+ 'kernel.percpu.cpu.irq.hard',
+ 'kernel.percpu.cpu.irq.soft',
+ 'kernel.percpu.cpu.wait.total',
+ 'kernel.percpu.cpu.idle', 'kernel.all.nprocs',
+ 'kernel.all.runnable',
+ # multiple inheritance?
+ 'proc.runq.blocked', 'proc.runq.defunct',
+ 'proc.runq.runnable', 'proc.runq.sleeping']
+ self.diff_metrics = ['kernel.all.uptime']
+
+
+ def get_total(self):
+ self.cpu_total = (self.get_metric_value('kernel.all.cpu.nice') +
+ self.get_metric_value('kernel.all.cpu.user') +
+ self.get_metric_value('kernel.all.cpu.intr') +
+ self.get_metric_value('kernel.all.cpu.sys') +
+ self.get_metric_value('kernel.all.cpu.idle') +
+ self.get_metric_value('kernel.all.cpu.steal') +
+ self.get_metric_value('kernel.all.cpu.irq.hard') +
+ self.get_metric_value('kernel.all.cpu.irq.soft') )
+
+# Disk -----------------------------------------------------------------
+
+ def init_disk_metrics(self):
+ self.metrics += ['disk.all.read', 'disk.all.write',
+ 'disk.all.read_bytes', 'disk.all.write_bytes',
+ 'disk.all.read_merge', 'disk.all.write_merge',
+ 'disk.dev.avactive',
+ 'disk.dev.blkread', 'disk.dev.blkwrite',
+ 'disk.dev.read', 'disk.dev.read_bytes',
+ 'disk.dev.read_merge', 'disk.dev.total',
+ 'disk.dev.write','disk.dev.write_bytes',
+ 'disk.dev.write_merge',
+ 'disk.partitions.blkread', 'disk.partitions.blkwrite',
+ 'disk.partitions.read', 'disk.partitions.write',
+ 'hinv.map.lvname'
+ ]
+
+# Memory -----------------------------------------------------------------
+
+ def init_memory_metrics(self):
+ self.metrics += ['mem.freemem', 'mem.physmem', 'mem.util.anonpages',
+ 'mem.util.bufmem',
+ 'mem.util.cached', 'mem.util.commitLimit',
+ 'mem.util.committed_AS',
+ 'mem.util.inactive',
+ 'mem.util.inactive', 'mem.util.mapped',
+ 'mem.util.mlocked',
+ 'mem.util.shmem', 'mem.util.slab',
+ 'mem.util.swapFree',
+ 'mem.util.swapTotal', 'mem.util.used',
+ 'mem.vmstat.allocstall', 'mem.vmstat.pgfault',
+ 'mem.vmstat.pginodesteal',
+ 'mem.vmstat.pgmajfault', 'mem.vmstat.pgpgin',
+ 'mem.vmstat.pgpgout', 'mem.vmstat.pswpin',
+ 'mem.vmstat.pswpout', 'mem.vmstat.slabs_scanned',
+ 'swap.free', 'swap.pagesin',
+ 'swap.pagesout', 'swap.used' ]
+
+
+# Network -----------------------------------------------------------------
+
+ def init_network_metrics(self):
+ self.metrics += ['network.interface.in.bytes',
+ 'network.interface.in.packets',
+ 'network.interface.out.bytes',
+ 'network.interface.out.packets',
+ 'network.interface.in.mcasts',
+ 'network.interface.total.mcasts',
+ 'network.interface.in.compressed',
+ 'network.interface.out.compressed',
+ 'network.interface.in.errors',
+ 'network.interface.out.errors',
+ 'network.icmp.inmsgs',
+ 'network.icmp.outmsgs',
+ 'network.ip.forwdatagrams', 'network.ip.indelivers',
+ 'network.ip.inreceives', 'network.ip.outrequests',
+ 'network.tcp.activeopens',
+ 'network.tcp.insegs',
+ 'network.tcp.outsegs',
+ 'network.tcp.passiveopens',
+ 'network.udp.indatagrams',
+ 'network.udp.outdatagrams' ]
+
+# Process -----------------------------------------------------------------
+
+ def init_process_metrics(self):
+ self.metrics += ['proc.id.uid', 'proc.id.uid_nm',
+ 'proc.memory.datrss', 'proc.memory.librss',
+ 'proc.memory.textrss', 'proc.memory.vmstack',
+ 'proc.nprocs', 'proc.psinfo.cmd',
+ 'proc.psinfo.maj_flt', 'proc.psinfo.minflt',
+ 'proc.psinfo.pid',
+ 'proc.psinfo.rss', 'proc.psinfo.sname',
+ 'proc.psinfo.stime', 'proc.psinfo.threads',
+ 'proc.psinfo.utime', 'proc.psinfo.vsize',
+ 'proc.runq.runnable', 'proc.runq.sleeping',
+ 'proc.runq.blocked', 'proc.runq.defunct',
+ ]
+ self.diff_metrics += ['proc.psinfo.rss', 'proc.psinfo.vsize']
+
+# Interrupt --------------------------------------------------------------
+
+ def init_interrupt_metrics(self):
+ self.metrics += ['kernel.percpu.interrupts.MCP',
+ 'kernel.percpu.interrupts.MCE',
+ 'kernel.percpu.interrupts.THR',
+ 'kernel.percpu.interrupts.TRM',
+ 'kernel.percpu.interrupts.TLB',
+ 'kernel.percpu.interrupts.CAL',
+ 'kernel.percpu.interrupts.RES',
+ 'kernel.percpu.interrupts.RTR',
+ 'kernel.percpu.interrupts.IWI',
+ 'kernel.percpu.interrupts.PMI',
+ 'kernel.percpu.interrupts.SPU',
+ 'kernel.percpu.interrupts.LOC',
+ 'kernel.percpu.interrupts.line46',
+ 'kernel.percpu.interrupts.line45',
+ 'kernel.percpu.interrupts.line44',
+ 'kernel.percpu.interrupts.line43',
+ 'kernel.percpu.interrupts.line42',
+ 'kernel.percpu.interrupts.line41',
+ 'kernel.percpu.interrupts.line40',
+ 'kernel.percpu.interrupts.line23',
+ 'kernel.percpu.interrupts.line19',
+ 'kernel.percpu.interrupts.line18',
+ 'kernel.percpu.interrupts.line16',
+ 'kernel.percpu.interrupts.line12',
+ 'kernel.percpu.interrupts.line9',
+ 'kernel.percpu.interrupts.line8',
+ 'kernel.percpu.interrupts.line1',
+ 'kernel.percpu.interrupts.line0',
+ ]
+
diff --git a/src/python/pmapi.c b/src/python/pmapi.c
new file mode 100644
index 0000000..6886214
--- /dev/null
+++ b/src/python/pmapi.c
@@ -0,0 +1,1364 @@
+/*
+ * Copyright (C) 2012-2014 Red Hat.
+ * Copyright (C) 2009-2012 Michael T. Werner
+ *
+ * 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 constants **
+** from PCP headers into the module dictionary. The PMAPI functions and **
+** data structures are wrapped in pmapi.py and friends, using ctypes. **
+** **
+\**************************************************************************/
+
+#include <Python.h>
+#include <pcp/pmapi.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 pmOptions options;
+static int longOptionsCount;
+static PyObject *optionCallback;
+static PyObject *overridesCallback;
+
+static void
+dict_add_unsigned(PyObject *dict, char *symbol, unsigned long value)
+{
+ PyObject *pyvalue = PyLong_FromUnsignedLong(value);
+ PyDict_SetItemString(dict, symbol, pyvalue);
+ Py_XDECREF(pyvalue);
+}
+
+static void
+dict_add(PyObject *dict, char *symbol, long value)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyvalue = PyLong_FromLong(value);
+#else
+ PyObject *pyvalue = PyInt_FromLong(value);
+#endif
+ PyDict_SetItemString(dict, symbol, pyvalue);
+ Py_XDECREF(pyvalue);
+}
+
+static void
+edict_add(PyObject *dict, PyObject *edict, char *symbol, long value)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyvalue = PyLong_FromLong(value);
+ PyObject *pysymbol = PyUnicode_FromString(symbol);
+#else
+ PyObject *pyvalue = PyInt_FromLong(value);
+ PyObject *pysymbol = PyString_FromString(symbol);
+#endif
+
+ PyDict_SetItemString(dict, symbol, pyvalue);
+ PyDict_SetItem(edict, pyvalue, pysymbol);
+ Py_XDECREF(pysymbol);
+ Py_XDECREF(pyvalue);
+}
+
+static PyObject *
+setExtendedTimeBase(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int type;
+ char *keyword_list[] = {"type", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:PM_XTB_SET", keyword_list, &type))
+ return NULL;
+ return Py_BuildValue("i", PM_XTB_SET(type));
+}
+
+static PyObject *
+getExtendedTimeBase(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int mode;
+ char *keyword_list[] = {"mode", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:PM_XTB_GET", keyword_list, &mode))
+ return NULL;
+ return Py_BuildValue("i", PM_XTB_GET(mode));
+}
+
+static PyObject *
+timevalSleep(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ struct timeval ctv;
+ long seconds, useconds;
+ char *keyword_list[] = {"seconds", "useconds", NULL};
+ extern void __pmtimevalSleep(struct timeval);
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "ll:pmtimevalSleep", keyword_list, &seconds, &useconds))
+ return NULL;
+ ctv.tv_sec = seconds;
+ ctv.tv_usec = useconds;
+ __pmtimevalSleep(ctv);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+timevalToReal(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ struct timeval ctv;
+ long seconds, useconds;
+ char *keyword_list[] = {"seconds", "useconds", NULL};
+ extern double __pmtimevalToReal(const struct timeval *);
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "ll:pmtimevalToReal", keyword_list, &seconds, &useconds))
+ return NULL;
+ ctv.tv_sec = seconds;
+ ctv.tv_usec = useconds;
+ return Py_BuildValue("d", __pmtimevalToReal(&ctv));
+}
+
+static PyObject *
+setIdentity(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *name;
+ char *keyword_list[] = {"name", NULL};
+ extern int __pmSetProcessIdentity(const char *);
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetProcessIdentity", keyword_list, &name))
+ return NULL;
+ return Py_BuildValue("i", __pmSetProcessIdentity(name));
+}
+
+/*
+ * Common command line option handling code - wrapping pmOptions
+ */
+
+static int
+addLongOption(pmLongOptions *opt, int duplicate)
+{
+ size_t bytes;
+ pmLongOptions *lp;
+ int index = longOptionsCount;
+
+ if (!opt->long_opt)
+ return -EINVAL;
+
+ bytes = (index + 2) * sizeof(pmLongOptions); /* +2 for PMAPI_OPTIONS_END */
+ if ((lp = realloc(options.long_options, bytes)) == NULL)
+ return -ENOMEM;
+ options.long_options = lp;
+
+ if (!duplicate)
+ goto update;
+
+ if ((opt->long_opt = strdup(opt->long_opt)) == NULL)
+ return -ENOMEM;
+ if (opt->argname &&
+ (opt->argname = strdup(opt->argname)) == NULL) {
+ free((char *)opt->long_opt);
+ return -ENOMEM;
+ }
+ if (opt->message &&
+ (opt->message = strdup(opt->message)) == NULL) {
+ free((char *)opt->long_opt);
+ free((char *)opt->argname);
+ return -ENOMEM;
+ }
+
+update:
+ lp[index].long_opt = opt->long_opt;
+ lp[index].has_arg = opt->has_arg;
+ lp[index].short_opt = opt->short_opt;
+ lp[index].argname = opt->argname;
+ lp[index].message = opt->message;
+ memset(&lp[index+1], 0, sizeof(pmLongOptions)); /* PMAPI_OPTIONS_END */
+ longOptionsCount++;
+ return 0;
+}
+
+static PyObject *
+setLongOptionHeader(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ pmLongOptions header = PMAPI_OPTIONS_HEADER("");
+ char *keyword_list[] = {"heading", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetLongOptionHeader", keyword_list,
+ &header.message))
+ return NULL;
+ if ((header.message = strdup(header.message)) == NULL)
+ return PyErr_NoMemory();
+
+ if (addLongOption(&header, 0) < 0) {
+ free((char *)header.message);
+ return PyErr_NoMemory();
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setLongOptionText(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ pmLongOptions text = PMAPI_OPTIONS_TEXT("");
+ char *keyword_list[] = {"text", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetLongOptionText", keyword_list,
+ &text.message))
+ return NULL;
+ if ((text.message = strdup(text.message)) == NULL)
+ return PyErr_NoMemory();
+
+ if (addLongOption(&text, 0) < 0) {
+ free((char *)text.message);
+ return PyErr_NoMemory();
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+addLongOptionObject(pmLongOptions *option)
+{
+ if (addLongOption(option, 1) < 0)
+ return PyErr_NoMemory();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setLongOption(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *short_opt = NULL;
+ pmLongOptions option = { 0 };
+ char *keyword_list[] = {"long_opt", "has_arg", "short_opt",
+ "argname", "message", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "sisss:pmSetLongOption", keyword_list,
+ &option.long_opt, &option.has_arg, &short_opt,
+ &option.argname, &option.message))
+ return NULL;
+ if (short_opt)
+ option.short_opt = (int)short_opt[0];
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionAlign(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_ALIGN;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionArchive(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_ARCHIVE;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionHostList(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_HOST_LIST;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionArchiveList(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_ARCHIVE_LIST;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionArchiveFolio(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_ARCHIVE_FOLIO;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionDebug(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_DEBUG;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionGuiMode(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_GUIMODE;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionHost(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_HOST;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionHostsFile(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_HOSTSFILE;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionSpecLocal(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_SPECLOCAL;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionLocalPMDA(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_LOCALPMDA;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionOrigin(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_ORIGIN;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionGuiPort(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_GUIPORT;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionStart(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_START;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionSamples(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_SAMPLES;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionFinish(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_FINISH;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionInterval(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_INTERVAL;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionVersion(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_VERSION;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionTimeZone(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_TIMEZONE;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionHostZone(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_HOSTZONE;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+setLongOptionHelp(PyObject *self, PyObject *args)
+{
+ pmLongOptions option = PMOPT_HELP;
+ return addLongOptionObject(&option);
+}
+
+static PyObject *
+resetAllOptions(PyObject *self, PyObject *args)
+{
+ pmFreeOptions(&options);
+ memset(&options, 0, sizeof(options));
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setShortOptions(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *short_opts;
+ char *keyword_list[] = {"short_options", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetShortOptions", keyword_list, &short_opts))
+ return NULL;
+
+ if ((short_opts = strdup(short_opts ? short_opts : "")) == NULL)
+ return PyErr_NoMemory();
+ if (options.short_options)
+ free((void *)options.short_options);
+ options.short_options = short_opts;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setShortUsage(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *short_usage;
+ char *keyword_list[] = {"short_usage", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetShortUsage", keyword_list, &short_usage))
+ return NULL;
+
+ if ((short_usage = strdup(short_usage ? short_usage : "")) == NULL)
+ return PyErr_NoMemory();
+ if (options.short_usage)
+ free((void *)options.short_usage);
+ options.short_usage = short_usage;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionFlags(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int flags;
+ char *keyword_list[] = {"flags", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:pmSetOptionFlags", keyword_list, &flags))
+ return NULL;
+
+ options.flags |= flags;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionArchiveFolio(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *folio;
+ char *keyword_list[] = {PMLONGOPT_ARCHIVE_FOLIO, NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetOptionArchiveFolio", keyword_list, &folio))
+ return NULL;
+
+ __pmAddOptArchiveFolio(&options, folio);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionArchiveList(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *archives;
+ char *keyword_list[] = {PMLONGOPT_ARCHIVE_LIST, NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetOptionArchiveList", keyword_list, &archives))
+ return NULL;
+
+ __pmAddOptArchiveList(&options, archives);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionHostList(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *hosts;
+ char *keyword_list[] = {PMLONGOPT_HOST_LIST, NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetOptionHostList", keyword_list, &hosts))
+ return NULL;
+
+ __pmAddOptHostList(&options, hosts);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionSamples(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *count, *endnum;
+ char *keyword_list[] = {"count", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetOptionSamples", keyword_list, &count))
+ return NULL;
+
+ if (options.finish_optarg) {
+ pmprintf("%s: at most one of finish time and sample count allowed\n",
+ pmProgname);
+ options.errors++;
+ } else {
+ options.samples = (int)strtol(count, &endnum, 10);
+ if (*endnum != '\0' || options.samples < 0) {
+ pmprintf("%s: sample count must be a positive numeric argument\n",
+ pmProgname);
+ options.errors++;
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionInterval(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ char *delta, *errmsg;
+ char *keyword_list[] = {"delta", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "s:pmSetOptionInterval", keyword_list, &delta))
+ return NULL;
+
+ if (pmParseInterval(delta, &options.interval, &errmsg) < 0) {
+ pmprintf("%s: interval argument not in pmParseInterval(3) format:\n",
+ pmProgname);
+ pmprintf("%s\n", errmsg);
+ options.errors++;
+ free(errmsg);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionErrors(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int errors;
+ char *keyword_list[] = {"errors", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "i:pmSetOptionErrors", keyword_list, &errors))
+ return NULL;
+
+ options.errors = errors;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int
+override_callback(int opt, pmOptions *opts)
+{
+ PyObject *arglist, *result;
+ char argstring[2] = { (char)opt, '\0' };
+ int sts;
+
+ arglist = Py_BuildValue("(s)", argstring);
+ if (!arglist) {
+ PyErr_Print();
+ return -ENOMEM;
+ }
+ result = PyEval_CallObject(overridesCallback, arglist);
+ Py_DECREF(arglist);
+ if (!result) {
+ PyErr_Print();
+ return -EAGAIN; /* exception thrown */
+ }
+ sts = PyLong_AsLong(result);
+ Py_DECREF(result);
+ return sts;
+}
+
+static void
+options_callback(int opt, pmOptions *opts)
+{
+ PyObject *arglist, *result;
+ char argstring[2] = { (char)opt, '\0' };
+
+ arglist = Py_BuildValue("(ssi)", argstring, options.optarg, options.index);
+ if (!arglist) {
+ PyErr_Print();
+ } else {
+ result = PyEval_CallObject(optionCallback, arglist);
+ Py_DECREF(arglist);
+ if (!result) {
+ PyErr_Print();
+ return;
+ }
+ Py_DECREF(result);
+ }
+}
+
+static PyObject *
+getNonOptionsFromList(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int argc, length, i;
+ PyObject *result;
+ PyObject *pyargv = NULL;
+ char *keyword_list[] = {"argv", NULL};
+
+ /* Caller must perform pmGetOptions before running this, check */
+ if (!(options.flags & PM_OPTFLAG_DONE))
+ return NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:pmGetNonOptionsFromList", keyword_list, &pyargv))
+ if (pyargv == NULL)
+ return NULL;
+
+ if (!PyList_Check(pyargv)) {
+ PyErr_SetString(PyExc_TypeError, "pmGetNonOptionsFromList uses a list");
+ return NULL;
+ }
+
+ length = 0;
+ if ((argc = PyList_GET_SIZE(pyargv)) > 0)
+ length = argc - options.optind;
+
+ if (length <= 0) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ if ((result = PyList_New(length)) == NULL)
+ return PyErr_NoMemory();
+
+ for (i = 0; i < length; i++) {
+ PyObject *pyarg = PyList_GET_ITEM(pyargv, options.optind + i);
+ PyList_SET_ITEM(result, i, pyarg);
+ }
+ Py_INCREF(result);
+ return result;
+}
+
+static PyObject *
+getOptionsFromList(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int i, argc;
+ char **argv;
+ PyObject *pyargv = NULL;
+ char *keyword_list[] = {"argv", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "O:pmGetOptionsFromList", keyword_list, &pyargv))
+ return NULL;
+
+ if (pyargv == NULL)
+ return Py_BuildValue("i", 0);
+
+ if (!PyList_Check(pyargv)) {
+ PyErr_SetString(PyExc_TypeError, "pmGetOptionsFromList uses a list");
+ Py_DECREF(pyargv);
+ return NULL;
+ }
+
+ if ((argc = PyList_GET_SIZE(pyargv)) <= 0) {
+ Py_DECREF(pyargv);
+ return Py_BuildValue("i", 0);
+ }
+
+ if ((argv = malloc(argc * sizeof(char *))) == NULL) {
+ Py_DECREF(pyargv);
+ return PyErr_NoMemory();
+ }
+
+ for (i = 0; i < argc; i++) {
+ PyObject *pyarg = PyList_GET_ITEM(pyargv, i);
+#if PY_MAJOR_VERSION >= 3
+ char *string = PyUnicode_AsUTF8(pyarg);
+#else
+ char *string = PyString_AsString(pyarg);
+#endif
+
+ /* argv[0] parameter will be used for pmProgname, so need to
+ * ensure the memory that backs it will be with us forever.
+ */
+ if (i == 0 && (string = strdup(string)) == NULL) {
+ free(argv);
+ Py_DECREF(pyargv);
+ return PyErr_NoMemory();
+ }
+ argv[i] = string;
+ }
+
+ if (overridesCallback)
+ options.override = override_callback;
+ while ((i = pmGetOptions(argc, argv, &options)) != -1)
+ options_callback(i, &options);
+ free(argv);
+
+ if (options.flags & PM_OPTFLAG_EXIT)
+ return Py_BuildValue("i", PM_ERR_APPVERSION);
+
+ return Py_BuildValue("i", options.errors);
+}
+
+static PyObject *
+setContextOptions(PyObject *self, PyObject *args, PyObject *keywords)
+{
+ int sts, ctx, step, mode, delta;
+ char *keyword_list[] = {"context", "mode", "delta", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywords,
+ "iii:pmSetContextOptions", keyword_list, &ctx, &mode, &delta))
+ return NULL;
+
+ /* complete time window and timezone setup */
+ if ((sts = pmGetContextOptions(ctx, &options)) < 0)
+ return Py_BuildValue("i", sts);
+
+ /* initial archive mode, position and delta */
+ if (options.context == PM_CONTEXT_ARCHIVE &&
+ (options.flags & PM_OPTFLAG_BOUNDARIES)) {
+ const int SECONDS_IN_24_DAYS = 2073600;
+ struct timeval interval = options.interval;
+ struct timeval position = options.origin;
+
+ if (interval.tv_sec > SECONDS_IN_24_DAYS) {
+ step = interval.tv_sec;
+ mode |= PM_XTB_SET(PM_TIME_SEC);
+ } else {
+ if (interval.tv_sec == 0 && interval.tv_usec == 0)
+ interval.tv_sec = delta;
+ step = interval.tv_sec * 1e3 + interval.tv_usec / 1e3;
+ mode |= PM_XTB_SET(PM_TIME_MSEC);
+ }
+ if ((sts = pmSetMode(mode, &position, step)) < 0) {
+ pmprintf("%s: pmSetMode: %s\n", pmProgname, pmErrStr(sts));
+ options.flags |= PM_OPTFLAG_RUNTIME_ERR;
+ options.errors++;
+ }
+ }
+ return Py_BuildValue("i", sts);
+}
+
+static PyObject *
+usageMessage(PyObject *self, PyObject *args)
+{
+ pmUsageMessage(&options);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOverrideCallback(PyObject *self, PyObject *args)
+{
+ PyObject *func;
+
+ if (!PyArg_ParseTuple(args, "O:pmSetOverrideCallback", &func))
+ return NULL;
+ if (!PyCallable_Check(func)) {
+ PyErr_SetString(PyExc_TypeError,
+ "pmSetOverrideCallback parameter not callable");
+ return NULL;
+ }
+ Py_XINCREF(func);
+ Py_XDECREF(overridesCallback);
+ overridesCallback = func;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+setOptionCallback(PyObject *self, PyObject *args)
+{
+ PyObject *func;
+
+ if (!PyArg_ParseTuple(args, "O:pmSetOptionCallback", &func))
+ return NULL;
+ if (!PyCallable_Check(func)) {
+ PyErr_SetString(PyExc_TypeError,
+ "pmSetOptionCallback parameter not callable");
+ return NULL;
+ }
+ Py_XINCREF(func);
+ Py_XDECREF(optionCallback);
+ optionCallback = func;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionErrors(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", options.errors);
+}
+
+static PyObject *
+getOptionFlags(PyObject *self, PyObject *args)
+{
+ return Py_BuildValue("i", options.flags);
+}
+
+static PyObject *
+getOptionContext(PyObject *self, PyObject *args)
+{
+ if (options.context > 0)
+ return Py_BuildValue("i", options.context);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionHosts(PyObject *self, PyObject *args)
+{
+ PyObject *result;
+ int i;
+
+ if (options.nhosts > 0) {
+ if ((result = PyList_New(options.nhosts)) == NULL)
+ return PyErr_NoMemory();
+ for (i = 0; i < options.nhosts; i++) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyent = PyUnicode_FromString(options.hosts[i]);
+#else
+ PyObject *pyent = PyString_FromString(options.hosts[i]);
+#endif
+ PyList_SET_ITEM(result, i, pyent);
+ }
+ Py_INCREF(result);
+ return result;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionArchives(PyObject *self, PyObject *args)
+{
+ PyObject *result;
+ int i;
+
+ if (options.narchives > 0) {
+ if ((result = PyList_New(options.narchives)) == NULL)
+ return PyErr_NoMemory();
+ for (i = 0; i < options.narchives; i++) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyent = PyUnicode_FromString(options.archives[i]);
+#else
+ PyObject *pyent = PyString_FromString(options.archives[i]);
+#endif
+ PyList_SET_ITEM(result, i, pyent);
+ }
+ Py_INCREF(result);
+ return result;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionStart_sec(PyObject *self, PyObject *args)
+{
+ if (options.start.tv_sec || options.start.tv_usec)
+ return Py_BuildValue("l", options.start.tv_sec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionStart_usec(PyObject *self, PyObject *args)
+{
+ if (options.start.tv_sec || options.start.tv_usec)
+ return Py_BuildValue("l", options.start.tv_usec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionFinish_optarg(PyObject *self, PyObject *args)
+{
+ if (options.finish_optarg)
+ return Py_BuildValue("s", options.finish_optarg);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionFinish_sec(PyObject *self, PyObject *args)
+{
+ if (options.finish.tv_sec || options.finish.tv_usec)
+ return Py_BuildValue("l", options.finish.tv_sec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionFinish_usec(PyObject *self, PyObject *args)
+{
+ if (options.finish.tv_sec || options.finish.tv_usec)
+ return Py_BuildValue("l", options.finish.tv_usec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionOrigin_sec(PyObject *self, PyObject *args)
+{
+ if (options.origin.tv_sec || options.origin.tv_usec)
+ return Py_BuildValue("l", options.origin.tv_sec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionOrigin_usec(PyObject *self, PyObject *args)
+{
+ if (options.origin.tv_sec || options.origin.tv_usec)
+ return Py_BuildValue("l", options.origin.tv_usec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionInterval_sec(PyObject *self, PyObject *args)
+{
+ if (options.interval.tv_sec || options.interval.tv_usec)
+ return Py_BuildValue("l", options.interval.tv_sec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionInterval_usec(PyObject *self, PyObject *args)
+{
+ if (options.interval.tv_sec || options.interval.tv_usec)
+ return Py_BuildValue("l", options.interval.tv_usec);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionSamples(PyObject *self, PyObject *args)
+{
+ if (options.samples)
+ return Py_BuildValue("i", options.samples);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+getOptionTimezone(PyObject *self, PyObject *args)
+{
+ if (options.timezone)
+ return Py_BuildValue("s", options.timezone);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyMethodDef methods[] = {
+ { .ml_name = "PM_XTB_SET",
+ .ml_meth = (PyCFunction) setExtendedTimeBase,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "PM_XTB_GET",
+ .ml_meth = (PyCFunction) getExtendedTimeBase,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmtimevalSleep",
+ .ml_meth = (PyCFunction) timevalSleep,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmtimevalToReal",
+ .ml_meth = (PyCFunction) timevalToReal,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetProcessIdentity",
+ .ml_meth = (PyCFunction) setIdentity,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmResetAllOptions",
+ .ml_meth = (PyCFunction) resetAllOptions,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHeader",
+ .ml_meth = (PyCFunction) setLongOptionHeader,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetLongOptionText",
+ .ml_meth = (PyCFunction) setLongOptionText,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetLongOption",
+ .ml_meth = (PyCFunction) setLongOption,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetLongOptionAlign",
+ .ml_meth = (PyCFunction) setLongOptionAlign,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionArchive",
+ .ml_meth = (PyCFunction) setLongOptionArchive,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionArchiveList",
+ .ml_meth = (PyCFunction) setLongOptionArchiveList,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionArchiveFolio",
+ .ml_meth = (PyCFunction) setLongOptionArchiveFolio,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionDebug",
+ .ml_meth = (PyCFunction) setLongOptionDebug,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionGuiMode",
+ .ml_meth = (PyCFunction) setLongOptionGuiMode,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHost",
+ .ml_meth = (PyCFunction) setLongOptionHost,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHostList",
+ .ml_meth = (PyCFunction) setLongOptionHostList,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHostsFile",
+ .ml_meth = (PyCFunction) setLongOptionHostsFile,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionSpecLocal",
+ .ml_meth = (PyCFunction) setLongOptionSpecLocal,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionLocalPMDA",
+ .ml_meth = (PyCFunction) setLongOptionLocalPMDA,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionOrigin",
+ .ml_meth = (PyCFunction) setLongOptionOrigin,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionGuiPort",
+ .ml_meth = (PyCFunction) setLongOptionGuiPort,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionStart",
+ .ml_meth = (PyCFunction) setLongOptionStart,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionSamples",
+ .ml_meth = (PyCFunction) setLongOptionSamples,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionFinish",
+ .ml_meth = (PyCFunction) setLongOptionFinish,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionInterval",
+ .ml_meth = (PyCFunction) setLongOptionInterval,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionVersion",
+ .ml_meth = (PyCFunction) setLongOptionVersion,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionTimeZone",
+ .ml_meth = (PyCFunction) setLongOptionTimeZone,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHostZone",
+ .ml_meth = (PyCFunction) setLongOptionHostZone,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetLongOptionHelp",
+ .ml_meth = (PyCFunction) setLongOptionHelp,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetShortOptions",
+ .ml_meth = (PyCFunction) setShortOptions,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetShortUsage",
+ .ml_meth = (PyCFunction) setShortUsage,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetOptionFlags",
+ .ml_meth = (PyCFunction) setOptionFlags,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetOptionErrors",
+ .ml_meth = (PyCFunction) setOptionErrors,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmGetOptionErrors",
+ .ml_meth = (PyCFunction) getOptionErrors,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionFlags",
+ .ml_meth = (PyCFunction) getOptionFlags,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionsFromList",
+ .ml_meth = (PyCFunction) getOptionsFromList,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmGetNonOptionsFromList",
+ .ml_meth = (PyCFunction) getNonOptionsFromList,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetContextOptions",
+ .ml_meth = (PyCFunction) setContextOptions,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS},
+ { .ml_name = "pmUsageMessage",
+ .ml_meth = (PyCFunction) usageMessage,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetOptionCallback",
+ .ml_meth = (PyCFunction) setOptionCallback,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetOverrideCallback",
+ .ml_meth = (PyCFunction) setOverrideCallback,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmGetOptionContext",
+ .ml_meth = (PyCFunction) getOptionContext,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionHosts",
+ .ml_meth = (PyCFunction) getOptionHosts,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionArchives",
+ .ml_meth = (PyCFunction) getOptionArchives,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionStart_sec",
+ .ml_meth = (PyCFunction) getOptionStart_sec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionStart_usec",
+ .ml_meth = (PyCFunction) getOptionStart_usec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionFinish_optarg",
+ .ml_meth = (PyCFunction) getOptionFinish_optarg,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionFinish_sec",
+ .ml_meth = (PyCFunction) getOptionFinish_sec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionFinish_usec",
+ .ml_meth = (PyCFunction) getOptionFinish_usec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionOrigin_sec",
+ .ml_meth = (PyCFunction) getOptionOrigin_sec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionOrigin_usec",
+ .ml_meth = (PyCFunction) getOptionOrigin_usec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionInterval_sec",
+ .ml_meth = (PyCFunction) getOptionInterval_sec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmGetOptionInterval_usec",
+ .ml_meth = (PyCFunction) getOptionInterval_usec,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetOptionInterval",
+ .ml_meth = (PyCFunction) setOptionInterval,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmGetOptionSamples",
+ .ml_meth = (PyCFunction) getOptionSamples,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetOptionSamples",
+ .ml_meth = (PyCFunction) setOptionSamples,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmGetOptionTimezone",
+ .ml_meth = (PyCFunction) getOptionTimezone,
+ .ml_flags = METH_NOARGS },
+ { .ml_name = "pmSetOptionArchiveList",
+ .ml_meth = (PyCFunction) setOptionArchiveList,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetOptionArchiveFolio",
+ .ml_meth = (PyCFunction) setOptionArchiveFolio,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { .ml_name = "pmSetOptionHostList",
+ .ml_meth = (PyCFunction) setOptionHostList,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS },
+ { NULL }
+};
+
+/* called when the module is initialized. */
+MOD_INIT(cpmapi)
+{
+ PyObject *module, *dict, *edict;
+
+ MOD_DEF(module, "cpmapi", NULL, methods);
+ if (module == NULL)
+ return MOD_ERROR_VAL;
+
+ dict = PyModule_GetDict(module);
+ edict = PyDict_New();
+ Py_INCREF(edict);
+ PyModule_AddObject(module, "pmErrSymDict", edict);
+
+ dict_add(dict, "PMAPI_VERSION_2", PMAPI_VERSION_2);
+ dict_add(dict, "PMAPI_VERSION", PMAPI_VERSION);
+
+ dict_add_unsigned(dict, "PM_ID_NULL", PM_ID_NULL);
+ dict_add_unsigned(dict, "PM_INDOM_NULL", PM_INDOM_NULL);
+ dict_add_unsigned(dict, "PM_IN_NULL", PM_IN_NULL);
+
+ dict_add_unsigned(dict, "PM_NS_DEFAULT", 0);
+
+#ifdef HAVE_BITFIELDS_LTOR
+ dict_add(dict, "HAVE_BITFIELDS_LTOR", 1);
+ dict_add(dict, "HAVE_BITFIELDS_RTOL", 0);
+#else
+ dict_add(dict, "HAVE_BITFIELDS_LTOR", 0);
+ dict_add(dict, "HAVE_BITFIELDS_RTOL", 1);
+#endif
+
+ dict_add(dict, "PM_SPACE_BYTE", PM_SPACE_BYTE);
+ dict_add(dict, "PM_SPACE_KBYTE", PM_SPACE_KBYTE);
+ dict_add(dict, "PM_SPACE_MBYTE", PM_SPACE_MBYTE);
+ dict_add(dict, "PM_SPACE_GBYTE", PM_SPACE_GBYTE);
+ dict_add(dict, "PM_SPACE_TBYTE", PM_SPACE_TBYTE);
+ dict_add(dict, "PM_SPACE_PBYTE", PM_SPACE_PBYTE);
+ dict_add(dict, "PM_SPACE_EBYTE", PM_SPACE_EBYTE);
+
+ dict_add(dict, "PM_TIME_NSEC", PM_TIME_NSEC);
+ dict_add(dict, "PM_TIME_USEC", PM_TIME_USEC);
+ dict_add(dict, "PM_TIME_MSEC", PM_TIME_MSEC);
+ dict_add(dict, "PM_TIME_SEC", PM_TIME_SEC);
+ dict_add(dict, "PM_TIME_MIN", PM_TIME_MIN);
+ dict_add(dict, "PM_TIME_HOUR", PM_TIME_HOUR);
+ dict_add(dict, "PM_COUNT_ONE", PM_COUNT_ONE);
+
+ dict_add(dict, "PM_TYPE_NOSUPPORT", PM_TYPE_NOSUPPORT);
+ dict_add(dict, "PM_TYPE_32", PM_TYPE_32);
+ dict_add(dict, "PM_TYPE_U32", PM_TYPE_U32);
+ dict_add(dict, "PM_TYPE_64", PM_TYPE_64);
+ dict_add(dict, "PM_TYPE_U64", PM_TYPE_U64);
+ dict_add(dict, "PM_TYPE_FLOAT", PM_TYPE_FLOAT);
+ dict_add(dict, "PM_TYPE_DOUBLE", PM_TYPE_DOUBLE);
+ dict_add(dict, "PM_TYPE_STRING", PM_TYPE_STRING);
+ dict_add(dict, "PM_TYPE_AGGREGATE", PM_TYPE_AGGREGATE);
+ dict_add(dict, "PM_TYPE_AGGREGATE_STATIC", PM_TYPE_AGGREGATE_STATIC);
+ dict_add(dict, "PM_TYPE_EVENT", PM_TYPE_EVENT);
+ dict_add(dict, "PM_TYPE_HIGHRES_EVENT", PM_TYPE_HIGHRES_EVENT);
+ dict_add(dict, "PM_TYPE_UNKNOWN", PM_TYPE_UNKNOWN);
+
+ dict_add(dict, "PM_SEM_COUNTER", PM_SEM_COUNTER);
+ dict_add(dict, "PM_SEM_INSTANT", PM_SEM_INSTANT);
+ dict_add(dict, "PM_SEM_DISCRETE", PM_SEM_DISCRETE);
+
+ dict_add(dict, "PMNS_LOCAL", PMNS_LOCAL);
+ dict_add(dict, "PMNS_REMOTE", PMNS_REMOTE);
+ dict_add(dict, "PMNS_ARCHIVE", PMNS_ARCHIVE);
+ dict_add(dict, "PMNS_LEAF_STATUS", PMNS_LEAF_STATUS);
+ dict_add(dict, "PMNS_NONLEAF_STATUS", PMNS_NONLEAF_STATUS);
+
+ dict_add(dict, "PM_CONTEXT_UNDEF", PM_CONTEXT_UNDEF);
+ dict_add(dict, "PM_CONTEXT_HOST", PM_CONTEXT_HOST);
+ dict_add(dict, "PM_CONTEXT_ARCHIVE", PM_CONTEXT_ARCHIVE);
+ dict_add(dict, "PM_CONTEXT_LOCAL", PM_CONTEXT_LOCAL);
+ dict_add(dict, "PM_CONTEXT_TYPEMASK", PM_CONTEXT_TYPEMASK);
+ dict_add(dict, "PM_CTXFLAG_SECURE", PM_CTXFLAG_SECURE);
+ dict_add(dict, "PM_CTXFLAG_COMPRESS", PM_CTXFLAG_COMPRESS);
+ dict_add(dict, "PM_CTXFLAG_RELAXED", PM_CTXFLAG_RELAXED);
+
+ dict_add(dict, "PM_VAL_HDR_SIZE", PM_VAL_HDR_SIZE);
+ dict_add(dict, "PM_VAL_VLEN_MAX", PM_VAL_VLEN_MAX);
+ dict_add(dict, "PM_VAL_INSITU", PM_VAL_INSITU);
+ dict_add(dict, "PM_VAL_DPTR", PM_VAL_DPTR);
+ dict_add(dict, "PM_VAL_SPTR", PM_VAL_SPTR);
+
+ dict_add(dict, "PMCD_NO_CHANGE", PMCD_NO_CHANGE);
+ dict_add(dict, "PMCD_ADD_AGENT", PMCD_ADD_AGENT);
+ dict_add(dict, "PMCD_RESTART_AGENT", PMCD_RESTART_AGENT);
+ dict_add(dict, "PMCD_DROP_AGENT", PMCD_DROP_AGENT);
+
+ dict_add(dict, "PM_MAXERRMSGLEN", PM_MAXERRMSGLEN);
+ dict_add(dict, "PM_TZ_MAXLEN", PM_TZ_MAXLEN);
+
+ dict_add(dict, "PM_LOG_MAXHOSTLEN", PM_LOG_MAXHOSTLEN);
+ dict_add(dict, "PM_LOG_MAGIC", PM_LOG_MAGIC);
+ dict_add(dict, "PM_LOG_VERS02", PM_LOG_VERS02);
+ dict_add(dict, "PM_LOG_VOL_TI", PM_LOG_VOL_TI);
+ dict_add(dict, "PM_LOG_VOL_META", PM_LOG_VOL_META);
+
+ dict_add(dict, "PM_MODE_LIVE", PM_MODE_LIVE);
+ dict_add(dict, "PM_MODE_INTERP", PM_MODE_INTERP);
+ dict_add(dict, "PM_MODE_FORW", PM_MODE_FORW);
+ dict_add(dict, "PM_MODE_BACK", PM_MODE_BACK);
+
+ dict_add(dict, "PM_TEXT_ONELINE", PM_TEXT_ONELINE);
+ dict_add(dict, "PM_TEXT_HELP", PM_TEXT_HELP);
+
+ dict_add(dict, "PM_XTB_FLAG", PM_XTB_FLAG);
+
+ dict_add(dict, "PM_OPTFLAG_INIT", PM_OPTFLAG_INIT);
+ dict_add(dict, "PM_OPTFLAG_DONE", PM_OPTFLAG_DONE);
+ dict_add(dict, "PM_OPTFLAG_MULTI", PM_OPTFLAG_MULTI);
+ dict_add(dict, "PM_OPTFLAG_USAGE_ERR", PM_OPTFLAG_USAGE_ERR);
+ dict_add(dict, "PM_OPTFLAG_RUNTIME_ERR", PM_OPTFLAG_RUNTIME_ERR);
+ dict_add(dict, "PM_OPTFLAG_EXIT", PM_OPTFLAG_EXIT);
+ dict_add(dict, "PM_OPTFLAG_POSIX", PM_OPTFLAG_POSIX);
+ dict_add(dict, "PM_OPTFLAG_MIXED", PM_OPTFLAG_MIXED);
+ dict_add(dict, "PM_OPTFLAG_ENV_ONLY", PM_OPTFLAG_ENV_ONLY);
+ dict_add(dict, "PM_OPTFLAG_LONG_ONLY", PM_OPTFLAG_LONG_ONLY);
+ dict_add(dict, "PM_OPTFLAG_BOUNDARIES", PM_OPTFLAG_BOUNDARIES);
+ dict_add(dict, "PM_OPTFLAG_STDOUT_TZ", PM_OPTFLAG_STDOUT_TZ);
+ dict_add(dict, "PM_OPTFLAG_NOFLUSH", PM_OPTFLAG_NOFLUSH);
+ dict_add(dict, "PM_OPTFLAG_QUIET", PM_OPTFLAG_QUIET);
+
+ dict_add(dict, "PM_EVENT_FLAG_POINT", PM_EVENT_FLAG_POINT);
+ dict_add(dict, "PM_EVENT_FLAG_START", PM_EVENT_FLAG_START);
+ dict_add(dict, "PM_EVENT_FLAG_END", PM_EVENT_FLAG_END);
+ dict_add(dict, "PM_EVENT_FLAG_ID", PM_EVENT_FLAG_ID);
+ dict_add(dict, "PM_EVENT_FLAG_PARENT", PM_EVENT_FLAG_PARENT);
+ dict_add(dict, "PM_EVENT_FLAG_MISSED", PM_EVENT_FLAG_MISSED);
+
+ edict_add(dict, edict, "PM_ERR_GENERIC", PM_ERR_GENERIC);
+ edict_add(dict, edict, "PM_ERR_PMNS", PM_ERR_PMNS);
+ edict_add(dict, edict, "PM_ERR_NOPMNS", PM_ERR_NOPMNS);
+ edict_add(dict, edict, "PM_ERR_DUPPMNS", PM_ERR_DUPPMNS);
+ edict_add(dict, edict, "PM_ERR_TEXT", PM_ERR_TEXT);
+ edict_add(dict, edict, "PM_ERR_APPVERSION", PM_ERR_APPVERSION);
+ edict_add(dict, edict, "PM_ERR_VALUE", PM_ERR_VALUE);
+ edict_add(dict, edict, "PM_ERR_TIMEOUT", PM_ERR_TIMEOUT);
+ edict_add(dict, edict, "PM_ERR_NODATA", PM_ERR_NODATA);
+ edict_add(dict, edict, "PM_ERR_RESET", PM_ERR_RESET);
+ edict_add(dict, edict, "PM_ERR_NAME", PM_ERR_NAME);
+ edict_add(dict, edict, "PM_ERR_PMID", PM_ERR_PMID);
+ edict_add(dict, edict, "PM_ERR_INDOM", PM_ERR_INDOM);
+ edict_add(dict, edict, "PM_ERR_INST", PM_ERR_INST);
+ edict_add(dict, edict, "PM_ERR_UNIT", PM_ERR_UNIT);
+ edict_add(dict, edict, "PM_ERR_CONV", PM_ERR_CONV);
+ edict_add(dict, edict, "PM_ERR_TRUNC", PM_ERR_TRUNC);
+ edict_add(dict, edict, "PM_ERR_SIGN", PM_ERR_SIGN);
+ edict_add(dict, edict, "PM_ERR_PROFILE", PM_ERR_PROFILE);
+ edict_add(dict, edict, "PM_ERR_IPC", PM_ERR_IPC);
+ edict_add(dict, edict, "PM_ERR_EOF", PM_ERR_EOF);
+ edict_add(dict, edict, "PM_ERR_NOTHOST", PM_ERR_NOTHOST);
+ edict_add(dict, edict, "PM_ERR_EOL", PM_ERR_EOL);
+ edict_add(dict, edict, "PM_ERR_MODE", PM_ERR_MODE);
+ edict_add(dict, edict, "PM_ERR_LABEL", PM_ERR_LABEL);
+ edict_add(dict, edict, "PM_ERR_LOGREC", PM_ERR_LOGREC);
+ edict_add(dict, edict, "PM_ERR_NOTARCHIVE", PM_ERR_NOTARCHIVE);
+ edict_add(dict, edict, "PM_ERR_LOGFILE", PM_ERR_LOGFILE);
+ edict_add(dict, edict, "PM_ERR_NOCONTEXT", PM_ERR_NOCONTEXT);
+ edict_add(dict, edict, "PM_ERR_PROFILESPEC", PM_ERR_PROFILESPEC);
+ edict_add(dict, edict, "PM_ERR_PMID_LOG", PM_ERR_PMID_LOG);
+ edict_add(dict, edict, "PM_ERR_INDOM_LOG", PM_ERR_INDOM_LOG);
+ edict_add(dict, edict, "PM_ERR_INST_LOG", PM_ERR_INST_LOG);
+ edict_add(dict, edict, "PM_ERR_NOPROFILE", PM_ERR_NOPROFILE);
+ edict_add(dict, edict, "PM_ERR_NOAGENT", PM_ERR_NOAGENT);
+ edict_add(dict, edict, "PM_ERR_PERMISSION", PM_ERR_PERMISSION);
+ edict_add(dict, edict, "PM_ERR_CONNLIMIT", PM_ERR_CONNLIMIT);
+ edict_add(dict, edict, "PM_ERR_AGAIN", PM_ERR_AGAIN);
+ edict_add(dict, edict, "PM_ERR_ISCONN", PM_ERR_ISCONN);
+ edict_add(dict, edict, "PM_ERR_NOTCONN", PM_ERR_NOTCONN);
+ edict_add(dict, edict, "PM_ERR_NEEDPORT", PM_ERR_NEEDPORT);
+ edict_add(dict, edict, "PM_ERR_NONLEAF", PM_ERR_NONLEAF);
+ edict_add(dict, edict, "PM_ERR_TYPE", PM_ERR_TYPE);
+ edict_add(dict, edict, "PM_ERR_THREAD", PM_ERR_THREAD);
+ edict_add(dict, edict, "PM_ERR_TOOSMALL", PM_ERR_TOOSMALL);
+ edict_add(dict, edict, "PM_ERR_TOOBIG", PM_ERR_TOOBIG);
+ edict_add(dict, edict, "PM_ERR_FAULT", PM_ERR_FAULT);
+ edict_add(dict, edict, "PM_ERR_PMDAREADY", PM_ERR_PMDAREADY);
+ edict_add(dict, edict, "PM_ERR_PMDANOTREADY", PM_ERR_PMDANOTREADY);
+ edict_add(dict, edict, "PM_ERR_NYI", PM_ERR_NYI);
+
+ return MOD_SUCCESS_VAL(module);
+}
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);
+}
diff --git a/src/python/pmgui.c b/src/python/pmgui.c
new file mode 100644
index 0000000..60256c4
--- /dev/null
+++ b/src/python/pmgui.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012-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 constants **
+** from PCP headers into the module dictionary. The GUI API functions **
+** and data structures are wrapped in pmgui.py and friends, using ctypes. **
+** **
+\**************************************************************************/
+
+#include <Python.h>
+#include <pcp/pmafm.h>
+#include <pcp/pmtime.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 void
+pmgui_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);
+}
+
+static PyMethodDef methods[] = { { NULL } };
+
+/* called when the module is initialized. */
+MOD_INIT(cpmgui)
+{
+ PyObject *module, *dict;
+
+ MOD_DEF(module, "cpmgui", NULL, methods);
+ if (module == NULL)
+ return MOD_ERROR_VAL;
+
+ dict = PyModule_GetDict(module);
+
+ /* pmafm.h */
+ pmgui_dict_add(dict, "PM_REC_ON", PM_REC_ON);
+ pmgui_dict_add(dict, "PM_REC_OFF", PM_REC_OFF);
+ pmgui_dict_add(dict, "PM_REC_DETACH", PM_REC_DETACH);
+ pmgui_dict_add(dict, "PM_REC_STATUS", PM_REC_STATUS);
+ pmgui_dict_add(dict, "PM_REC_SETARG", PM_REC_SETARG);
+
+ /* TODO: pmtime.h */
+
+ return MOD_SUCCESS_VAL(module);
+}
diff --git a/src/python/pmi.c b/src/python/pmi.c
new file mode 100644
index 0000000..b253eff
--- /dev/null
+++ b/src/python/pmi.c
@@ -0,0 +1,115 @@
+/*
+ * 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 constants **
+** from the PCP log import header into the module dictionary. PMI data **
+** structures and interfaces are wrapped in pmi.py, using ctypes. **
+** **
+\**************************************************************************/
+
+#include <Python.h>
+#include <pcp/pmapi.h>
+#include <pcp/import.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 PyMethodDef methods[] = { { NULL } };
+
+static void
+pmi_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);
+}
+
+static void
+pmi_edict_add(PyObject *dict, PyObject *edict, char *sym, long val)
+{
+ PyObject *pySym;
+#if PY_MAJOR_VERSION >= 3
+ PyObject *pyVal = PyLong_FromLong(val);
+#else
+ PyObject *pyVal = PyInt_FromLong(val);
+#endif
+
+ PyDict_SetItemString(dict, sym, pyVal);
+#if PY_MAJOR_VERSION >= 3
+ pySym = PyUnicode_FromString(sym);
+#else
+ pySym = PyString_FromString(sym);
+#endif
+ PyDict_SetItem(edict, pyVal, pySym);
+ Py_XDECREF(pySym);
+ Py_XDECREF(pyVal);
+}
+
+/* This function is called when the module is initialized. */
+MOD_INIT(cpmi)
+{
+ PyObject *module, *dict, *edict;
+
+ MOD_DEF(module, "cpmi", NULL, methods);
+ if (module == NULL)
+ return MOD_ERROR_VAL;
+
+ dict = PyModule_GetDict(module);
+ edict = PyDict_New();
+ Py_INCREF(edict);
+ PyModule_AddObject(module, "pmiErrSymDict", edict);
+
+ /* import.h */
+ pmi_dict_add(dict, "PMI_MAXERRMSGLEN", PMI_MAXERRMSGLEN);
+ pmi_dict_add(dict, "PMI_ERR_BASE", PMI_ERR_BASE);
+
+ pmi_edict_add(dict, edict, "PMI_ERR_DUPMETRICNAME", PMI_ERR_DUPMETRICNAME);
+ pmi_edict_add(dict, edict, "PMI_ERR_DUPMETRICID", PMI_ERR_DUPMETRICID);
+ pmi_edict_add(dict, edict, "PMI_ERR_DUPINSTNAME", PMI_ERR_DUPINSTNAME);
+ pmi_edict_add(dict, edict, "PMI_ERR_DUPINSTID", PMI_ERR_DUPINSTID);
+ pmi_edict_add(dict, edict, "PMI_ERR_INSTNOTNULL", PMI_ERR_INSTNOTNULL);
+ pmi_edict_add(dict, edict, "PMI_ERR_INSTNULL", PMI_ERR_INSTNULL);
+ pmi_edict_add(dict, edict, "PMI_ERR_BADHANDLE", PMI_ERR_BADHANDLE);
+ pmi_edict_add(dict, edict, "PMI_ERR_DUPVALUE", PMI_ERR_DUPVALUE);
+ pmi_edict_add(dict, edict, "PMI_ERR_BADTYPE", PMI_ERR_BADTYPE);
+ pmi_edict_add(dict, edict, "PMI_ERR_BADSEM", PMI_ERR_BADSEM);
+ pmi_edict_add(dict, edict, "PMI_ERR_NODATA", PMI_ERR_NODATA);
+ pmi_edict_add(dict, edict, "PMI_ERR_BADMETRICNAME", PMI_ERR_BADMETRICNAME);
+ pmi_edict_add(dict, edict, "PMI_ERR_BADTIMESTAMP", PMI_ERR_BADTIMESTAMP);
+
+ return MOD_SUCCESS_VAL(module);
+}
diff --git a/src/python/setup.py b/src/python/setup.py
new file mode 100644
index 0000000..c235b08
--- /dev/null
+++ b/src/python/setup.py
@@ -0,0 +1,61 @@
+""" Build script for the PCP python package """
+#
+# Copyright (C) 2012-2014 Red Hat.
+# Copyright (C) 2009-2012 Michael T. Werner
+#
+# 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.
+#
+
+from distutils.core import setup, Extension
+
+setup(name = 'pcp',
+ version = '1.0',
+ description = 'Python package for Performance Co-Pilot',
+ license = 'GPLv2+',
+ author = 'Performance Co-Pilot Development Team',
+ author_email = 'pcp@oss.sgi.com',
+ url = 'http://www.performancecopilot.org',
+ packages = ['pcp'],
+ ext_modules = [
+ Extension('cpmapi', ['pmapi.c'], libraries = ['pcp']),
+ Extension('cpmda', ['pmda.c'], libraries = ['pcp_pmda', 'pcp']),
+ Extension('cpmgui', ['pmgui.c'], libraries = ['pcp_gui']),
+ Extension('cpmi', ['pmi.c'], libraries = ['pcp_import']),
+ Extension('cmmv', ['mmv.c'], libraries = ['pcp_mmv']),
+ ],
+ platforms = [ 'Windows', 'Linux', 'FreeBSD', 'Solaris', 'Mac OS X', 'AIX' ],
+ long_description =
+ 'PCP provides services to support system-level performance monitoring',
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable'
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'Intended Audience :: Information Technology',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Natural Language :: English',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+ 'Operating System :: POSIX :: AIX',
+ 'Operating System :: POSIX :: Linux',
+ 'Operating System :: POSIX :: BSD :: NetBSD',
+ 'Operating System :: POSIX :: BSD :: FreeBSD',
+ 'Operating System :: POSIX :: SunOS/Solaris',
+ 'Operating System :: Unix',
+ 'Topic :: System :: Logging',
+ 'Topic :: System :: Monitoring',
+ 'Topic :: System :: Networking :: Monitoring',
+ 'Topic :: Software Development :: Libraries',
+ ],
+)