diff options
Diffstat (limited to 'src/python/pcp/pmapi.py')
-rw-r--r-- | src/python/pcp/pmapi.py | 1866 |
1 files changed, 1866 insertions, 0 deletions
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() + |