summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFrank Ch. Eigler <fche@redhat.com>2013-06-14 23:38:56 -0400
committerJan Safranek <jsafrane@redhat.com>2013-07-18 13:39:48 +0200
commit30d91d80793392adb4c5b3890555662caa0031da (patch)
treeafc3980d82b12eb646c3a6e5d05a7a55203e0d04 /src
parentf3cee6b1ca62752134c6c3520c3739a193753768 (diff)
downloadopenlmi-providers-30d91d80793392adb4c5b3890555662caa0031da.tar.gz
openlmi-providers-30d91d80793392adb4c5b3890555662caa0031da.tar.xz
openlmi-providers-30d91d80793392adb4c5b3890555662caa0031da.zip
PCP<->CIM bridge prototype v3
- contents properly built/packaged into openlmi-pcp subrpm - a cron.daily job conditionally (rarely) rebuilds the MOF/REG files based upon current PCP state - /usr/bin/openlmi-pcp-generate able to be run by hand, cron.daily job minimal - more run-time PCP error tolerance
Diffstat (limited to 'src')
-rw-r--r--src/pcp/README52
-rw-r--r--src/pcp/lmi/pcp/__init__.py25
-rw-r--r--src/pcp/lmi/pcp/metric.py167
-rw-r--r--src/pcp/openlmi-pcp-generate68
-rw-r--r--src/pcp/openlmi-pcp.cron3
-rw-r--r--src/pcp/setup.py19
6 files changed, 334 insertions, 0 deletions
diff --git a/src/pcp/README b/src/pcp/README
new file mode 100644
index 0000000..1f5e225
--- /dev/null
+++ b/src/pcp/README
@@ -0,0 +1,52 @@
+CIM <-> PCP (http://oss.sgi.com/projects/pcp) bridge
+<fche@redhat.com>
+
+The idea of the bridge is to create a suite of CIM classes (derived from
+CIM_StatisticsData), each of which represents one PCP metric. There are
+hundreds of these. (Since PCP metrics can come and go, the MOF and REG
+files may be regenerated by a simple shell script at any with an enclosed
+shell script.)
+
+Install PCP and start the pmcd service to start. We assume you're
+already running OpenLMI / Pegasus with a client like YAWN and are far
+more familiar with this WBEM business than the author. Install the
+CIM <-> PCP bridge thusly:
+
+ vi PCP_pmns2mofreg.sh # to fix the path to pcp-metric.py
+ sh PCP_pmns2mofreg.sh mof > PCP_Metric_PMNS.mof
+ sh PCP_pmns2mofreg.sh reg > PCP_Metric_PMNS.reg
+ openlmi-mof-register register *.mof *.reg
+
+For example, whereas PCP command line tools may show these sorts of results:
+
+ % pminfo -dTf hinv.machine
+
+ hinv.machine
+ Data Type: string InDom: PM_INDOM_NULL 0xffffffff
+ Semantics: discrete Units: none
+ Help:
+ machine name, IP35 if SGI SNIA, else simply linux
+ value "linux"
+
+ % pminfo -dTf network.interface.total.bytes
+ network.interface.total.bytes
+ Data Type: 64-bit unsigned int InDom: 60.3 0xf000003
+ Semantics: counter Units: byte
+ Help:
+ network total (in+out) bytes from /proc/net/dev per network interface
+ inst [0 or "eth0"] value 585236365
+ inst [1 or "lo"] value 85894766
+
+These same metrics would show up in the CIM namespace as classes
+
+ PCP_Metric_hinv__machine
+and PCP_Metric_network__interface__total__bytes
+
+with instances with InstanceIDs such as
+
+ PCP:hinv.machine
+ PCP:network.interface.total.bytes:lo
+ PCP:network.interface.total.bytes:eth0
+
+These currently supply a string-formatted value of the respective
+metric measurement, an accurate StatisticTime, and other metadata.
diff --git a/src/pcp/lmi/pcp/__init__.py b/src/pcp/lmi/pcp/__init__.py
new file mode 100644
index 0000000..81de2e5
--- /dev/null
+++ b/src/pcp/lmi/pcp/__init__.py
@@ -0,0 +1,25 @@
+# PCP bridge Providers
+#
+# Copyright (C) 2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Frank Ch. Eigler <fche@redhat.com>
+#
+
+"""
+CIM providers for PCP metrics.
+Part of OpenLMI project.
+"""
diff --git a/src/pcp/lmi/pcp/metric.py b/src/pcp/lmi/pcp/metric.py
new file mode 100644
index 0000000..454f54a
--- /dev/null
+++ b/src/pcp/lmi/pcp/metric.py
@@ -0,0 +1,167 @@
+"""Python Provider for PCP_Metric_*
+Instruments the CIM class family PCP_Metric_*
+"""
+
+from pywbem.cim_provider2 import CIMProvider2
+import pywbem
+from pcp import pmapi
+import cpmapi as c_api
+import datetime
+
+context = None # persistent pcp context
+
+
+# Since we serve a whole slew of PCP_Metric_** subclasses, we can't
+# use a straight classname->provider-class dictionary.
+#
+#def get_providers(env):
+# return {'PCP_Metric_****': PCP_MetricProvider}
+#
+# Instead, we implement the one-hop-higher proxy-level function calls,
+# namely the IM_* functions at the bottom.
+
+
+# Undo mangling done by PCP_pmns2mofreg.sh
+def MOFname_to_PCPmetric (op):
+ assert (op.namespace == 'root/cimv2')
+ assert (op.classname[0:11] == 'PCP_Metric_')
+ return op.classname[11:].replace('__', '.')
+
+
+# Search the given PM_ResultSet for a given instance number (if any);
+# return formatted CIM of the value (or CIM None)
+def PCP_CIMValueString (context, result, desc, inst):
+ for i in range (0,result.contents.get_numval(0)):
+ value = result.contents.get_vlist(0,i)
+ iv = value.inst
+ if (inst is not None and inst != iv):
+ continue
+ atom = context.pmExtractValue (result.contents.get_valfmt(0),
+ value,
+ desc.contents.type,
+ desc.contents.type)
+ atomValue = atom.dref(desc.contents.type) # nb: atomValue could be numeric etc.
+ return pywbem.CIMProperty(name='ValueString',
+ value=str(atomValue), # stringify it here
+ type='string')
+
+ return pywbem.CIMProperty(name='ValueString',
+ value=None,
+ type='string')
+
+
+def PCP_CIMStatisticTime (result):
+ dt = datetime.datetime.fromtimestamp(float(str(result.contents.timestamp)))
+ return pywbem.CIMDateTime(dt)
+
+
+# generic payload generator, used for
+# - iterating across instance domains (keys_only=1)
+# - fetching metric values
+# - fetching metric metadata (for those model/filter fields set)
+def get_instance (env, op, model, keys_only):
+ metric = MOFname_to_PCPmetric (op)
+ global context
+
+ try:
+ if (context == None):
+ context = pmapi.pmContext() # localhost or equivalent
+ context.pmReconnectContext() # in case it was nuked recently
+ except pmapi.pmErr, e:
+ context = None
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Unable to connect to local PMCD:" + str(e))
+
+ pmids = context.pmLookupName(metric)
+ pmid = pmids[0]
+ desc = context.pmLookupDesc(pmid)
+
+ if ('InstanceID' in model):
+ selected_instanceid = model['InstanceID']
+ else:
+ selected_instanceid = None
+
+ model.path.update({'InstanceID':None})
+
+ if (not keys_only):
+ model['PMID'] = pywbem.Uint32(pmid)
+ model['ElementName'] = metric
+ # must not fail, or else we have no metric value data worth sharing
+ try:
+ results = context.pmFetch(pmids)
+ except pmapi.pmErr, e:
+ # fatal
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "PCP pmFetch failed:" + str(e))
+ # cannot fail
+ model['Units'] = context.pmUnitsStr(desc.contents.units)
+ model['Type'] = context.pmTypeStr(desc.contents.type)
+ # these may fail, but not fatally
+ try:
+ model['Caption'] = context.pmLookupText(pmid)
+ except pmapi.pmErr:
+ pass
+ try:
+ model['Description'] = context.pmLookupText(pmid, c_api.PM_TEXT_HELP)
+ except pmapi.pmErr:
+ pass
+
+ try:
+ instL, nameL = context.pmGetInDom(desc)
+ for iL, nL in zip(instL, nameL):
+ new_instanceid = 'PCP:'+metric+':'+nL
+ if (selected_instanceid is None or
+ new_instanceid == selected_instanceid):
+ model['InstanceNumber'] = pywbem.Uint32(iL)
+ model['InstanceName'] = nL
+ model['InstanceID'] = new_instanceid
+ if (not keys_only):
+ model['StatisticTime'] = PCP_CIMStatisticTime (results)
+ model['ValueString'] = PCP_CIMValueString (context, results, desc, iL)
+ yield model
+ except pmapi.pmErr: # pmGetInDom is expected to fail for non-instance (PM_INDOM_NULL) metrics
+ new_instanceid = 'PCP:'+metric
+ if (selected_instanceid is None or
+ new_instanceid == selected_instanceid):
+ model['InstanceNumber'] = pywbem.CIMProperty(name='InstanceNumber',
+ value=None,type='uint32')
+ model['InstanceName'] = pywbem.CIMProperty(name='InstanceName',
+ value=None,type='string')
+ model['InstanceID'] = new_instanceid
+ if (not keys_only):
+ model['StatisticTime'] = PCP_CIMStatisticTime (results)
+ model['ValueString'] = PCP_CIMValueString (context, results, desc, None)
+ yield model
+
+# hooks for impersonating CIMProvider2 functions
+
+
+def MI_enumInstanceNames (env, op):
+ model = pywbem.CIMInstance(classname = op.classname, path=op)
+ for x in get_instance (env, op, model, True):
+ yield x.path
+
+def MI_enumInstances (env, op, plist):
+ model = pywbem.CIMInstance(classname = op.classname, path=op)
+ return get_instance (env, op, model, False)
+
+def MI_getInstance (env, op, plist):
+ proplist = None
+ if plist is not None:
+ proplist = [s.lower() for s in propertyList]
+ proplist+= [s.lower() for s in op.keybindings.keys()]
+ model = pywbem.CIMInstance(classname=op.classname, path=op,
+ property_list=proplist)
+ model.update(model.path.keybindings)
+ for x in get_instance (env, op, model, False):
+ return x # XXX: first one
+
+def MI_createInstance (env, pinst):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+def MI_modifyInstance (env, pinst, plist):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+def MI_deleteInstance (env, piname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+# See also extra MI_* functions for associations, etc.;
+# cmpi-bindings.git swig/python/cmpi_pywbem_bindings.py
diff --git a/src/pcp/openlmi-pcp-generate b/src/pcp/openlmi-pcp-generate
new file mode 100644
index 0000000..8c53512
--- /dev/null
+++ b/src/pcp/openlmi-pcp-generate
@@ -0,0 +1,68 @@
+#! /bin/sh
+
+# This script refreshes WBEM/CIM MOF & REG files from the current PCP PMNS,
+# if necessary, and reloads the new MOF/REGs into the CIMMON.
+#
+# The PCP PMNS changes infrequently (when the sysadmin manuall installs or
+# removes pcp PMDA (agent) modules in /var/lib/pcp/pmdas/*).
+#
+# This script encodes the PCP->CIM metric name-mapping convention of replacing
+# dots with double-underscores, which lmi/pcp/metric.py will dutifully undo.
+
+_LOCALSTATEDIR=/var
+_DATADIR=/usr/share
+NAME=openlmi-providers
+PYTHON2_SITELIB=/usr/lib/python2.7/site-packages
+
+PCP_PMNS=$_LOCALSTATEDIR/lib/pcp/pmns/root
+PCP_HOST=${1-localhost} # or local:// for pcp 3.9+
+BASEMOFFILE=$_DATADIR/$NAME/60_LMI_PCP.mof
+MOFREGDIR=$_LOCALSTATEDIR/lib/$NAME
+STAMPFILE=$MOFREGDIR/stamp
+MOFFILE=$MOFREGDIR/60_LMI_PCP_PMNS.mof
+REGFILE=$MOFREGDIR/60_LMI_PCP_PMNS.reg
+PROVIDER=$PYTHON2_SITELIB/lmi/pcp/metric.py
+
+if [ ! -f $PROVIDER ]; then
+ echo "Cannot find $PROVIDER" 1>&2
+ exit 1
+fi
+
+set -e
+
+echo Refreshing PCP_Metric CIMMON classes from current PCP PMNS
+
+# quick liveness test
+pcp -h $PCP_HOST
+
+if [ -s $PCP_PMNS -a $PCP_PMNS -nt $STAMPFILE ]; then
+ if [ -f $MOFFILE -a -f $REGFILE ]; then
+ echo Unregistering $BASEMOFFILE
+ echo Unregistering previous $MOFFILE
+ echo Unregistering previous $REGFILE
+ openlmi-mof-register unregister $BASEMOFFILE $MOFFILE $REGFILE || :
+ fi
+
+ echo Generating $MOFFILE
+ pminfo -h $PCP_HOST | sed -e 's,\.,__,g' |
+ awk '{print "class PCP_Metric_" $1 " : PCP_MetricValue { } ;" }' > $MOFFILE
+
+ echo Generating $REGFILE
+ pminfo -h $PCP_HOST | sed -e 's,\.,__,g' |
+ awk '{print "[PCP_Metric_" $1 "]"
+ print " provider: '$PROVIDER'"
+ print " location: pyCmpiProvider"
+ print " type: instance"
+ print " namespace: root/cimv2"
+ print " group: pcp"
+ print ""}' > $REGFILE
+
+ echo Registering $BASEMOFFILE
+ echo Registering new $MOFFILE
+ echo Registering new $REGFILE
+ openlmi-mof-register register $BASEMOFFILE $MOFFILE $REGFILE 2>&1 | # filter out two noise diagnostics
+ egrep -v 'Warning: the instance already exists.|In this implementation, that means it cannot be changed.' || :
+ touch $STAMPFILE
+else
+ echo Doing nothing, $PCP_PMNS older than $STAMPFILE
+fi
diff --git a/src/pcp/openlmi-pcp.cron b/src/pcp/openlmi-pcp.cron
new file mode 100644
index 0000000..a9691f0
--- /dev/null
+++ b/src/pcp/openlmi-pcp.cron
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+openlmi-pcp-generate localhost >/dev/null 2>&1
diff --git a/src/pcp/setup.py b/src/pcp/setup.py
new file mode 100644
index 0000000..9ffa320
--- /dev/null
+++ b/src/pcp/setup.py
@@ -0,0 +1,19 @@
+from setuptools import setup
+setup(
+ name='lmi-pcp',
+ description='PCP metric providers',
+ author='Frank Ch. Eigler',
+ author_email='fche@redhat.com',
+ url='https://fedorahosted.org/openlmi/',
+ version='0.1',
+ namespace_packages=['lmi'],
+ packages=[
+ 'lmi.pcp'],
+ install_requires=['lmi', 'pcp'],
+ license="LGPLv2+",
+ classifiers=[
+ 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
+ 'Operating System :: POSIX :: Linux',
+ 'Topic :: System :: Systems Administration',
+ ]
+ )