summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Schiffer <pschiffe@redhat.com>2013-07-18 14:15:54 +0200
committerPeter Schiffer <pschiffe@redhat.com>2013-07-18 14:15:54 +0200
commit7c5b2d12272abd8f9dd0057ea10a8578f492d2dd (patch)
treea492bf9ad6b0632a7827c0cdbd8b7a3c85c607f8
parentec3391938be3058f11036bade813ba4239d7103b (diff)
parent30d91d80793392adb4c5b3890555662caa0031da (diff)
downloadopenlmi-providers-7c5b2d12272abd8f9dd0057ea10a8578f492d2dd.tar.gz
openlmi-providers-7c5b2d12272abd8f9dd0057ea10a8578f492d2dd.tar.xz
openlmi-providers-7c5b2d12272abd8f9dd0057ea10a8578f492d2dd.zip
Merge branch 'master' of ssh://git.fedorahosted.org/git/openlmi-providers
-rw-r--r--README7
-rw-r--r--mof/60_LMI_PCP.mof44
-rwxr-xr-xopenlmi-mof-register26
-rw-r--r--openlmi-providers.spec82
-rwxr-xr-xopenlmi-register-pegasus15
-rw-r--r--src/account/test/TestAccount.py8
-rw-r--r--src/account/test/TestIndications.py100
-rw-r--r--src/account/test/TestMemberOfGroup.py4
-rw-r--r--src/account/test/common.py158
-rw-r--r--src/account/test/methods.py37
-rw-r--r--src/indmanager/ind_manager.c3
-rw-r--r--src/logicalfile/LMI_DirectoryContainsFileProvider.c12
-rw-r--r--src/logicalfile/LMI_FileIdentityProvider.c26
-rw-r--r--src/logicalfile/LMI_RootDirectoryProvider.c12
-rw-r--r--src/logicalfile/LMI_UnixDirectoryProvider.c7
-rw-r--r--src/logicalfile/LMI_UnixFileProvider.c1
-rw-r--r--src/logicalfile/file.c141
-rw-r--r--src/logicalfile/file.h1
-rw-r--r--src/logicalfile/test/README6
-rw-r--r--src/logicalfile/test/test_base.py9
-rw-r--r--src/logicalfile/test/test_basic.py97
-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
27 files changed, 1014 insertions, 116 deletions
diff --git a/README b/README
index 1962f17..8c51e92 100644
--- a/README
+++ b/README
@@ -64,6 +64,11 @@ Following providers are part of this sub-project:
This is a CIM interface for the RealmD daemon which allows for the Kerberos
and Active Directory realms enrollment
+* PCP
+ This is a CIM interface for the PCP (Performance Co-Pilot) daemon. Allows
+ reading of PCP metrics on the local host.
+
+
*******************************************************************************
* Build Dependencies *
*******************************************************************************
@@ -90,6 +95,8 @@ Provider specific dependencies:
* RealmD
- glib2-devel
- dbus-devel
+* PCP
+ - python-pcp
*******************************************************************************
* Compilation and installation *
diff --git a/mof/60_LMI_PCP.mof b/mof/60_LMI_PCP.mof
new file mode 100644
index 0000000..8ba122b
--- /dev/null
+++ b/mof/60_LMI_PCP.mof
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Stub MOF for the CIM<->PCP bridge. Further MOF/REG clauses that
+ * describe the PCP PMNS details may be generated periodically using
+ * pcp2cim.sh.
+ *
+ * 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>
+ */
+class PCP_MetricValue : CIM_StatisticalData
+{
+ [Description("PCP metric PMID")]
+ uint32 PMID;
+
+ [Description("PMAPI indom instance number")]
+ uint32 InstanceNumber;
+
+ [Description("PMAPI indom instance name")]
+ string InstanceName;
+
+ [Description("The metric type, as returned by pmTypeStr(3)")]
+ string Type;
+
+ [Description("The metric value, as rendered into string form by pmAtomStr() or pmPrintValue(3)")]
+ string ValueString;
+
+ [Description("The metric units, as returned by pmUnitsStr(3)")]
+ string Units;
+};
+
diff --git a/openlmi-mof-register b/openlmi-mof-register
index d327a1e..efc9df3 100755
--- a/openlmi-mof-register
+++ b/openlmi-mof-register
@@ -17,6 +17,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors: Radek Novacek <rnovacek@redhat.com>
+# Jan Safranek <jsafrane@redhat.com>
#
pegasus_repository="/var/lib/Pegasus/"
@@ -44,12 +45,15 @@ function stop_pegasus()
function usage()
{
- printf "Usage: $0 [ --just-mofs ] [ -n namespace ] [ -c cimom ]
+ printf "Usage: $0 [ --just-mofs ] [ -n namespace ] [ -c cimom ] [-v version]
CMD <mof> [mof] [...] [reg]
CMD is one of [ register, unregister ]
Default namespace is $default_namespace, which can be changed with '-n' option.
+ If a registration file is provided, '-v' parameter is mandatory and specifies
+ version of the provider API.
+
Supported cimoms are sfcbd and tog-pegasus. Without \"-c\" argument, the
operation is processed for any cimom present on system (all of them).
@@ -57,9 +61,9 @@ function usage()
treated as mof files - no registration file is expected.
usage with --just-mofs:
- $0 CMD <mof> [mof] [...]
+ $0 --just-mofs CMD <mof> [mof] [...]
usage without:
- $0 CMD <mof> [mof] [...] <reg>\n"
+ $0 -v <version> CMD <mof> [mof] [...] <reg>\n"
}
@@ -91,9 +95,9 @@ function register()
if [ $JUST_MOFS -eq 0 ]; then
if [ -x $(dirname $0)/openlmi-register-pegasus ];
then
- cat "$reg" | $(dirname $0)/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
+ cat "$reg" | $(dirname $0)/openlmi-register-pegasus -v "$version" | $CIMMOF -uc -n root/PG_Interop
else
- cat "$reg" | /usr/libexec/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
+ cat "$reg" | /usr/libexec/openlmi-register-pegasus -v "$version" | $CIMMOF -uc -n root/PG_Interop
fi
fi
fi
@@ -141,7 +145,7 @@ function unregister()
}
JUST_MOFS=0
-optspec=":hn:c:-:"
+optspec=":hn:c:v:-:"
while getopts "$optspec" optchar; do
case "$optchar" in
@@ -176,6 +180,9 @@ while getopts "$optspec" optchar; do
usage;
exit 0;
;;
+ v)
+ version="$OPTARG"
+ ;;
*)
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
echo "Non-option argument: '-${OPTARG}'" >&2
@@ -185,10 +192,17 @@ while getopts "$optspec" optchar; do
esac
done
+
shift $(($OPTIND - 1))
namespace=${namespace:-$default_namespace}
cimom=${cimom:-all}
+if [ "$JUST_MOFS" -eq 0 -a -z "$version" ]; then
+ echo "Missing -v option"
+ usage
+ exit 1
+fi
+
if [ $# -lt 2 ];
then
usage
diff --git a/openlmi-providers.spec b/openlmi-providers.spec
index 6e6d8b7..2d181bf 100644
--- a/openlmi-providers.spec
+++ b/openlmi-providers.spec
@@ -1,6 +1,6 @@
Name: openlmi-providers
Version: 0.0.25
-Release: 5%{?dist}
+Release: 6%{?dist}
Summary: Set of basic CIM providers
License: LGPLv2+
@@ -177,6 +177,21 @@ Requires: %{name}%{?_isa} = %{version}-%{release}
%description -n openlmi-indicationmanager-libs-devel
%{summary}.
+%package -n openlmi-pcp
+Summary: pywbem providers for accessing PCP metrics
+Requires: %{name} = %{version}-%{release}
+BuildArch: noarch
+Requires: python-setuptools
+Requires: cmpi-bindings-pywbem
+Requires: python-pcp
+
+%description -n openlmi-pcp
+openlmi-pcp exposes metrics from a local PMCD (Performance Co-Pilot server)
+to the CIMOM. They appear as potentially hundreds of MOF classes, e.g.
+class "PCP_Metric_kernel__pernode__cpu__use", with instances for each PCP
+metric instance, e.g. "node0". PCP metric values and metadata are transcribed
+into strings on demand.
+
%prep
%setup -q
@@ -186,7 +201,7 @@ pushd %{_target_platform}
%{cmake} ..
popd
-make %{?_smp_mflags} -C %{_target_platform}
+make -k %{?_smp_mflags} -C %{_target_platform}
pushd src/python
%{__python} setup.py build
@@ -195,6 +210,9 @@ popd # src/python
pushd src/software
%{__python} setup.py build
popd # src/software
+pushd src/pcp
+%{__python} setup.py build
+popd
%install
make install/fast DESTDIR=$RPM_BUILD_ROOT -C %{_target_platform}
@@ -217,6 +235,21 @@ install -m 755 pycmpiLMI_Software-cimprovagt $RPM_BUILD_ROOT/%{_libexecdir}/pega
popd # src/software
cp mof/LMI_Software.reg $RPM_BUILD_ROOT/%{_datadir}/%{name}/
+# pcp
+pushd src/pcp
+%{__python} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT
+popd
+cp -p src/pcp/openlmi-pcp-generate $RPM_BUILD_ROOT/%{_bindir}/openlmi-pcp-generate
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/cron.daily
+cp -p src/pcp/openlmi-pcp.cron $RPM_BUILD_ROOT/%{_sysconfdir}/cron.daily/openlmi-pcp
+sed -i -e 's,^_LOCALSTATEDIR=.*,_LOCALSTATEDIR="%{_localstatedir}",' \
+ -e 's,^_DATADIR=.*,_DATADIR="%{_datadir}",' \
+ -e 's,^NAME=.*,NAME="%{name}",' \
+ -e 's,^PYTHON2_SITELIB=.*,PYTHON2_SITELIB="%{python2_sitelib}",' \
+ $RPM_BUILD_ROOT/%{_bindir}/openlmi-pcp-generate \
+ $RPM_BUILD_ROOT/%{_sysconfdir}/cron.daily/openlmi-pcp
+mkdir -p $RPM_BUILD_ROOT/%{_localstatedir}/lib/%{name}
+
%files
%doc README COPYING
@@ -290,6 +323,19 @@ cp mof/LMI_Software.reg $RPM_BUILD_ROOT/%{_datadir}/%{name}/
%{_datadir}/%{name}/70_LMI_SoftwareIndicationFilters.mof
%{_datadir}/%{name}/LMI_Software.reg
+%files -n openlmi-pcp
+%doc README COPYING
+%{_datadir}/%{name}/60_LMI_PCP.mof
+%dir %{python_sitelib}/lmi/pcp
+%{python_sitelib}/lmi/pcp/*
+%{python_sitelib}/lmi_pcp-*
+%attr(755, root, root) %{_bindir}/openlmi-pcp-generate
+%attr(755, root, root) %{_sysconfdir}/cron.daily/openlmi-pcp
+%dir %{_localstatedir}/lib/%{name}
+%ghost %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.mof
+%ghost %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.reg
+%ghost %{_localstatedir}/lib/%{name}/stamp
+
%files -n openlmi-logicalfile
%doc README COPYING
%{_libdir}/cmpi/libcmpiLMI_LogicalFile.so
@@ -435,6 +481,7 @@ if [ "$1" -gt 1 ]; then
> /dev/null 2>&1 || :;
fi
+
%pre -n openlmi-hardware
if [ "$1" -gt 1 ]; then
%{_bindir}/openlmi-mof-register unregister \
@@ -443,6 +490,15 @@ if [ "$1" -gt 1 ]; then
> /dev/null 2>&1 || :;
fi
+%pre -n openlmi-pcp
+if [ "$1" -gt 1 ]; then
+ %{_bindir}/openlmi-mof-register unregister \
+ %{_datadir}/%{name}/60_LMI_PCP.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.reg \
+ > /dev/null 2>&1 || :;
+fi
+
%post -n openlmi-fan
# Register Schema and Provider
if [ "$1" -ge 1 ]; then
@@ -518,6 +574,15 @@ if [ "$1" -gt 1 ]; then
> /dev/null 2>&1 || :;
fi
+%post -n openlmi-pcp
+if [ "$1" -gt 1 ]; then
+ %{_bindir}/openlmi-mof-register register \
+ %{_datadir}/%{name}/60_LMI_PCP.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.reg \
+ > /dev/null 2>&1 || :;
+fi
+
%preun -n openlmi-fan
# Deregister only if not upgrading
if [ "$1" -eq 0 ]; then
@@ -585,6 +650,7 @@ if [ "$1" -gt 1 ]; then
> /dev/null 2>&1 || :;
fi
+
%preun -n openlmi-hardware
if [ "$1" -gt 1 ]; then
%{_bindir}/openlmi-mof-register unregister \
@@ -593,7 +659,19 @@ if [ "$1" -gt 1 ]; then
> /dev/null 2>&1 || :;
fi
+%preun -n openlmi-pcp
+if [ "$1" -gt 1 ]; then
+ %{_bindir}/openlmi-mof-register unregister \
+ %{_datadir}/%{name}/60_LMI_PCP.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.mof \
+ %{_localstatedir}/lib/%{name}/60_LMI_PCP_PMNS.reg \
+ > /dev/null 2>&1 || :;
+fi
+
%changelog
+* Mon Jul 08 2013 Frank Ch. Eigler <fche@redhat.com> 0.0.25-6
+- Added PCP provider in optional openlmi-pcp subrpm.
+
* Mon Jul 15 2013 Jan Synáček <jsynacek@redhat.com> - 0.0.25-5
- Added libselinux-devel to BuildRequires.
diff --git a/openlmi-register-pegasus b/openlmi-register-pegasus
index d314d27..149af59 100755
--- a/openlmi-register-pegasus
+++ b/openlmi-register-pegasus
@@ -17,10 +17,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors: Radek Novacek <rnovacek@redhat.com>
+# Jan Safranek <jsafrane@redhat.com>
#
import sys
import re
+import argparse
reg_parse = re.compile(r"\[([^\]]+)\]\s+"
"provider: ([^\s]+)\s+"
@@ -38,18 +40,18 @@ Types = {
'instanceQuery': '7'
}
-def define_module(location, group):
+def define_module(location, group, version):
return """instance of PG_ProviderModule
{
Name = "%(group)s";
Location = "%(location)s";
Vendor = "OpenLMI";
- Version = "0.0.1";
+ Version = "%(version)s";
InterfaceType = "CMPI";
InterfaceVersion = "2.0.0";
ModuleGroupName = "%(group)s";
};
-""" % { 'location': location, 'group': group }
+""" % { 'location': location, 'group': group, 'version': version }
def getTypes(types):
l = []
@@ -78,6 +80,11 @@ instance of PG_ProviderCapabilities
};
""" % { 'location': location, 'provider': provider, 'class': cls, 'types': getTypes(types), 'group': group }
+parser = argparse.ArgumentParser(description='Create MOF files with registration of providers for Pegasus CIMOM.')
+parser.add_argument('-v', '--version', action='store', required=True, help="Version of the provider API.")
+args = parser.parse_args()
+version = args.version
+
modules_defined = {}
for record in reg_parse.findall(sys.stdin.read()):
cls, provider, location, types, namespace, _unused, group = record
@@ -86,7 +93,7 @@ for record in reg_parse.findall(sys.stdin.read()):
group = location
if group not in modules_defined:
- print define_module(location, group)
+ print define_module(location, group, version)
modules_defined[group] = True
print define_capability(location, provider, cls, types, group)
diff --git a/src/account/test/TestAccount.py b/src/account/test/TestAccount.py
index e6f0c14..5763c4d 100644
--- a/src/account/test/TestAccount.py
+++ b/src/account/test/TestAccount.py
@@ -55,7 +55,7 @@ class TestAccount(AccountBase):
self.wbemconnection.InvokeMethod("CreateAccount", lams.path,
Name=self.user_name, System=computer_system.path)
# The user now should be created, check it
- subprocess.check_call(["id", self.user_name])
+ self.assertTrue(user_exists(self.user_name))
# now delete that user
clean_account(self.user_name)
@@ -69,9 +69,7 @@ class TestAccount(AccountBase):
'select * from LMI_Account where Name = "%s"' % self.user_name)[0]
self.wbemconnection.DeleteInstance(i.path)
# check if it was really deleted
- c = subprocess.Popen(["id", self.user_name])
- c.communicate()
- self.assertEqual(c.returncode, 1)
+ self.assertFalse(user_exists(self.user_name))
clean_account(self.user_name)
def test_modify_account(self):
@@ -82,9 +80,7 @@ class TestAccount(AccountBase):
i = self.wbemconnection.ExecQuery('WQL',
'select * from LMI_Account where Name = "%s"' % self.user_name)[0]
# gecos
- print i["ElementName"]
i["ElementName"] = "GECOS"
- print i["ElementName"]
self.wbemconnection.ModifyInstance(i)
self.assertEqual(field_in_passwd(self.user_name, 4), "GECOS")
# login shell
diff --git a/src/account/test/TestIndications.py b/src/account/test/TestIndications.py
new file mode 100644
index 0000000..7b34b2e
--- /dev/null
+++ b/src/account/test/TestIndications.py
@@ -0,0 +1,100 @@
+# 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: Roman Rakus <rrakus@redhat.com>
+#
+
+from common import AccountBase
+import time
+from methods import *
+
+class TestIndications(AccountBase):
+ """
+ Class for testing LMI_Account indications
+ """
+ def test_check_good_filter(self):
+ """
+ Account: Test good indication filter
+ """
+ filter_name = "test_good_filter_%d" % time.time() * 1000
+ sub = self.subscribe(filter_name, "select * from LMI_AccountInstanceCreationIndication where SourceInstance isa LMI_Account")
+ self.assertIsNotNone(sub)
+ self.unsubscribe(filter_name);
+
+ def test_check_bad_filter(self):
+ """
+ Account: Test bad indication filter
+ """
+ pass
+
+ def test_group_deletion_indication(self):
+ """
+ Account: Test indication of group deletion
+ """
+ create_group(self.group_name)
+ filter_name = "test_delete_group_%d" % time.time() * 1000
+ sub = self.subscribe(filter_name, "select * from LMI_AccountInstanceDeletionIndication where SourceInstance isa LMI_Group")
+ clean_group(self.group_name)
+ indication = self.get_indication(10)
+ self.assertEqual(indication.classname, "LMI_AccountInstanceDeletionIndication")
+ self.assertIn("SourceInstance", indication.keys())
+ self.assertTrue(indication["SourceInstance"] is not None)
+ self.assertEqual(indication["SourceInstance"]["Name"], self.group_name)
+
+ def test_group_creation_indication(self):
+ """
+ Account: Test indication of group creation
+ """
+ clean_group(self.group_name)
+ filter_name = "test_create_group_%d" % time.time() * 1000
+ sub = self.subscribe(filter_name, "select * from LMI_AccountInstanceCreationIndication where SourceInstance isa LMI_Group")
+ create_group(self.group_name)
+ indication = self.get_indication(10)
+ self.assertEqual(indication.classname, "LMI_AccountInstanceCreationIndication")
+ self.assertIn("SourceInstance", indication.keys())
+ self.assertTrue(indication["SourceInstance"] is not None)
+ self.assertEqual(indication["SourceInstance"]["Name"], self.group_name)
+ clean_group(self.group_name)
+
+ def test_account_deletion_indication(self):
+ """
+ Account: Test indication of account deletion
+ """
+ create_account(self.user_name)
+ filter_name = "test_delete_account_%d" % time.time() * 1000
+ sub = self.subscribe(filter_name, "select * from LMI_AccountInstanceDeletionIndication where SourceInstance isa LMI_Account")
+ clean_account(self.user_name)
+ indication = self.get_indication(10)
+ self.assertEqual(indication.classname, "LMI_AccountInstanceDeletionIndication")
+ self.assertIn("SourceInstance", indication.keys())
+ self.assertTrue(indication["SourceInstance"] is not None)
+ self.assertEqual(indication["SourceInstance"]["Name"], self.user_name)
+
+ def test_account_creation_indication(self):
+ """
+ Account: Test indication of account creation
+ """
+ clean_account(self.user_name)
+ filter_name = "test_create_account_%d" % time.time() * 1000
+ sub = self.subscribe(filter_name, "select * from LMI_AccountInstanceCreationIndication where SourceInstance isa LMI_Account")
+ create_account(self.user_name)
+ indication = self.get_indication(10)
+ self.assertEqual(indication.classname, "LMI_AccountInstanceCreationIndication")
+ self.assertIn("SourceInstance", indication.keys())
+ self.assertTrue(indication["SourceInstance"] is not None)
+ self.assertEqual(indication["SourceInstance"]["Name"], self.user_name)
+ clean_account(self.user_name)
+
diff --git a/src/account/test/TestMemberOfGroup.py b/src/account/test/TestMemberOfGroup.py
index 635ac24..211244e 100644
--- a/src/account/test/TestMemberOfGroup.py
+++ b/src/account/test/TestMemberOfGroup.py
@@ -53,9 +53,7 @@ class TestMemberOfGroup(AccountBase):
'select * from LMI_Account where Name = "%s"' % self.user_name)[0]
self.wbemconnection.DeleteInstance(i.path)
# check if it was really deleted
- c = subprocess.Popen(["id", self.user_name])
- c.communicate()
- self.assertEqual(c.returncode, 1)
+ self.assertFalse(user_exists(self.user_name))
clean_account(self.user_name)
def test_user_in_groups(self):
diff --git a/src/account/test/common.py b/src/account/test/common.py
index c56e94d..39442af 100644
--- a/src/account/test/common.py
+++ b/src/account/test/common.py
@@ -21,11 +21,56 @@
import pywbem
import os
import unittest
+import Queue
+import random
+import BaseHTTPServer
+import socket
+import threading
"""
Base class for all tests
"""
+class CIMListener(object):
+ """ CIM Listener
+ """
+ class CIMHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ def do_POST(self):
+ data = self.rfile.read(int(self.headers['Content-Length']))
+ tt = pywbem.parse_cim(pywbem.xml_to_tupletree(data))
+ # Get the instance from CIM-XML, copied from
+ # http://sf.net/apps/mediawiki/pywbem/?title=Indications_Tutorial
+ insts = [x[1] for x in tt[2][2][0][2][2]]
+ for inst in insts:
+ self.callback(inst)
+ self.send_response(200)
+ self.end_headers()
+
+ def log_message(self, format, *p):
+ # suppress log messages
+ pass
+
+ def __init__(self, callback, http_port=5988):
+ self.address = ('', http_port)
+ self.CIMHandler.callback = callback
+ self.thread = None
+ self.server = None
+
+ def start(self):
+ BaseHTTPServer.HTTPServer.allow_reuse_address = True
+ self.server = BaseHTTPServer.HTTPServer(self.address, self.CIMHandler)
+ self.thread = threading.Thread(target=self.server.serve_forever)
+ self.thread.start()
+
+ def stop(self):
+ if self.server is not None:
+ self.server.shutdown()
+ self.server.socket.close()
+ if self.thread is not None:
+ self.thread.join()
+
+ def running(self):
+ return self.thread is not None
class AccountBase(unittest.TestCase):
@@ -44,3 +89,116 @@ class AccountBase(unittest.TestCase):
self.wbemconnection = pywbem.WBEMConnection(self.url,
(self.username, self.password))
+ # for indications
+ self.indication_port = random.randint(12000, 13000)
+ self.indication_queue = Queue.Queue()
+ self.listener = CIMListener(
+ callback=self._process_indication,
+ http_port=self.indication_port)
+
+ self.subscribed = {}
+
+ def tearDown(self):
+ self.listener.stop()
+ if self.subscribed:
+ for name in self.subscribed.keys():
+ self.unsubscribe(name)
+
+ def get_indication(self, timeout):
+ """ Wait for an indication for given nr. of seconds and return it."""
+ try:
+ indication = self.indication_queue.get(timeout=timeout)
+ except Queue.Empty:
+ raise AssertionError("Timeout when waiting for indicaiton")
+ self.indication_queue.task_done()
+ return indication
+
+ def subscribe(self, filter_name, query=None, querylang="DMTF:CQL"):
+ """
+ Create indication subscription for given filter name.
+ """
+ namespace = "root/PG_interop"
+ hostname = socket.gethostname()
+
+ if query is not None:
+ # Create filter first
+ filterinst = pywbem.CIMInstance('CIM_IndicationFilter')
+ filterinst['CreationClassName'] = 'CIM_IndicationFilter'
+ filterinst['SystemCreationClassName'] = 'CIM_ComputerSystem'
+ filterinst['SystemName'] = hostname
+ filterinst['Name'] = filter_name
+ filterinst['Query'] = query
+ filterinst['QueryLanguage'] = querylang
+ filterinst['SourceNamespace'] = "root/cimv2"#namespace
+ cop = pywbem.CIMInstanceName('CIM_IndicationFilter')
+ cop.keybindings = { 'CreationClassName': 'CIM_IndicationFilter',
+ 'SystemClassName': 'CIM_ComputerSystem',
+ 'SystemName': hostname,
+ 'Name': filter_name
+ }
+ cop.namespace=namespace
+ filterinst.path = cop
+ indfilter = self.wbemconnection.CreateInstance(filterinst)
+ else:
+ # the filter is already created, assemble its name
+ indfilter = pywbem.CIMInstanceName(
+ classname="CIM_IndicationFilter",
+ namespace=namespace,
+ keybindings={
+ 'CreationClassName': 'CIM_IndicationFilter',
+ 'SystemClassName': 'CIM_ComputerSystem',
+ 'SystemName': hostname,
+ 'Name': filter_name})
+
+ # create destination
+ destinst = pywbem.CIMInstance('CIM_ListenerDestinationCIMXML')
+ destinst['CreationClassName'] = 'CIM_ListenerDestinationCIMXML'
+ destinst['SystemCreationClassName'] = 'CIM_ComputerSystem'
+ destinst['SystemName'] = hostname
+ destinst['Name'] = filter_name
+ destinst['Destination'] = "http://localhost:%d" % (self.indication_port)
+ destinst['PersistenceType'] = pywbem.Uint16(3) # Transient
+ cop = pywbem.CIMInstanceName('CIM_ListenerDestinationCIMXML')
+ cop.keybindings = { 'CreationClassName':'CIM_ListenerDestinationCIMXML',
+ 'SystemClassName':'CIM_ComputerSystem',
+ 'SystemName':hostname,
+ 'Name':filter_name }
+ cop.namespace = namespace
+ destinst.path = cop
+ destname = self.wbemconnection.CreateInstance(destinst)
+
+ # create the subscription
+ subinst = pywbem.CIMInstance('CIM_IndicationSubscription')
+ subinst['Filter'] = indfilter
+ subinst['Handler'] = destname
+ cop = pywbem.CIMInstanceName('CIM_IndicationSubscription')
+ cop.keybindings = { 'Filter': indfilter,
+ 'Handler': destname }
+ cop.namespace = namespace
+ subinst.path = cop
+ subscription = self.wbemconnection.CreateInstance(subinst)
+
+ self.subscribed[filter_name] = [subscription, destname]
+
+ # start listening
+ if not self.listener.running():
+ self._start_listening()
+ return subscription
+
+ def unsubscribe(self, filter_name):
+ """
+ Unsubscribe fron given filter.
+ """
+ _list = self.subscribed.pop(filter_name)
+ for instance in _list:
+ self.wbemconnection.DeleteInstance(instance)
+
+ def _start_listening(self):
+ """ Start listening for incoming indications. """
+ self.listener.start()
+
+ def _process_indication(self, indication):
+ """ Callback to process one indication."""
+ self.indication_queue.put(indication)
+
+
diff --git a/src/account/test/methods.py b/src/account/test/methods.py
index c0407df..ffb9f7d 100644
--- a/src/account/test/methods.py
+++ b/src/account/test/methods.py
@@ -20,6 +20,20 @@
import subprocess
+def user_exists(username):
+ """
+ Return true/false if user does/does not exists
+ """
+ got = field_in_passwd(username, 0)
+ return got == username
+
+def group_exists(groupname):
+ """
+ Return true/false if user does/does not exists
+ """
+ got = field_in_group(groupname, 0)
+ return got == groupname
+
def field_in_passwd(username, number):
"""
Return numberth field in /etc/passwd for given username
@@ -48,33 +62,36 @@ def clean_account(user_name):
"""
Force to delete testing account and remove home dir
"""
- subprocess.call(["userdel", "-fr", user_name])
- # groups should be expicitely deleted
- subprocess.call(["groupdel", user_name])
- subprocess.call(["rm", "-fr", "/home/%s" %user_name])
+ if user_exists(user_name):
+ subprocess.check_call(["userdel", "-fr", user_name])
+ if group_exists(user_name):
+ # groups should be expicitely deleted
+ subprocess.check_call(["groupdel", user_name])
def add_user_to_group(user_name, group_name):
"""
Will add user to group
"""
- subprocess.call(["usermod", "-a", "-G", group_name, user_name])
+ subprocess.check_call(["usermod", "-a", "-G", group_name, user_name])
def create_account(user_name):
"""
Force to create account; run clean_account before creation
"""
- clean_account(user_name)
- subprocess.call(["useradd", user_name])
+ if not user_exists(user_name):
+ subprocess.check_call(["useradd", user_name])
def clean_group(group_name):
"""
Force to delete testing group
"""
- subprocess.call(["groupdel", group_name])
+ if group_exists(group_name):
+ subprocess.check_call(["groupdel", group_name])
def create_group(group_name):
"""
Force to create group
"""
- clean_group(group_name)
- subprocess.call(["groupadd", group_name])
+ if not group_exists(group_name):
+ subprocess.check_call(["groupadd", group_name])
+
diff --git a/src/indmanager/ind_manager.c b/src/indmanager/ind_manager.c
index d3a73e8..7e2cda3 100644
--- a/src/indmanager/ind_manager.c
+++ b/src/indmanager/ind_manager.c
@@ -582,6 +582,9 @@ char *get_classname(CMPISelectExp *se)
#else
char *select = (char*)CMGetCharsPtr(CMGetSelExpString(se, NULL), NULL);
char *rest = NULL, *token = NULL, *ptr = select;
+ if (!ptr) {
+ return NULL;
+ }
for (; ptr || strcasecmp(token, "from") != 0; ptr = NULL) {
token = strtok_r(ptr, " ", &rest);
if (!token) {
diff --git a/src/logicalfile/LMI_DirectoryContainsFileProvider.c b/src/logicalfile/LMI_DirectoryContainsFileProvider.c
index 1794ea8..46db891 100644
--- a/src/logicalfile/LMI_DirectoryContainsFileProvider.c
+++ b/src/logicalfile/LMI_DirectoryContainsFileProvider.c
@@ -127,7 +127,6 @@ static CMPIStatus associators(
CMPIObjectPath *o;
CMPIInstance *ci;
CMPIStatus st;
- CMPIData pathd;
const char *ns = KNameSpace(cop);
st = lmi_check_required(_cb, cop, LOGICALFILE);
@@ -139,8 +138,7 @@ static CMPIStatus associators(
CMReturn(CMPI_RC_OK);
}
- pathd = CMGetKey(cop, "Name", &st);
- const char *path = KChars(pathd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
CMPIObjectPath *refs[MAX_REFS];
unsigned int count;
@@ -200,8 +198,6 @@ static CMPIStatus references(
{
LMI_DirectoryContainsFile lmi_dcf;
CMPIStatus st;
- CMPIData pathd;
- CMPIData cd;
const char *ns = KNameSpace(cop);
/* GroupComponent */
@@ -225,10 +221,8 @@ static CMPIStatus references(
return st;
}
- cd = CMGetKey(cop, "CreationClassName", &st);
- const char *ccname = KChars(cd.value.string);
- pathd = CMGetKey(cop, "Name", &st);
- const char *path = KChars(pathd.value.string);
+ const char *ccname = get_string_property_from_op(cop, "CreationClassName");
+ const char *path = get_string_property_from_op(cop, "Name");
char *fsname;
if (get_fsname_from_path(path, &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
diff --git a/src/logicalfile/LMI_FileIdentityProvider.c b/src/logicalfile/LMI_FileIdentityProvider.c
index fe3324f..d31c305 100644
--- a/src/logicalfile/LMI_FileIdentityProvider.c
+++ b/src/logicalfile/LMI_FileIdentityProvider.c
@@ -65,8 +65,6 @@ static CMPIStatus associators(
CMPIInstance *ci;
CMPIObjectPath *o;
const char *ns = KNameSpace(cop);
- CMPIData pathd;
- CMPIData cd;
char fileclass[BUFLEN];
char *fsname;
@@ -83,10 +81,8 @@ static CMPIStatus associators(
return st;
}
- pathd = CMGetKey(cop, "LFName", &st);
- cd = CMGetKey(cop, "LFCreationClassName", &st);
- const char *path = KChars(pathd.value.string);
- const char *ccname = KChars(cd.value.string);
+ const char *path = get_string_property_from_op(cop, "LFName");
+ const char *ccname = get_string_property_from_op(cop, "LFCreationClassName");
get_class_from_path(path, fileclass);
if (get_fsname_from_path(path, &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
@@ -111,9 +107,7 @@ static CMPIStatus associators(
return st;
}
- pathd = CMGetKey(cop, "Name", &st);
- cd = CMGetKey(cop, "CreationClassName", &st);
- const char *path = KChars(pathd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
get_class_from_path(path, fileclass);
if (get_fsname_from_path(path, &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
@@ -152,8 +146,6 @@ static CMPIStatus references(
{
LMI_FileIdentity lmi_fi;
CMPIStatus st, res;
- CMPIData pathd;
- CMPIData cd;
const char *ns = KNameSpace(cop);
CMPIInstance *ci;
CMPIObjectPath *o;
@@ -181,10 +173,8 @@ static CMPIStatus references(
return st;
}
- pathd = CMGetKey(cop, "LFName", &st);
- cd = CMGetKey(cop, "LFCreationClassName", &st);
- const char *path = KChars(pathd.value.string);
- const char *ccname = KChars(cd.value.string);
+ const char *path = get_string_property_from_op(cop, "LFName");
+ const char *ccname = get_string_property_from_op(cop, "LFCreationClassName");
get_class_from_path(path, fileclass);
if (get_fsname_from_path(path, &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
@@ -212,10 +202,8 @@ static CMPIStatus references(
return st;
}
- pathd = CMGetKey(cop, "Name", &st);
- cd = CMGetKey(cop, "CreationClassName", &st);
- const char *path = KChars(pathd.value.string);
- const char *ccname = KChars(cd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
+ const char *ccname = get_string_property_from_op(cop, "CreationClassName");
if (get_fsname_from_path(path, &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
}
diff --git a/src/logicalfile/LMI_RootDirectoryProvider.c b/src/logicalfile/LMI_RootDirectoryProvider.c
index dc6cd6b..600f7a5 100644
--- a/src/logicalfile/LMI_RootDirectoryProvider.c
+++ b/src/logicalfile/LMI_RootDirectoryProvider.c
@@ -59,9 +59,7 @@ static CMPIStatus associators(
const char *ns = KNameSpace(cop);
const char *comp_ccname = get_system_creation_class_name();
- CMPIData pathd;
- pathd = CMGetKey(cop, "Name", &st);
- const char *path = KChars(pathd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
char *fsname;
if (get_fsname_from_path("/", &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
@@ -128,12 +126,8 @@ static CMPIStatus references(
const char *comp_ccname = get_system_creation_class_name();
const char *ns = KNameSpace(cop);
- CMPIData pathd;
- CMPIData cd;
- pathd = CMGetKey(cop, "Name", &st);
- cd = CMGetKey(cop, "CreationClassName", &st);
- const char *path = KChars(pathd.value.string);
- const char *ccname = KChars(cd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
+ const char *ccname = get_string_property_from_op(cop, "CreationClassName");
char *fsname;
if (get_fsname_from_path("/", &fsname) < 0) {
CMReturnWithChars(_cb, CMPI_RC_ERR_FAILED, "Can't get filesystem name");
diff --git a/src/logicalfile/LMI_UnixDirectoryProvider.c b/src/logicalfile/LMI_UnixDirectoryProvider.c
index 2628982..7face91 100644
--- a/src/logicalfile/LMI_UnixDirectoryProvider.c
+++ b/src/logicalfile/LMI_UnixDirectoryProvider.c
@@ -78,8 +78,7 @@ static CMPIStatus LMI_UnixDirectoryCreateInstance(
LMI_UnixDirectory_InitFromInstance(&lmi_ud, _cb, ci);
CMPIStatus st;
CMPIObjectPath *iop = CMGetObjectPath(ci, &st);
- CMPIData pathd = CMGetKey(iop, "Name", &st);
- const char *path = KChars(pathd.value.string);
+ const char *path = get_string_property_from_op(iop, "Name");
if (mkdir(path, 0777) < 0) {
char errmsg[BUFLEN];
@@ -107,9 +106,7 @@ static CMPIStatus LMI_UnixDirectoryDeleteInstance(
const CMPIResult* cr,
const CMPIObjectPath* cop)
{
- CMPIStatus st;
- CMPIData pathd = CMGetKey(cop, "Name", &st);
- const char *path = KChars(pathd.value.string);
+ const char *path = get_string_property_from_op(cop, "Name");
if (rmdir(path) < 0) {
char errmsg[BUFLEN];
diff --git a/src/logicalfile/LMI_UnixFileProvider.c b/src/logicalfile/LMI_UnixFileProvider.c
index ff9423b..97f651e 100644
--- a/src/logicalfile/LMI_UnixFileProvider.c
+++ b/src/logicalfile/LMI_UnixFileProvider.c
@@ -121,6 +121,7 @@ static CMPIStatus LMI_UnixFileGetInstance(
snprintf(aux, BUFLEN, "Can't stat file: %s", path);
CMReturnWithChars(_cb, CMPI_RC_ERR_NOT_FOUND, aux);
}
+ LMI_UnixFile_Set_Name(&lmi_file, path);
sprintf(aux, "%u", sb.st_uid);
LMI_UnixFile_Set_UserID(&lmi_file, aux);
sprintf(aux, "%u", sb.st_gid);
diff --git a/src/logicalfile/file.c b/src/logicalfile/file.c
index 64b7d52..e3be07f 100644
--- a/src/logicalfile/file.c
+++ b/src/logicalfile/file.c
@@ -19,52 +19,112 @@
*/
#include "file.h"
-static const char *LOGICALFILE_REQUIRED_NAMES[] = {
- "CSCreationClassName",
- "CSName",
- "CreationClassName",
- "FSCreationClassName",
- "FSName",
- "Name",
- NULL
-};
-
-static const char *UNIXFILE_REQUIRED_NAMES[] = {
- "CSCreationClassName",
- "CSName",
- "LFCreationClassName",
- "FSCreationClassName",
- "FSName",
- "LFName",
- NULL
-};
-
CMPIStatus lmi_check_required(
const CMPIBroker *b,
const CMPIObjectPath *o,
const enum RequiredNames rn)
{
- const char **names;
-
- switch (rn) {
- case LOGICALFILE:
- names = LOGICALFILE_REQUIRED_NAMES;
- break;
- case UNIXFILE:
- names = UNIXFILE_REQUIRED_NAMES;
- break;
- default:
+ const char *prop;
+ const char *errmsg = NULL;
+ char *path = NULL;
+
+ /* check computer system creation class name */
+ if (CMIsNullValue(CMGetKey(o, "CSCreationClassName", NULL))) {
+ errmsg = "CSCreationClassName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "CSCreationClassName");
+ if (strcmp(prop, lmi_get_system_creation_class_name())) {
+ errmsg = "Wrong CSCreationClassName";
+ goto done;
+ }
+
+ /* check fqdn */
+ if (CMIsNullValue(CMGetKey(o, "CSName", NULL))) {
+ errmsg = "CSName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "CSName");
+ if (strcmp(prop, lmi_get_system_name())) {
+ errmsg = "Wrong CSName";
+ goto done;
+ }
+
+ if (rn == UNIXFILE) {
+ /* check creation class name */
+ char fileclass[BUFLEN];
+ if (CMIsNullValue(CMGetKey(o, "LFCreationClassName", NULL))) {
+ errmsg = "LFCreationClassName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "LFCreationClassName");
+ if (get_class_from_path(get_string_property_from_op(o, "LFName"), fileclass) != 0) {
+ errmsg = "Can't get class from path";
+ goto done;
+ }
+ if (strcmp(prop, fileclass)) {
+ errmsg = "LFCreationClassName doesn't match the file's type";
+ goto done;
+ }
+ if (CMIsNullValue(CMGetKey(o, "LFName", NULL))) {
+ errmsg = "LFName is empty";
+ goto done;
+ }
+ if (get_fsname_from_path(get_string_property_from_op(o, "LFName"), &path) < 0) {
+ errmsg = "Can't get FSName from path";
+ goto done;
+ }
+ } else if (rn == LOGICALFILE) {
+ /* check creation class name */
+ if (CMIsNullValue(CMGetKey(o, "CreationClassName", NULL))) {
+ errmsg = "CreationClassName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "CreationClassName");
+ if (!CMClassPathIsA(b, o, prop, NULL)) {
+ errmsg = "CreationClassName and the class name don't match";
+ goto done;
+ }
+ if (CMIsNullValue(CMGetKey(o, "Name", NULL))) {
+ errmsg = "Name is empty";
+ goto done;
+ }
+ if (get_fsname_from_path(get_string_property_from_op(o, "Name"), &path) < 0) {
+ errmsg = "Can't get FSName from path";
+ goto done;
+ }
+ } else {
/* not possible! */
assert(0);
- break;
}
- for (int i = 0; names[i]; i++) {
- if (CMIsNullValue(CMGetKey(o, names[i], NULL))) {
- char errmsg[BUFLEN];
- snprintf(errmsg, BUFLEN, "No '%s' specified", names[i]);
- CMReturnWithChars(b, CMPI_RC_ERR_FAILED, errmsg);
- }
+ /* check fs creation class name and fsname */
+ if (CMIsNullValue(CMGetKey(o, "FSCreationClassName", NULL))) {
+ errmsg = "FSCreationClassName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "FSCreationClassName");
+ if (strcmp(prop, FSCREATIONCLASSNAME)) {
+ errmsg = "Wrong FSCreationClassName";
+ goto done;
+ }
+
+ if (CMIsNullValue(CMGetKey(o, "FSName", NULL))) {
+ errmsg = "FSName is empty";
+ goto done;
+ }
+ prop = get_string_property_from_op(o, "FSName");
+ if (strcmp(prop, path)) {
+ errmsg = "Wrong FSName";
+ goto done;
+ }
+
+done:
+ if (path) {
+ free(path);
+ }
+ if (errmsg) {
+ CMReturnWithChars(b, CMPI_RC_ERR_FAILED, errmsg);
}
CMReturn(CMPI_RC_OK);
}
@@ -137,6 +197,13 @@ int get_fsname_from_path(const char *path, char **fsname)
return rc;
}
+const char *get_string_property_from_op(const CMPIObjectPath *o, const char *prop)
+{
+ CMPIData d;
+ d = CMGetKey(o, prop, NULL);
+ return KChars(d.value.string);
+}
+
void _dump_objectpath(const CMPIObjectPath *o)
{
printf("OP: %s\n", CMGetCharsPtr(o->ft->toString(o, NULL), NULL));
diff --git a/src/logicalfile/file.h b/src/logicalfile/file.h
index df906c2..b7f8c50 100644
--- a/src/logicalfile/file.h
+++ b/src/logicalfile/file.h
@@ -105,6 +105,7 @@ void get_class_from_stat(const struct stat *, char *);
int get_class_from_path(const char *, char *);
int get_fsname_from_stat(const struct stat *, char **);
int get_fsname_from_path(const char *, char **);
+const char *get_string_property_from_op(const CMPIObjectPath *, const char *);
void _dump_objectpath(const CMPIObjectPath *);
diff --git a/src/logicalfile/test/README b/src/logicalfile/test/README
index 54c7c92..3c5a32e 100644
--- a/src/logicalfile/test/README
+++ b/src/logicalfile/test/README
@@ -9,6 +9,10 @@ associations-related methods, they should all be tested from both sides, meaning
that the respective associations should be called twice with both of their
"arguments".
+Dependencies
+------------
+$ yum install python-pyudev
+
Usage
-----
The tests must be run on the same machine as the CIMOM!
@@ -25,4 +29,4 @@ LMI_LOGICALFILE_TESTDIR - Testing directory where all the files and testing will
Running the tests:
LMI_CIMOM_PASSWORD=opensesame \
LMI_LOGICALFILE_TESTDIR="/home/user/my-testing-dir" \
- python test/test_basic.py \ No newline at end of file
+ python test/test_basic.py
diff --git a/src/logicalfile/test/test_base.py b/src/logicalfile/test/test_base.py
index 6d2f2a1..5c71196 100644
--- a/src/logicalfile/test/test_base.py
+++ b/src/logicalfile/test/test_base.py
@@ -21,6 +21,7 @@ import os
import socket
import unittest
import subprocess
+import pyudev
class LogicalFileTestBase(unittest.TestCase):
"""
@@ -43,6 +44,14 @@ class LogicalFileTestBase(unittest.TestCase):
if rc != 0: cls.selinux_enabled = False
except:
cls.selinux_enabled = False
+ ctx = pyudev.Context()
+ sb = os.stat(os.path.realpath(cls.testdir + "/.."))
+ device = pyudev.Device.from_device_number(ctx, "block", sb.st_dev)
+ dev_name = device.get("ID_FS_UUID_ENC")
+ if not dev_name:
+ cls.fsname = "DEVICE=" + device.get("DEVNAME")
+ else:
+ cls.fsname = "UUID=" + dev_name
def setUp(self):
pass
diff --git a/src/logicalfile/test/test_basic.py b/src/logicalfile/test/test_basic.py
index f14de6e..fc4d336 100644
--- a/src/logicalfile/test/test_basic.py
+++ b/src/logicalfile/test/test_basic.py
@@ -71,7 +71,7 @@ class TestLogicalFile(LogicalFileTestBase):
'CSCreationClassName':self.SYSTEM_CLASS_NAME,
'CSName':self.SYSTEM_NAME,
'FSCreationClassName':'LMI_LocalFileSystem',
- 'FSName':'NotImportant',
+ 'FSName':self.fsname,
'CreationClassName':'LMI_UnixDirectory',
'Name':self.testdir
})
@@ -152,7 +152,7 @@ class TestLogicalFile(LogicalFileTestBase):
'CSCreationClassName':self.SYSTEM_CLASS_NAME,
'CSName':self.SYSTEM_NAME,
'FSCreationClassName':'LMI_LocalFileSystem',
- 'FSName':'NotImportant',
+ 'FSName':self.fsname,
'CreationClassName':f['class'],
'Name':f['path']
})
@@ -179,7 +179,7 @@ class TestLogicalFile(LogicalFileTestBase):
'CSCreationClassName':self.SYSTEM_CLASS_NAME,
'CSName':self.SYSTEM_NAME,
'FSCreationClassName':'LMI_LocalFileSystem',
- 'FSName':'NotImportant',
+ 'FSName':self.fsname,
'CreationClassName':f['class'],
'Name':f['path']
})
@@ -218,7 +218,7 @@ class TestLogicalFile(LogicalFileTestBase):
'CSCreationClassName':self.SYSTEM_CLASS_NAME,
'CSName':self.SYSTEM_NAME,
'FSCreationClassName':'LMI_LocalFileSystem',
- 'FSName':'NotImportant',
+ 'FSName':self.fsname,
'LFCreationClassName':f['class'],
'LFName':f['path']
})
@@ -249,7 +249,7 @@ class TestLogicalFile(LogicalFileTestBase):
'CSCreationClassName':self.SYSTEM_CLASS_NAME,
'CSName':self.SYSTEM_NAME,
'FSCreationClassName':'LMI_LocalFileSystem',
- 'FSName':'NotImportant',
+ 'FSName':self.fsname,
'LFCreationClassName':f['class'],
'LFName':f['path']
})
@@ -320,5 +320,92 @@ class TestLogicalFile(LogicalFileTestBase):
rmdir,
'/cant/remove/me')
+ def _test_missing_or_wrong_properties(self, is_unixfile):
+ testfile = self.files['data']
+ if is_unixfile:
+ prefix = 'LF'
+ clsname = 'LMI_UnixFile'
+ else:
+ prefix = ''
+ clsname = 'LMI_DataFile'
+ cop = pywbem.CIMInstanceName(classname=clsname,
+ namespace='root/cimv2',
+ keybindings={})
+
+ prop = 'CSCreationClassName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ '%s is empty' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = 'BadClass'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ 'Wrong %s' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = self.SYSTEM_CLASS_NAME
+
+ prop = 'CSName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ '%s is empty' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = 'BadClass'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ 'Wrong %s' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = self.SYSTEM_NAME
+
+ prop = prefix + 'CreationClassName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ '%s is empty' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = testfile['class']
+ prop = prefix + 'Name'
+ cop.keybindings[prop] = self.files['dir']['path']
+ if is_unixfile:
+ self.assertRaisesRegexp(pywbem.CIMError,
+ 'LFCreationClassName doesn\'t match',
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = testfile['path']
+
+ prop = 'FSCreationClassName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ '%s is empty' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = 'BadFS'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ 'Wrong %s' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = 'LMI_LocalFileSystem'
+
+ prop = 'FSName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ '%s is empty' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = 'BadFSName'
+ self.assertRaisesRegexp(pywbem.CIMError,
+ 'Wrong %s' % prop,
+ self.wbemconnection.GetInstance,
+ cop)
+ cop.keybindings[prop] = self.fsname
+
+ # finally, test GetInstance on the correct object path
+ try:
+ self.wbemconnection.GetInstance(cop)
+ except pywbem.CIMError as pe:
+ self.fail(pe[1])
+
+ def test_unixfile_missing_or_wrong_properties(self):
+ self._test_missing_or_wrong_properties(True)
+
+ def test_logicalfile_missing_or_wrong_properties(self):
+ self._test_missing_or_wrong_properties(False)
+
if __name__ == '__main__':
unittest.main()
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',
+ ]
+ )