diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/python | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/python')
-rw-r--r-- | src/python/GNUmakefile | 47 | ||||
-rw-r--r-- | src/python/mmv.c | 92 | ||||
-rw-r--r-- | src/python/pcp/GNUmakefile | 25 | ||||
-rw-r--r-- | src/python/pcp/__init__.py | 1 | ||||
-rw-r--r-- | src/python/pcp/mmv.py | 332 | ||||
-rw-r--r-- | src/python/pcp/pmapi.py | 1866 | ||||
-rw-r--r-- | src/python/pcp/pmcc.py | 620 | ||||
-rw-r--r-- | src/python/pcp/pmda.py | 407 | ||||
-rw-r--r-- | src/python/pcp/pmgui.py | 174 | ||||
-rw-r--r-- | src/python/pcp/pmi.py | 312 | ||||
-rw-r--r-- | src/python/pcp/pmsubsys.py | 355 | ||||
-rw-r--r-- | src/python/pmapi.c | 1364 | ||||
-rw-r--r-- | src/python/pmda.c | 1098 | ||||
-rw-r--r-- | src/python/pmgui.c | 81 | ||||
-rw-r--r-- | src/python/pmi.c | 115 | ||||
-rw-r--r-- | src/python/setup.py | 61 |
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', + ], +) |