summaryrefslogtreecommitdiffstats
path: root/src/software
diff options
context:
space:
mode:
authorMichal Minar <miminar@redhat.com>2012-10-10 17:55:12 +0200
committerMichal Minar <miminar@redhat.com>2012-10-10 17:55:12 +0200
commit2aa4f8635b13e1849763a1964eb45032ee30948c (patch)
tree3563082116a1a3464a365668505a4db68d55a20a /src/software
parent167c13bdf07c2a86ff7043b4b874073556858e69 (diff)
downloadopenlmi-providers-2aa4f8635b13e1849763a1964eb45032ee30948c.tar.gz
openlmi-providers-2aa4f8635b13e1849763a1964eb45032ee30948c.tar.xz
openlmi-providers-2aa4f8635b13e1849763a1964eb45032ee30948c.zip
renamed yum package providers to software
renamed LMI_Yum* to LMI_Software* removed attributes GroupString and LicenseString from LMI_SoftwarePackage modified attributes Group, Architecture and License of LMI_SoftwarePackage to return string instead of Uints - because values and valuemaps would need frequent updates
Diffstat (limited to 'src/software')
-rw-r--r--src/software/TODO4
-rw-r--r--src/software/providers/LMI_SoftwareFileCheck.py408
-rw-r--r--src/software/providers/LMI_SoftwareInstalledPackage.py475
-rw-r--r--src/software/providers/LMI_SoftwarePackage.py518
-rw-r--r--src/software/providers/LMI_SoftwarePackageFile.py301
-rw-r--r--src/software/providers/__init__.py16
-rw-r--r--src/software/providers/util/__init__.py17
-rw-r--r--src/software/providers/util/common.py804
-rw-r--r--src/software/providers/util/singletonmixin.py508
-rw-r--r--src/software/setup.py16
-rw-r--r--src/software/test/README5
-rw-r--r--src/software/test/common.py109
-rwxr-xr-xsrc/software/test/test_software_file_check.py289
-rwxr-xr-xsrc/software/test/test_software_installed_package.py241
-rwxr-xr-xsrc/software/test/test_software_package.py89
15 files changed, 3800 insertions, 0 deletions
diff --git a/src/software/TODO b/src/software/TODO
new file mode 100644
index 0000000..18c155b
--- /dev/null
+++ b/src/software/TODO
@@ -0,0 +1,4 @@
+provide a way to manage repositories
+associate LMI_YumPackages with each LMI_YumRepository for available packages
+ this will solve enumeration of available packages
+make installation/update/remove asynchronous jobs
diff --git a/src/software/providers/LMI_SoftwareFileCheck.py b/src/software/providers/LMI_SoftwareFileCheck.py
new file mode 100644
index 0000000..0058f78
--- /dev/null
+++ b/src/software/providers/LMI_SoftwareFileCheck.py
@@ -0,0 +1,408 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareFileCheck
+
+Instruments the CIM class LMI_SoftwareFileCheck
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from util.common import *
+
+filecheck2model = SoftwareFileCheck.filecheck_wrapper(
+ 'root/cimv2', 'LMI_SoftwareFileCheck')
+
+class LMI_SoftwareFileCheck(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareFileCheck
+
+ Identifies a file contained by RPM package. It's located in directory
+ identified in FileName. The Invoke methods check for file existence
+ and whether its attributes match those given by RPM package.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ vpkg = SoftwareFileCheck.object_path2yumcheck(env, model.path)
+ pkg = vpkg.po
+ fi = pkg.hdr.fiFromHeader()
+ return filecheck2model(vpkg, model['Name'], env, False)
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+ # TODO create or modify the instance
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+ return instance
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ # TODO delete the resource
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def cim_method_invoke(self, env, object_name):
+ """Implements LMI_SoftwareFileCheck.Invoke()
+
+ The Invoke method evaluates this Check. The details of the
+ evaluation are described by the specific subclasses of CIM_Check.
+ When the SoftwareElement being checked is already installed, the
+ CIM_InstalledSoftwareElement association identifies the
+ CIM_ComputerSystem in whose context the Invoke is executed. If
+ this association is not in place, then the InvokeOnSystem method
+ should be used - since it identifies the TargetSystem as an input
+ parameter of the method. \nThe results of the Invoke method are
+ based on the return value. A zero is returned if the condition is
+ satisfied. A one is returned if the method is not supported. Any
+ other value indicates the condition is not satisfied.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Invoke()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_invoke()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env) as yb:
+ vpkg = SoftwareFileCheck.object_path2yumcheck(env, object_name)
+ fc = SoftwareFileCheck.test_file(env,
+ SoftwareFileCheck.pkg_checksum_type(vpkg.po),
+ vpkg._files[object_name["Name"]])
+ out_params = []
+ ret = 0 if SoftwareFileCheck.filecheck_passed(fc) else 2
+ return (pywbem.Uint32(ret), out_params)
+
+ def cim_method_invokeonsystem(self, env, object_name,
+ param_targetsystem=None):
+ """Implements LMI_SoftwareFileCheck.InvokeOnSystem()
+
+ The InvokeOnSystem method evaluates this Check. The details of the
+ evaluation are described by the specific subclasses of CIM_Check.
+ The method\'s TargetSystem input parameter specifies the
+ ComputerSystem in whose context the method is invoked. \nThe
+ results of the InvokeOnSystem method are based on the return
+ value. A zero is returned if the condition is satisfied. A one is
+ returned if the method is not supported. Any other value indicates
+ the condition is not satisfied.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InvokeOnSystem()
+ should be invoked.
+ param_targetsystem -- The input parameter TargetSystem (
+ type REF (pywbem.CIMInstanceName(
+ classname='CIM_ComputerSystem', ...))
+ Reference to ComputerSystem in whose context the method is to
+ be invoked.
+
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_invokeonsystem()' \
+ % self.__class__.__name__)
+
+ # TODO do something
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented
+ out_params = []
+ #rval = # TODO (type pywbem.Uint32)
+ return (rval, out_params)
+
+ class Values(object):
+ class TargetOperatingSystem(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ MACOS = pywbem.Uint16(2)
+ ATTUNIX = pywbem.Uint16(3)
+ DGUX = pywbem.Uint16(4)
+ DECNT = pywbem.Uint16(5)
+ Tru64_UNIX = pywbem.Uint16(6)
+ OpenVMS = pywbem.Uint16(7)
+ HPUX = pywbem.Uint16(8)
+ AIX = pywbem.Uint16(9)
+ MVS = pywbem.Uint16(10)
+ OS400 = pywbem.Uint16(11)
+ OS_2 = pywbem.Uint16(12)
+ JavaVM = pywbem.Uint16(13)
+ MSDOS = pywbem.Uint16(14)
+ WIN3x = pywbem.Uint16(15)
+ WIN95 = pywbem.Uint16(16)
+ WIN98 = pywbem.Uint16(17)
+ WINNT = pywbem.Uint16(18)
+ WINCE = pywbem.Uint16(19)
+ NCR3000 = pywbem.Uint16(20)
+ NetWare = pywbem.Uint16(21)
+ OSF = pywbem.Uint16(22)
+ DC_OS = pywbem.Uint16(23)
+ Reliant_UNIX = pywbem.Uint16(24)
+ SCO_UnixWare = pywbem.Uint16(25)
+ SCO_OpenServer = pywbem.Uint16(26)
+ Sequent = pywbem.Uint16(27)
+ IRIX = pywbem.Uint16(28)
+ Solaris = pywbem.Uint16(29)
+ SunOS = pywbem.Uint16(30)
+ U6000 = pywbem.Uint16(31)
+ ASERIES = pywbem.Uint16(32)
+ HP_NonStop_OS = pywbem.Uint16(33)
+ HP_NonStop_OSS = pywbem.Uint16(34)
+ BS2000 = pywbem.Uint16(35)
+ LINUX = pywbem.Uint16(36)
+ Lynx = pywbem.Uint16(37)
+ XENIX = pywbem.Uint16(38)
+ VM = pywbem.Uint16(39)
+ Interactive_UNIX = pywbem.Uint16(40)
+ BSDUNIX = pywbem.Uint16(41)
+ FreeBSD = pywbem.Uint16(42)
+ NetBSD = pywbem.Uint16(43)
+ GNU_Hurd = pywbem.Uint16(44)
+ OS9 = pywbem.Uint16(45)
+ MACH_Kernel = pywbem.Uint16(46)
+ Inferno = pywbem.Uint16(47)
+ QNX = pywbem.Uint16(48)
+ EPOC = pywbem.Uint16(49)
+ IxWorks = pywbem.Uint16(50)
+ VxWorks = pywbem.Uint16(51)
+ MiNT = pywbem.Uint16(52)
+ BeOS = pywbem.Uint16(53)
+ HP_MPE = pywbem.Uint16(54)
+ NextStep = pywbem.Uint16(55)
+ PalmPilot = pywbem.Uint16(56)
+ Rhapsody = pywbem.Uint16(57)
+ Windows_2000 = pywbem.Uint16(58)
+ Dedicated = pywbem.Uint16(59)
+ OS_390 = pywbem.Uint16(60)
+ VSE = pywbem.Uint16(61)
+ TPF = pywbem.Uint16(62)
+ Windows__R__Me = pywbem.Uint16(63)
+ Caldera_Open_UNIX = pywbem.Uint16(64)
+ OpenBSD = pywbem.Uint16(65)
+ Not_Applicable = pywbem.Uint16(66)
+ Windows_XP = pywbem.Uint16(67)
+ z_OS = pywbem.Uint16(68)
+ Microsoft_Windows_Server_2003 = pywbem.Uint16(69)
+ Microsoft_Windows_Server_2003_64_Bit = pywbem.Uint16(70)
+ Windows_XP_64_Bit = pywbem.Uint16(71)
+ Windows_XP_Embedded = pywbem.Uint16(72)
+ Windows_Vista = pywbem.Uint16(73)
+ Windows_Vista_64_Bit = pywbem.Uint16(74)
+ Windows_Embedded_for_Point_of_Service = pywbem.Uint16(75)
+ Microsoft_Windows_Server_2008 = pywbem.Uint16(76)
+ Microsoft_Windows_Server_2008_64_Bit = pywbem.Uint16(77)
+ FreeBSD_64_Bit = pywbem.Uint16(78)
+ RedHat_Enterprise_Linux = pywbem.Uint16(79)
+ RedHat_Enterprise_Linux_64_Bit = pywbem.Uint16(80)
+ Solaris_64_Bit = pywbem.Uint16(81)
+ SUSE = pywbem.Uint16(82)
+ SUSE_64_Bit = pywbem.Uint16(83)
+ SLES = pywbem.Uint16(84)
+ SLES_64_Bit = pywbem.Uint16(85)
+ Novell_OES = pywbem.Uint16(86)
+ Novell_Linux_Desktop = pywbem.Uint16(87)
+ Sun_Java_Desktop_System = pywbem.Uint16(88)
+ Mandriva = pywbem.Uint16(89)
+ Mandriva_64_Bit = pywbem.Uint16(90)
+ TurboLinux = pywbem.Uint16(91)
+ TurboLinux_64_Bit = pywbem.Uint16(92)
+ Ubuntu = pywbem.Uint16(93)
+ Ubuntu_64_Bit = pywbem.Uint16(94)
+ Debian = pywbem.Uint16(95)
+ Debian_64_Bit = pywbem.Uint16(96)
+ Linux_2_4_x = pywbem.Uint16(97)
+ Linux_2_4_x_64_Bit = pywbem.Uint16(98)
+ Linux_2_6_x = pywbem.Uint16(99)
+ Linux_2_6_x_64_Bit = pywbem.Uint16(100)
+ Linux_64_Bit = pywbem.Uint16(101)
+ Other_64_Bit = pywbem.Uint16(102)
+ Microsoft_Windows_Server_2008_R2 = pywbem.Uint16(103)
+ VMware_ESXi = pywbem.Uint16(104)
+ Microsoft_Windows_7 = pywbem.Uint16(105)
+ CentOS_32_bit = pywbem.Uint16(106)
+ CentOS_64_bit = pywbem.Uint16(107)
+ Oracle_Enterprise_Linux_32_bit = pywbem.Uint16(108)
+ Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109)
+ eComStation_32_bitx = pywbem.Uint16(110)
+
+ class SoftwareElementState(object):
+ Deployable = pywbem.Uint16(0)
+ Installable = pywbem.Uint16(1)
+ Executable = pywbem.Uint16(2)
+ Running = pywbem.Uint16(3)
+
+ class FileType(object):
+ Unknown = pywbem.Uint16(0)
+ File = pywbem.Uint16(1)
+ Directory = pywbem.Uint16(2)
+ Symlink = pywbem.Uint16(3)
+ FIFO = pywbem.Uint16(4)
+ Character_Device = pywbem.Uint16(5)
+ Block_Device = pywbem.Uint16(6)
+
+## end of class LMI_SoftwareFileCheckProvider
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_softwarefilecheck_prov = LMI_SoftwareFileCheck(env)
+ return {'LMI_SoftwareFileCheck': lmi_softwarefilecheck_prov}
diff --git a/src/software/providers/LMI_SoftwareInstalledPackage.py b/src/software/providers/LMI_SoftwareInstalledPackage.py
new file mode 100644
index 0000000..008a47b
--- /dev/null
+++ b/src/software/providers/LMI_SoftwareInstalledPackage.py
@@ -0,0 +1,475 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwareInstalledPackage
+
+Instruments the CIM class LMI_SoftwareInstalledPackage
+
+"""
+
+import itertools
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from LMI_SoftwarePackage import pkg2model, LMI_SoftwarePackage
+from LMI_SoftwareFileCheck import filecheck2model
+from util.common import *
+
+class LMI_SoftwareInstalledPackage(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstalledPackage
+
+ The InstalledSoftwareElement association allows the identification of
+ the ComputerSystem on which a particular SoftwareElement is installed.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ check_computer_system_op(env, model['System'])
+ with YumDB.getInstance(env):
+ pkg = SoftwarePackage.object_path2pkg(env, model['Software'])
+ model['Software'] = pkg2model(env, pkg, True)
+ return model
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'System': None, 'Software': None})
+
+ yum_package_path = pywbem.CIMInstanceName("LMI_SoftwarePackage",
+ namespace=model.path.namespace,
+ host=model.path.host)
+ model['System'] = get_computer_system_op()
+ with YumDB.getInstance(env) as yb:
+ for rpm in yb.rpmdb:
+ iname = pkg2model(env, rpm, True, yum_package_path)
+ model['Software'] = iname
+ if keys_only:
+ yield model
+ else:
+ try:
+ yield self.get_instance(env, model)
+ except pywbem.CIMError, (num, msg):
+ if num not in (pywbem.CIM_ERR_NOT_FOUND,
+ pywbem.CIM_ERR_ACCESS_DENIED):
+ raise
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+
+ # parse and check arguments
+ if modify_existing is True:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "MofifyInstance is not supported")
+
+ check_computer_system_op(env, instance['System'])
+
+ with YumDB.getInstance(env) as yb:
+ pkg = SoftwarePackage.object_path2pkg_search(env, instance['Software'])
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
+ "Package is already installed.")
+
+ logger.log_info('installing package {}'.format(pkg.nevra))
+ # install
+ yb.install(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package installed'.format(pkg.nevra))
+
+ # return instance
+ pkg_iname = pkg2model(env, pkg, True)
+ pkg_iname["SoftwareElementState"] = \
+ LMI_SoftwarePackage.Values.SoftwareElementState.Executable
+ instance["Software"] = pkg_iname
+ return LMI_SoftwareInstalledPackage(env).get_instance(env, instance)
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ check_computer_system_op(env, instance_name['System'])
+ with YumDB.getInstance(env) as yb:
+ pkg = SoftwarePackage.object_path2pkg(env, instance_name["Software"])
+ logger.log_info('removing package "%s"' % pkg.nevra)
+ yb.remove(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package "%s" removed' % pkg.nevra)
+
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.references()' \
+ % self.__class__.__name__)
+ ch = env.get_cimom_handle()
+
+ # If you want to get references for free, implemented in terms
+ # of enum_instances, just leave the code below unaltered.
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_ComputerSystem') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwarePackage'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
+
+ def cim_method_checkintegrity(self, env, object_name):
+ """Implements LMI_SoftwarePackage.CheckIntegrity()
+
+ Verify existence and attributes of files installed from RPM
+ package. If all files installed exist and their attributes
+ matches, method returns "Pass". "Not passed" is returned when
+ arbitrary file differs in its attributes. And "Error" if
+ verification could not be done.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method CheckIntegrity()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.CheckIntegrity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Failed -- (type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareFileCheck', ...))
+ Array of references to LMI_SoftwareFileCheck, that did not pass
+ verification.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_checkintegrity()' \
+ % self.__class__.__name__)
+
+ failed = []
+ with YumDB.getInstance(env) as yb:
+ pkg = SoftwarePackage.object_path2pkg(env, object_name['Software'])
+ csum = SoftwareFileCheck.pkg_checksum_type(pkg)
+ vpkg = yum.packages._RPMVerifyPackage(
+ pkg, pkg.hdr.fiFromHeader(), csum, [], True)
+ for vpf in vpkg:
+ fc = SoftwareFileCheck.test_file(env, csum, vpf)
+ if SoftwareFileCheck.filecheck_passed(fc): continue
+ failed.append(filecheck2model(
+ vpkg, vpf.filename, env, keys_only=True, fc=fc))
+ out_params = [ pywbem.CIMParameter('Failed', type='reference',
+ value=failed) ]
+ return ( getattr(self.Values.CheckIntegrity,
+ 'Pass' if len(failed) == 0 else 'Not_passed')
+ , out_params )
+
+ def cim_method_update(self, env, object_name,
+ param_epoch=None,
+ param_release=None,
+ param_version=None):
+ """Implements LMI_SoftwareInstalledPackage.Update()
+
+ Updates the package to latest version. When any of "version" or
+ "release" argument is given, install only matching available
+ package. Otherwise try to update to newest package available.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Update()
+ should be invoked.
+ param_release -- The input parameter release (type unicode)
+ Specify particular release of package to update to. Update to
+ newest, when empty
+ param_version -- The input parameter version (type unicode)
+ Specify particular version of package to update to. Update to
+ newest, when empty
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint16 self.Values.Update)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Installed -- (type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareInstalledPackage', ...))
+ The reference to newly installed package, if installation was
+ successful. Otherwise reference to current package.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_update()' \
+ % self.__class__.__name__)
+
+ check_computer_system_op(env, object_name['System'])
+
+ with YumDB.getInstance(env) as yb:
+ orig = SoftwarePackage.object_path2pkg(env, object_name['Software'])
+
+ evr_str = []
+ for name, param in (
+ ('epoch', param_epoch),
+ ('version', param_version),
+ ('release', param_release)):
+ evr_str.append("%s(%s)"%(name,param))
+ if len(evr_str):
+ evr_str = "specific "+'-'.join(evr_str)
+ else:
+ evr_str = "the newest version-release"
+
+ logger.log_info('trying to update to %s of package \"%s\"' %
+ (evr_str, object_name["Software"]["Name"]))
+
+ pl = yb.doPackageLists('all', showdups=True)
+ exact,_,_ = yum.packages.parsePackages(
+ itertools.chain(pl.available, pl.installed),
+ [orig.name])
+ # NOTE: available ∩ installed = ∅
+ exact = sorted(exact, key=lambda a:a.evra)
+
+ try:
+ pkg = [ p for p in exact
+ if (not param_epoch or param_epoch == p.epoch)
+ and (not param_version or param_version == p.ver)
+ and (not param_release or param_release == p.rel) ] [-1]
+ except IndexError:
+ logger.log_error(
+ 'could not find any matching available package')
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND)
+ out_params = [pywbem.CIMParameter('Installed', type='reference',
+ value=pkg2model(env, pkg, True))]
+ if orig.evra == pkg.evra:
+ logger.log_info('already up to date')
+ return (self.Values.Update.Already_newest, out_params)
+
+ yb.update(update_to=True,
+ name=pkg.name,
+ epoch=pkg.epoch,
+ version=pkg.version,
+ release=pkg.release)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package {} updated to: {}'.format(
+ orig.name, pkg.evra))
+
+ out_params[0].value["SoftwareElementState"] = \
+ LMI_SoftwarePackage.Values.SoftwareElementState.Executable
+
+ return (self.Values.Update.Successful_installation, out_params)
+
+ class Values(object):
+ class Update(object):
+ Already_newest = pywbem.Uint16(0)
+ Successful_installation = pywbem.Uint16(1)
+ Failed = pywbem.Uint16(2)
+
+ class CheckIntegrity(object):
+ Pass = pywbem.Uint32(0)
+ Not_passed = pywbem.Uint32(1)
+ Error = pywbem.Uint32(2)
+
+## end of class LMI_SoftwareInstalledPackage
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_softwareinstalledpackage_prov = LMI_SoftwareInstalledPackage(env)
+ return {'LMI_SoftwareInstalledPackage': lmi_softwareinstalledpackage_prov}
diff --git a/src/software/providers/LMI_SoftwarePackage.py b/src/software/providers/LMI_SoftwarePackage.py
new file mode 100644
index 0000000..9ef3266
--- /dev/null
+++ b/src/software/providers/LMI_SoftwarePackage.py
@@ -0,0 +1,518 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwarePackage
+
+Instruments the CIM class LMI_SoftwarePackage
+
+"""
+
+import itertools
+import datetime
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from util.common import *
+
+pkg2model = SoftwarePackage.pkg2model_wrapper('root/cimv2', "LMI_SoftwarePackage")
+
+class LMI_SoftwarePackage(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwarePackage
+
+ RPM package installed on particular computer system with YUM (The
+ Yellowdog Updated, Modified) package manager.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env):
+ pkg = SoftwarePackage.object_path2pkg(env, model.path, 'all')
+ return pkg2model(env, pkg, keys_only=False, model=model)
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'TargetOperatingSystem': None, 'Version': None,
+ 'SoftwareElementState': None, 'Name': None,
+ 'SoftwareElementID': None})
+
+ with YumDB.getInstance(env) as yb:
+ # get all packages
+ pl = yb.doPackageLists('all', showdups=True)
+ pl = itertools.chain(pl.installed, pl.available)
+ # NOTE: available ∩ installed = ∅
+ for pkg in sorted(pl, key=lambda a:a.evra):
+ yield pkg2model(env, pkg, keys_only, model)
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+ # TODO create or modify the instance
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+ return instance
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ # TODO delete the resource
+ # Remove to implement
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ def cim_method_install(self, env, object_name):
+ """Implements LMI_SoftwarePackage.Install()
+
+ Will install available package.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Update()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.Install)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Installed -- (type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareInstalledPackage', ...))
+ The reference to newly installed package, if installation was
+ successful. Null otherwise.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_install()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env) as yb:
+ # get available packages
+ pkg = SoftwarePackage.object_path2pkg_search(env, object_name)
+ out_params = [ pywbem.CIMParameter('Installed', type='reference') ]
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ out_params[0].value = pkg2model(env, pkg, True)
+ return (self.Values.Install.Already_installed, out_params)
+
+ logger.log_info('installing package {}'.format(pkg.nevra))
+ # install
+ yb.install(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package installed'.format(pkg.nevra))
+
+ out_params[0].value = pkg2model(env, pkg, True, object_name)
+ out_params[0].value['SoftwareElementState'] = \
+ self.Values.SoftwareElementState.Executable
+ return (self.Values.Install.Successful_installation, out_params)
+
+ def cim_method_remove(self, env, object_name):
+ """Implements LMI_SoftwarePackage.Remove()
+
+ Will uninstall installed package.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Remove()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.Remove)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_remove()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env) as yb:
+ pkg = SoftwarePackage.object_path2pkg(env, object_name, 'all')
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ logger.log_info('removing package "%s"' % pkg.nevra)
+ yb.remove(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package "%s" removed' % pkg.nevra)
+ rval = self.Values.Remove.Successful_removal
+ else:
+ rval = self.Values.Remove.Not_installed
+ #rval = # TODO (type pywbem.Uint32 self.Values.Remove)
+ return (rval, [])
+
+ class Values(object):
+ class DetailedStatus(object):
+ Not_Available = pywbem.Uint16(0)
+ No_Additional_Information = pywbem.Uint16(1)
+ Stressed = pywbem.Uint16(2)
+ Predictive_Failure = pywbem.Uint16(3)
+ Non_Recoverable_Error = pywbem.Uint16(4)
+ Supporting_Entity_in_Error = pywbem.Uint16(5)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class Status(object):
+ OK = 'OK'
+ Error = 'Error'
+ Degraded = 'Degraded'
+ Unknown = 'Unknown'
+ Pred_Fail = 'Pred Fail'
+ Starting = 'Starting'
+ Stopping = 'Stopping'
+ Service = 'Service'
+ Stressed = 'Stressed'
+ NonRecover = 'NonRecover'
+ No_Contact = 'No Contact'
+ Lost_Comm = 'Lost Comm'
+ Stopped = 'Stopped'
+
+ class HealthState(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(5)
+ Degraded_Warning = pywbem.Uint16(10)
+ Minor_failure = pywbem.Uint16(15)
+ Major_failure = pywbem.Uint16(20)
+ Critical_failure = pywbem.Uint16(25)
+ Non_recoverable_error = pywbem.Uint16(30)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class TargetOperatingSystem(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ MACOS = pywbem.Uint16(2)
+ ATTUNIX = pywbem.Uint16(3)
+ DGUX = pywbem.Uint16(4)
+ DECNT = pywbem.Uint16(5)
+ Tru64_UNIX = pywbem.Uint16(6)
+ OpenVMS = pywbem.Uint16(7)
+ HPUX = pywbem.Uint16(8)
+ AIX = pywbem.Uint16(9)
+ MVS = pywbem.Uint16(10)
+ OS400 = pywbem.Uint16(11)
+ OS_2 = pywbem.Uint16(12)
+ JavaVM = pywbem.Uint16(13)
+ MSDOS = pywbem.Uint16(14)
+ WIN3x = pywbem.Uint16(15)
+ WIN95 = pywbem.Uint16(16)
+ WIN98 = pywbem.Uint16(17)
+ WINNT = pywbem.Uint16(18)
+ WINCE = pywbem.Uint16(19)
+ NCR3000 = pywbem.Uint16(20)
+ NetWare = pywbem.Uint16(21)
+ OSF = pywbem.Uint16(22)
+ DC_OS = pywbem.Uint16(23)
+ Reliant_UNIX = pywbem.Uint16(24)
+ SCO_UnixWare = pywbem.Uint16(25)
+ SCO_OpenServer = pywbem.Uint16(26)
+ Sequent = pywbem.Uint16(27)
+ IRIX = pywbem.Uint16(28)
+ Solaris = pywbem.Uint16(29)
+ SunOS = pywbem.Uint16(30)
+ U6000 = pywbem.Uint16(31)
+ ASERIES = pywbem.Uint16(32)
+ HP_NonStop_OS = pywbem.Uint16(33)
+ HP_NonStop_OSS = pywbem.Uint16(34)
+ BS2000 = pywbem.Uint16(35)
+ LINUX = pywbem.Uint16(36)
+ Lynx = pywbem.Uint16(37)
+ XENIX = pywbem.Uint16(38)
+ VM = pywbem.Uint16(39)
+ Interactive_UNIX = pywbem.Uint16(40)
+ BSDUNIX = pywbem.Uint16(41)
+ FreeBSD = pywbem.Uint16(42)
+ NetBSD = pywbem.Uint16(43)
+ GNU_Hurd = pywbem.Uint16(44)
+ OS9 = pywbem.Uint16(45)
+ MACH_Kernel = pywbem.Uint16(46)
+ Inferno = pywbem.Uint16(47)
+ QNX = pywbem.Uint16(48)
+ EPOC = pywbem.Uint16(49)
+ IxWorks = pywbem.Uint16(50)
+ VxWorks = pywbem.Uint16(51)
+ MiNT = pywbem.Uint16(52)
+ BeOS = pywbem.Uint16(53)
+ HP_MPE = pywbem.Uint16(54)
+ NextStep = pywbem.Uint16(55)
+ PalmPilot = pywbem.Uint16(56)
+ Rhapsody = pywbem.Uint16(57)
+ Windows_2000 = pywbem.Uint16(58)
+ Dedicated = pywbem.Uint16(59)
+ OS_390 = pywbem.Uint16(60)
+ VSE = pywbem.Uint16(61)
+ TPF = pywbem.Uint16(62)
+ Windows__R__Me = pywbem.Uint16(63)
+ Caldera_Open_UNIX = pywbem.Uint16(64)
+ OpenBSD = pywbem.Uint16(65)
+ Not_Applicable = pywbem.Uint16(66)
+ Windows_XP = pywbem.Uint16(67)
+ z_OS = pywbem.Uint16(68)
+ Microsoft_Windows_Server_2003 = pywbem.Uint16(69)
+ Microsoft_Windows_Server_2003_64_Bit = pywbem.Uint16(70)
+ Windows_XP_64_Bit = pywbem.Uint16(71)
+ Windows_XP_Embedded = pywbem.Uint16(72)
+ Windows_Vista = pywbem.Uint16(73)
+ Windows_Vista_64_Bit = pywbem.Uint16(74)
+ Windows_Embedded_for_Point_of_Service = pywbem.Uint16(75)
+ Microsoft_Windows_Server_2008 = pywbem.Uint16(76)
+ Microsoft_Windows_Server_2008_64_Bit = pywbem.Uint16(77)
+ FreeBSD_64_Bit = pywbem.Uint16(78)
+ RedHat_Enterprise_Linux = pywbem.Uint16(79)
+ RedHat_Enterprise_Linux_64_Bit = pywbem.Uint16(80)
+ Solaris_64_Bit = pywbem.Uint16(81)
+ SUSE = pywbem.Uint16(82)
+ SUSE_64_Bit = pywbem.Uint16(83)
+ SLES = pywbem.Uint16(84)
+ SLES_64_Bit = pywbem.Uint16(85)
+ Novell_OES = pywbem.Uint16(86)
+ Novell_Linux_Desktop = pywbem.Uint16(87)
+ Sun_Java_Desktop_System = pywbem.Uint16(88)
+ Mandriva = pywbem.Uint16(89)
+ Mandriva_64_Bit = pywbem.Uint16(90)
+ TurboLinux = pywbem.Uint16(91)
+ TurboLinux_64_Bit = pywbem.Uint16(92)
+ Ubuntu = pywbem.Uint16(93)
+ Ubuntu_64_Bit = pywbem.Uint16(94)
+ Debian = pywbem.Uint16(95)
+ Debian_64_Bit = pywbem.Uint16(96)
+ Linux_2_4_x = pywbem.Uint16(97)
+ Linux_2_4_x_64_Bit = pywbem.Uint16(98)
+ Linux_2_6_x = pywbem.Uint16(99)
+ Linux_2_6_x_64_Bit = pywbem.Uint16(100)
+ Linux_64_Bit = pywbem.Uint16(101)
+ Other_64_Bit = pywbem.Uint16(102)
+ Microsoft_Windows_Server_2008_R2 = pywbem.Uint16(103)
+ VMware_ESXi = pywbem.Uint16(104)
+ Microsoft_Windows_7 = pywbem.Uint16(105)
+ CentOS_32_bit = pywbem.Uint16(106)
+ CentOS_64_bit = pywbem.Uint16(107)
+ Oracle_Enterprise_Linux_32_bit = pywbem.Uint16(108)
+ Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109)
+ eComStation_32_bitx = pywbem.Uint16(110)
+
+ class Remove(object):
+ Not_installed = pywbem.Uint32(0)
+ Successful_removal = pywbem.Uint32(1)
+ Failed = pywbem.Uint32(2)
+
+ class CommunicationStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Communication_OK = pywbem.Uint16(2)
+ Lost_Communication = pywbem.Uint16(3)
+ No_Contact = pywbem.Uint16(4)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class OperationalStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ OK = pywbem.Uint16(2)
+ Degraded = pywbem.Uint16(3)
+ Stressed = pywbem.Uint16(4)
+ Predictive_Failure = pywbem.Uint16(5)
+ Error = pywbem.Uint16(6)
+ Non_Recoverable_Error = pywbem.Uint16(7)
+ Starting = pywbem.Uint16(8)
+ Stopping = pywbem.Uint16(9)
+ Stopped = pywbem.Uint16(10)
+ In_Service = pywbem.Uint16(11)
+ No_Contact = pywbem.Uint16(12)
+ Lost_Communication = pywbem.Uint16(13)
+ Aborted = pywbem.Uint16(14)
+ Dormant = pywbem.Uint16(15)
+ Supporting_Entity_in_Error = pywbem.Uint16(16)
+ Completed = pywbem.Uint16(17)
+ Power_Mode = pywbem.Uint16(18)
+ Relocating = pywbem.Uint16(19)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class OperatingStatus(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Available = pywbem.Uint16(1)
+ Servicing = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Stopping = pywbem.Uint16(4)
+ Stopped = pywbem.Uint16(5)
+ Aborted = pywbem.Uint16(6)
+ Dormant = pywbem.Uint16(7)
+ Completed = pywbem.Uint16(8)
+ Migrating = pywbem.Uint16(9)
+ Emigrating = pywbem.Uint16(10)
+ Immigrating = pywbem.Uint16(11)
+ Snapshotting = pywbem.Uint16(12)
+ Shutting_Down = pywbem.Uint16(13)
+ In_Test = pywbem.Uint16(14)
+ Transitioning = pywbem.Uint16(15)
+ In_Service = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class SoftwareElementState(object):
+ Deployable = pywbem.Uint16(0)
+ Installable = pywbem.Uint16(1)
+ Executable = pywbem.Uint16(2)
+ Running = pywbem.Uint16(3)
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ class Install(object):
+ Already_installed = pywbem.Uint32(0)
+ Successful_installation = pywbem.Uint32(1)
+ Failed = pywbem.Uint32(2)
+
+## end of class LMI_SoftwarePackage
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_softwarepackage_prov = LMI_SoftwarePackage(env)
+ return {'LMI_SoftwarePackage': lmi_softwarepackage_prov}
diff --git a/src/software/providers/LMI_SoftwarePackageFile.py b/src/software/providers/LMI_SoftwarePackageFile.py
new file mode 100644
index 0000000..389dbbf
--- /dev/null
+++ b/src/software/providers/LMI_SoftwarePackageFile.py
@@ -0,0 +1,301 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_SoftwarePackageFile
+
+Instruments the CIM class LMI_SoftwarePackageFile
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from LMI_SoftwareFileCheck import filecheck2model
+from LMI_SoftwarePackage import pkg2model
+from util.common import *
+
+class LMI_SoftwarePackageFile(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwarePackageFile
+
+ This association ties a SoftwareElement to a specific Check to validate
+ its state or its movement to the next state. Note that
+ SoftwareElements in a running state cannot transition to another
+ state. Therefore, the value of the Phase property is restricted to 0
+ ("In-State") for SoftwareElements in the running state.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ vpkg = SoftwareFileCheck.object_path2yumcheck(env, model['Check'])
+ model['Check'] = filecheck2model(vpkg, model['Check']['Name'],
+ env, keys_only=True)
+ model['Element'] = pkg2model(env, vpkg.po, keys_only=True)
+ return model
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'Check': None, 'Element': None})
+
+ while False: # TODO more instances?
+ # TODO fetch system resource
+ # Key properties
+ #model['Check'] = pywbem.CIMInstanceName(classname='LMI_SoftwareFileCheck', ...) # TODO (type = REF (pywbem.CIMInstanceName(classname='LMI_SoftwareFileCheck', ...))
+ #model['Element'] = pywbem.CIMInstanceName(classname='LMI_SoftwarePackage', ...) # TODO (type = REF (pywbem.CIMInstanceName(classname='LMI_SoftwarePackage', ...))
+ if keys_only:
+ yield model
+ else:
+ try:
+ yield self.get_instance(env, model)
+ except pywbem.CIMError, (num, msg):
+ if num not in (pywbem.CIM_ERR_NOT_FOUND,
+ pywbem.CIM_ERR_ACCESS_DENIED):
+ raise
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+ # TODO create or modify the instance
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+ return instance
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ # TODO delete the resource
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.references()' \
+ % self.__class__.__name__)
+ ch = env.get_cimom_handle()
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'Check': None, 'Element': None})
+
+ with YumDB.getInstance(env):
+ if ( (not role or role.lower() == 'element')
+ and ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwarePackage')):
+ filecheck_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareFileCheck',
+ namespace="root/cimv2",
+ host=model.path.host)
+ model['Element'] = object_name
+
+ pkg = SoftwarePackage.object_path2pkg(env, object_name)
+ vpkg = yum.packages._RPMVerifyPackage(
+ pkg, pkg.hdr.fiFromHeader(),
+ SoftwareFileCheck.pkg_checksum_type(pkg), [], True)
+ for fc in vpkg:
+ model['Check'] = filecheck2model(
+ vpkg, fc.filename, env, keys_only=True,
+ model=filecheck_model)
+ yield model
+
+ if ( (not role or role.lower() == 'check')
+ and ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareFileCheck')):
+ model['Check'] = object_name
+
+ vpkg = SoftwareFileCheck.object_path2yumcheck(env, object_name)
+ model['Element'] = pkg2model(
+ env, vpkg.po, keys_only=True)
+ yield model
+
+ class Values(object):
+ class Phase(object):
+ In_State = pywbem.Uint16(0)
+ Next_State = pywbem.Uint16(1)
+
+## end of class LMI_SoftwarePackageFileProvider
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_softwarepackagefile_prov = LMI_SoftwarePackageFile(env)
+ return {'LMI_SoftwarePackageFile': lmi_softwarepackagefile_prov}
diff --git a/src/software/providers/__init__.py b/src/software/providers/__init__.py
new file mode 100644
index 0000000..452bf25
--- /dev/null
+++ b/src/software/providers/__init__.py
@@ -0,0 +1,16 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/src/software/providers/util/__init__.py b/src/software/providers/util/__init__.py
new file mode 100644
index 0000000..fddeea6
--- /dev/null
+++ b/src/software/providers/util/__init__.py
@@ -0,0 +1,17 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
diff --git a/src/software/providers/util/common.py b/src/software/providers/util/common.py
new file mode 100644
index 0000000..4190d41
--- /dev/null
+++ b/src/software/providers/util/common.py
@@ -0,0 +1,804 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Common utilities for LMI_Software* providers
+"""
+
+import collections
+from datetime import datetime
+import grp
+import hashlib
+import itertools
+import os
+import platform
+import pwd
+import re
+import rpm
+import socket
+import stat
+import pywbem
+import yum
+import cmpi_pywbem_bindings as pycimmb
+import singletonmixin
+
+re_evra = re.compile(r'^(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+)$')
+re_nevra = re.compile(r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+
+rpmdb_path = '/var/lib/rpm/Packages'
+
+class YumDB(singletonmixin.Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, env, *args, **kwargs):
+ if not isinstance(env, pycimmb.ProviderEnvironment):
+ raise TypeError("env must be instance of"
+ " pycimmb.ProviderEnvironment")
+ self._yum_args = (args, kwargs)
+ self._yum = None
+ self._db_mtime = 0
+ self._lock_cnt = 0
+ self.env = env
+ env.get_logger().log_info('init called')
+
+ def is_dirty(self):
+ return self._db_mtime < os.stat(rpmdb_path).st_mtime
+
+ def is_locked(self):
+ return self._yum._lockfile is not None
+
+ def update_db(self):
+ self.env.get_logger().log_info('updating rpmdb')
+ self._db_mtime = os.stat(rpmdb_path).st_mtime
+ self._yum.doConfigSetup()
+ self._yum.doTsSetup()
+ self._yum.doRpmDBSetup()
+
+ def __enter__(self):
+ self._lock_cnt += 1
+ if self._lock_cnt < 2:
+ self._yum = yum.YumBase(*self._yum_args[0], **self._yum_args[1])
+ if not self.is_locked() and self.is_dirty():
+ self.update_db()
+ self._yum.doLock()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if self._lock_cnt == 1:
+ del self._yum
+ self._lock_cnt -= 1
+
+ def __getattr__(self, name):
+ if not self.is_locked() and self.is_dirty():
+ self.update_db()
+ return getattr(self._yum, name)
+
+
+def _get_distname():
+ if hasattr(platform, 'linux_distribution'):
+ return platform.linux_distribution(
+ full_distribution_name=False)[0].lower()
+ else:
+ return platform.dist()[0].lower()
+
+
+def get_target_operating_system():
+ """
+ @return (val, text).
+ Where val is a number from ValueMap of TargetOperatingSystem property
+ of CIM_SoftwareElement class and text is its testual representation.
+ """
+
+ system = platform.system()
+ if system.lower() == 'linux':
+ try:
+ val, dist = \
+ { 'redhat' : (79, 'RedHat Enterprise Linux')
+ , 'suse' : (81, 'SUSE')
+ , 'mandriva' : (88, 'Mandriva')
+ , 'ubuntu' : (93, 'Ubuntu')
+ , 'debian' : (95, 'Debian')
+ }[_get_distname()]
+ except KeyError:
+ linrel = platform.uname()[2]
+ if linrel.startswith('2.4'):
+ val, dist = (97, 'Linux 2.4.x')
+ elif linrel.startswith('2.6'):
+ val, dist = (99, 'Linux 2.6.x')
+ else:
+ return (36, 'LINUX') # no check for x86_64
+ if platform.machine() == 'x86_64':
+ val += 1
+ dist += ' 64-Bit'
+ return (val, dist)
+ elif system.lower() in ('macosx', 'darwin'):
+ return (2, 'MACOS')
+ # elif system.lower() == 'windows': # no general value
+ else:
+ return (0, 'Unknown')
+
+def get_computer_system_op():
+ return pywbem.CIMInstanceName(
+ classname='CIM_ComputerSystem',
+ keybindings={
+ "CreationClassName": "CIM_ComputerSystem"
+ , "Name" : socket.gethostname() },
+ namespace="root/cimv2")
+
+def check_target_operating_system(system):
+ """
+ @return if param system matches current target operating system
+ """
+ if isinstance(system, basestring):
+ system = int(system)
+ if not isinstance(system, (int, long)):
+ raise TypeError("system must be either string or integer, not {}"
+ .format(system.__class__.__name__))
+ tos = get_target_operating_system()
+ if system == tos: return True
+ if system == 36: # linux
+ if platform.system().lower() == "linux": return True
+ if ( system >= 97 and system <= 100 # linux 2.x.x
+ and platform.uname()[2].startswith('2.4' if system < 99 else '2.6')
+ # check machine
+ and ( bool(platform.machine().endswith('64'))
+ == bool(not (system % 2)))):
+ return True
+ return False
+
+def check_computer_system_op(env, system):
+ if not isinstance(system, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"System\" must be a CIMInstanceName")
+ our_system = get_computer_system_op()
+ ch = env.get_cimom_handle()
+ if not ch.is_subclass(system.namespace,
+ sub=system.classname,
+ super=our_system.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"System\" must be a sublass of %s" %
+ our_system.classname)
+ if not 'CreationClassName' in system or not 'Name' in system:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"System\" is missing one of keys")
+ if not ch.is_subclass(system.namespace,
+ sub=system['CreationClassName'],
+ super=our_system.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "CreationClassName of \"System\" must be a sublass of %s" %
+ our_system.classname)
+ if system['Name'] != our_system['Name']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Name of \"System\" does not match \"%s\"" %
+ our_system['Name'])
+ return True
+
+
+def match_pkg(pkg, **kwargs):
+ """
+ all not Null and not empty arguments will be matched against pkg
+ attributes; if all of them match, return True
+
+ possible arguments:
+ name, epoch, version, release, arch
+ evra, nevra
+ """
+ for a in ('evra', 'nevra', 'name', 'epoch', 'version', 'release', 'arch'):
+ v = kwargs.get(a, None)
+ if v and getattr(pkg, a) != v:
+ return False
+ return True
+
+class SoftwarePackage:
+ """
+ Just a namespace for common function related to SoftwarePackage provider.
+ """
+
+ @staticmethod
+ def parse_group(group, env):
+ try:
+ res = \
+ { "amusements/games" : 3
+ , "amusements/graphics" : 4
+ , "applications/archiving" : 5
+ , "applications/communications" : 6
+ , "applications/databases" : 7
+ , "applications/editors" : 8
+ , "applications/emulators" : 9
+ , "applications/engineering" : 10
+ , "applications/file" : 11
+ , "applications/internet" : 12
+ , "applications/multimedia" : 13
+ , "applications/productivity" : 14
+ , "applications/publishing" : 15
+ , "applications/system" : 16
+ , "applications/text" : 17
+ , "development/build tools" : 18
+ , "development/debug" : 19
+ , "development/debuggers" : 20
+ , "development/documentation" : 21
+ , "development/java" : 22
+ , "development/languages" : 23
+ , "development/libraries" : 24
+ , "development/libraries/java" : 25
+ , "development/system" : 26
+ , "development/tools" : 27
+ , "documentation" : 28
+ , "internet/www/dynamic content" : 29
+ , "system/libraries" : 30
+ , "system environment/base" : 31
+ , "system environment/daemons" : 32
+ , "system environment/kernel" : 33
+ , "system environment/libraries" : 34
+ , "system environment/shells" : 35
+ , "text processing/markup/xml" : 36
+ , "user interface/desktops" : 37
+ , "user interface/x" : 38
+ , "user interface/x hardware support" : 39
+ , "utilities" : 40
+ }[group.lower()]
+ except KeyError:
+ logger = env.get_logger()
+ if not group or group.lower() == "unspecified":
+ logger.log_info("unspecified group '{}'".format(group))
+ res = 2
+ else:
+ logger.log_error("failed to parse group '{}'".format(group))
+ res = 0
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def object_path2pkg(env, op, package_list='installed'):
+ """
+ @param op must contain precise information of package,
+ otherwise a CIM_ERR_NOT_FOUND error is raised
+ @param package_list one of {'installed', 'all', 'available'}
+ says, where to look for given package
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be an instance of CIMInstanceName")
+ if not isinstance(package_list, basestring):
+ raise TypeError("package_list must be a string")
+ if not package_list in ('installed', 'all', 'available'):
+ raise ValueError('unsupported package list "%s"'%package_list)
+
+ tos = get_target_operating_system()[0]
+ if ( not op['Name'] or not op['SoftwareElementID']
+ or not op['SoftwareElementID'].startswith(op['Name'])
+ or op['SoftwareElementID'].find(op['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+# if op['SoftwareElementState'] not in ("2", 2):
+# raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+# "Only \"Executable\" software element state supported")
+ if not check_target_operating_system(op['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not op['Name'] or not op['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ m = re_nevra.match(op['SoftwareElementID'])
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-[epoch:]version-release.arch).")
+ if op['Version'] != m.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+ evra = "{}:{}-{}.{}".format(*(
+ (m.group(k) if k != "epoch" or m.group(k) else "0")
+ for k in ("epoch", 'ver', 'rel', 'arch')))
+ with YumDB.getInstance(env) as yb:
+ pl = yb.doPackageLists(package_list,
+ showdups=package_list != 'installed')
+ if package_list != 'all':
+ pl = getattr(pl, package_list)
+ else:
+ # NOTE: available ∩ installed = ∅
+ pl = itertools.chain(pl.available, pl.installed)
+ exact,_,_ = yum.packages.parsePackages(pl, [op['Name']])
+ for pkg in yum.misc.unique(exact):
+ if pkg.evra == evra: break
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package found.")
+ return pkg
+
+ @staticmethod
+ def object_path2pkg_search(env, op):
+ """
+ similar to object_path2pkg, but tries to find best suitable
+ package matching keys
+
+ If any matching package is already installed, it is returned.
+ Otherwise available package with highest version is returned.
+
+ @param op may be object of CIMInstance or CIMInstanceName and
+ must contain at least \"Name\" or \"SoftwareElementID\"
+ @return instance of yum.rpmsack.RPMInstalledPackage in case of
+ installed package, otherwise yum.packages.YumAvailablePackage
+ """
+ logger = env.get_logger()
+ if isinstance(op, pywbem.CIMInstance):
+ def _get_key(k):
+ v = op.properties.get(k, None)
+ if isinstance(v, pywbem.CIMProperty): return v.value
+ if v is not None: return v
+ logger.log_error('missing key "{}" in inst.props'.format(k))
+ return op.path[k] if k in op.path else None
+ elif isinstance(op, pywbem.CIMInstanceName):
+ _get_key = lambda k: op[k] if k in op else None
+ else:
+ raise TypeError("op must be either CIMInstance or CIMInstanceName")
+
+ # parse and check arguments
+ match_props = {} # args for match_pkg
+ if _get_key('SoftwareElementID'):
+ m = re_nevra.match(_get_key('SoftwareElementID'))
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "SoftwareElementID could not be parsed.")
+ match_props['nevra'] = _get_key('SoftwareElementID')
+ match_props['name'] = m.group('name')
+ else:
+ for matchattr, instattr in (
+ ('name', 'name'), ('epoch', 'epoch'), ('version', 'ver'),
+ ('release', 'rel'), ('arch', 'arch')):
+ if _get_key(matchattr):
+ match_props[matchattr] = _get_key(matchattr)
+
+ if not match_props:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Too few key values given (give at least a Name).")
+ if not 'name' in match_props and not 'nevra' in match_props:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Missing either Name or SoftwareElementID property.")
+
+ # get available packages
+ pl = YumDB.getInstance(env).doPackageLists('all', showdups=True)
+ # NOTE: available ∩ installed = ∅
+ exact,_,_ = yum.packages.parsePackages(
+ itertools.chain(pl.available, pl.installed),
+ [match_props['name']])
+ exact = yum.misc.unique(exact)
+ exact_orig = exact
+ exact = sorted( [ p for p in exact if match_pkg(p, **match_props) ]
+ , key=lambda a: a.evra)
+ if len(exact) == 0:
+ logger.log_error('could not find any package for query: {}'
+ ' in list: {}'
+ .format(match_props, [p.nevra for p in exact_orig]))
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package found.")
+ for pkg in exact: # check, whether package is already installed
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ return pkg
+ logger.log_info(('found multiple matching packages'
+ ' for query: {}' if len(exact) > 1 else
+ 'exact match found for query: {}')
+ .format(match_props))
+ return exact[-1] # select highest version
+
+ @staticmethod
+ def pkg2model_wrapper(namespace, classname):
+ """
+ @return a function that transforms YumAvailablePackage object
+ to CIMInstanceName object
+ """
+
+ tos = pywbem.Uint16(get_target_operating_system()[0])
+
+ def pkg2model(env, pkg, keys_only=True, model=None):
+ """
+ @param model if None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ #if not isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ #if not isinstance(pkg, yum.packages.YumHeaderPackage):
+ if not isinstance(pkg, yum.packages.YumAvailablePackage):
+ raise TypeError(
+ "pkg must be an instance of YumAvailablePackage")
+ if model is None:
+ model = pywbem.CIMInstanceName(classname, namespace=namespace)
+ if not keys_only:
+ model = pywbem.CIMInstance(classname, path=model)
+ if isinstance(model, pywbem.CIMInstance):
+ def _set_key(k, v):
+ model[k] = v
+ model.path[k] = v
+ else:
+ _set_key = model.__setitem__
+ with YumDB.getInstance(env):
+ _set_key('Name', pkg.name)
+ _set_key('SoftwareElementID', pkg.nevra)
+ _set_key('SoftwareElementState', pywbem.Uint16(2
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage)
+ else 1))
+ _set_key('TargetOperatingSystem', tos)
+ _set_key('Version', pkg.version)
+ if not keys_only:
+ model['Caption'] = pkg.summary
+ model['Description'] = pkg.description
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ model['InstallDate'] = pywbem.CIMDateTime(
+ datetime.fromtimestamp(pkg.installtime))
+ if pkg.vendor:
+ model['Manufacturer'] = pkg.vendor
+ model['Release'] = pkg.release
+ model['Epoch'] = pywbem.Uint16(pkg.epoch)
+ model["Architecture"] = pkg.arch
+ model['License'] = pkg.license
+ model['Group'] = pkg.group
+ model['Size'] = pywbem.Uint64(pkg.size)
+ return model
+
+ return pkg2model
+
+class SoftwareFileCheck:
+ """
+ Just a namespace for functions related to FileCheck provider.
+ """
+
+ passed_flags_descriptions = (
+ "Existence",
+ "File Type",
+ "File Size",
+ "File Mode",
+ "Checksum",
+ "Device major/minor number",
+ "Symlink Target",
+ "User Ownership", "Group Ownership",
+ "Modify Time")
+
+ checksumtype_str2num = dict((v, k) for (k, v) in
+ yum.constants.RPM_CHECKSUM_TYPES.items())
+
+ @staticmethod
+ def pkg_checksum_type(pkg):
+ """
+ @return integer representation of checksum type
+ """
+ if not isinstance(pkg, yum.packages.YumAvailablePackage):
+ raise TypeError("pkg must be an instance of YumAvailablePackage")
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ return pkg.hdr[rpm.RPMTAG_FILEDIGESTALGO]
+ with self: # ensure, that _yum is inited
+ return SoftwareFileCheck.checksumtype_str2pywbem(
+ pkg.yumdb_info.checksum_type)
+
+ @staticmethod
+ def checksumtype_num2hash(csumt):
+ return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
+
+ @staticmethod
+ def checksumtype_str2pywbem(alg):
+ try:
+ res = SoftwareFileCheck.checksumtype_str2num[alg.lower()]
+ except KeyError: res = 0
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def filetype_str2pywbem(ft):
+ try:
+ return pywbem.Uint16(
+ { 'file' : 1
+ , 'directory' : 2
+ , 'symlink' : 3
+ , 'fifo' : 4
+ , 'character device' : 5
+ , 'block device' : 6
+ }[ft])
+ except KeyError:
+ return pywbem.Uint16(0)
+
+ @staticmethod
+ def filetype_mode2pywbem(mode):
+ for i, name in enumerate(
+ ('REG', 'DIR', 'LNK', 'FIFO', 'CHR', 'BLK'), 1):
+ if getattr(stat, 'S_IS' + name)(mode):
+ return pywbem.Uint16(i)
+ return pywbem.Uint16(0)
+
+ @staticmethod
+ def mode2pywbem_flags(mode):
+ """
+ @param mode if None, file does not exist
+ """
+ if mode is None: return None
+ flags = []
+ for i, c in enumerate((
+ stat.S_IXOTH,
+ stat.S_IWOTH,
+ stat.S_IROTH,
+ stat.S_IXGRP,
+ stat.S_IWGRP,
+ stat.S_IRGRP,
+ stat.S_IXUSR,
+ stat.S_IWUSR,
+ stat.S_IRUSR,
+ stat.S_ISVTX,
+ stat.S_ISGID,
+ stat.S_ISUID)):
+ if c & mode:
+ flags.append(pywbem.Uint8(i))
+ return flags
+
+ @staticmethod
+ def hashfile(afile, hashers, blocksize=65536):
+ """
+ @param hashers is a list of hash objects
+ @return list of digest strings (in hex format) for each hash object
+ given in the same order
+ """
+ if not isinstance(hashers, (tuple, list, set, frozenset)):
+ hashers = (hashers, )
+ buf = afile.read(blocksize)
+ while len(buf) > 0:
+ for h in hashers: h.update(buf)
+ buf = afile.read(blocksize)
+ return [ h.hexdigest() for h in hashers ]
+
+ @staticmethod
+ def compute_checksums(env, checksum_type, file_type, file_path):
+ """
+ @param file_type is not a file, then zeroes are returned
+ @param checksum_type selected hash algorithm to compute second
+ checksum
+ @return (md5sum, checksum)
+ both checksums are computed from file_path's content
+ first one is always md5, the second one depends on checksum_type
+ if file does not exists, (None, None) is returned
+ """
+ hashers = [hashlib.md5()]
+ if checksum_type != SoftwareFileCheck.checksumtype_str2num["md5"]:
+ hashers.append(SoftwareFileCheck.checksumtype_num2hash(checksum_type)())
+ if file_type != SoftwareFileCheck.filetype_str2pywbem('file'):
+ rslts = ['0'*len(h.hexdigest()) for h in hashers]
+ else:
+ try:
+ with open(file_path, 'rb') as f:
+ rslts = SoftwareFileCheck.hashfile(f, hashers)
+ except (OSError, IOError) as e:
+ env.get_logger().log_error("could not open file \"%s\""
+ " for reading: %s" % (file_path, e))
+ return None, None
+ return (rslts[0], rslts[1] if len(rslts) > 1 else rslts[0]*2)
+
+ @staticmethod
+ def object_path2yumcheck(env, op):
+ """
+ @return instance of yum.packages._RPMVerifyPackage
+ this object holds RPMInstalledPackage under its po attribute
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be instance of CIMInstanceName, "
+ "not \"%s\"" % op.__class__.__name__)
+ log = env.get_logger()
+
+ tos = get_target_operating_system()[0]
+ if ( not op['Name'] or not op['SoftwareElementID']
+ or not op['CheckID']
+ or not op['CheckID'].endswith('#'+op['Name'])
+ or op['SoftwareElementID'].find(op['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+ if op['SoftwareElementState'] not in ("2", 2):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Only \"Executable\" software element state supported")
+ if not check_target_operating_system(op['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not op['Name'] or not op['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ m = re_nevra.match(op['SoftwareElementID'])
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-epoch:version-release.arch).")
+ if op['Version'] != m.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+
+ evra = "{}:{}-{}.{}".format(*(
+ (m.group(k) if k != "epoch" or m.group(k) else "0")
+ for k in ("epoch", 'ver', 'rel', 'arch')))
+ with YumDB.getInstance(env) as yb:
+ pl = yb.doPackageLists('installed')
+ exact, matched, unmatched = yum.packages.parsePackages(
+ pl.installed, [m.group('name')])
+ exact = yum.misc.unique(exact)
+ for pkg in exact:
+ if pkg.evra == evra: break
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package installed.")
+
+ fi = pkg.hdr.fiFromHeader()
+ vpkg = yum.packages._RPMVerifyPackage(pkg, fi,
+ SoftwareFileCheck.pkg_checksum_type(pkg), [], True)
+ if not op['Name'] in vpkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "File not found in RPM package.")
+ return vpkg
+
+ """
+ Named tuple to store results of rpm file check as pywbem values,
+ all results are in form:
+ (expected, reality)
+ where
+ expected is value from rpm package
+ reality is value obtained from installed file
+ None means, that value could not be obtained
+ except for "exists" and "md5_checksum" attributes, where "exists"
+ is boolean and md5_checksum is a string
+ for example:
+ file_check.file_type == (4, 3)
+ """
+ FileCheck = collections.namedtuple('FileCheck',
+ 'exists, md5_checksum, file_type, file_size, file_mode, '
+ 'checksum, device, link_target, user_id, group_id, '
+ 'last_modification_time')
+
+ @staticmethod
+ def test_file(env, checksum_type, vpf):
+ """
+ @param checksum type is a pywbem value for ChecksumType property
+ @return instance of FileCheck
+ """
+ if not isinstance(vpf, yum.packages._RPMVerifyPackageFile):
+ raise TypeError("vpf must be an instance of _RPMVerifyPackage,"
+ " not \"%s\"" % vpf.__class__.__name__)
+ exists = os.path.lexists(vpf.filename)
+ md5_checksum = None
+ expected = {
+ "file_type" : SoftwareFileCheck.filetype_str2pywbem(vpf.ftype),
+ "user_id" : pywbem.Uint32(pwd.getpwnam(vpf.user).pw_uid),
+ "group_id" : pywbem.Uint32(grp.getgrnam(vpf.group).gr_gid),
+ "file_mode" : pywbem.Uint32(vpf.mode),
+ "file_size" : pywbem.Uint64(vpf.size),
+ "link_target" : vpf.readlink if vpf.readlink else None,
+ "checksum" : vpf.digest[1],
+ "device" : (pywbem.Uint64(vpf.dev)
+ if vpf.ftype.endswith('device') else None),
+ "last_modification_time" : pywbem.Uint64(vpf.mtime)
+ }
+ if not exists:
+ reality = collections.defaultdict(lambda: None)
+ else:
+ fstat = os.lstat(vpf.filename)
+ reality = {
+ "file_type" : SoftwareFileCheck.filetype_mode2pywbem(fstat.st_mode),
+ "user_id" : pywbem.Uint32(fstat.st_uid),
+ "group_id" : pywbem.Uint32(fstat.st_gid),
+ "file_mode" : pywbem.Uint32(fstat.st_mode),
+ "file_size" : pywbem.Uint64(fstat.st_size),
+ "last_modification_time" : pywbem.Uint64(fstat.st_mtime)
+ }
+ reality["device"] = (pywbem.Uint64(fstat.st_dev)
+ if reality['file_type'] == SoftwareFileCheck.filetype_str2pywbem(
+ "device") else None)
+ reality["link_target"] = (os.readlink(vpf.filename)
+ if os.path.islink(vpf.filename) else None)
+ md5_checksum, checksum = SoftwareFileCheck.compute_checksums(
+ env, checksum_type, reality["file_type"], vpf.filename)
+ reality["checksum"] = checksum
+ kwargs = dict(exists=exists, md5_checksum=md5_checksum,
+ **dict((k, (expected[k], reality[k])) for k in expected))
+ return SoftwareFileCheck.FileCheck(**kwargs)
+
+ @staticmethod
+ def filecheck_passed(fc):
+ if not isinstance(fc, SoftwareFileCheck.FileCheck):
+ raise TypeError("fc must be an instance of FileCheck")
+ return ( fc.exists
+ and all( v[0] == v[1]
+ for k, v in fc._asdict().items() if (
+ isinstance(v, tuple)
+ and ( k != "last_modification_time"
+ or fc.file_type[0] == \
+ SoftwareFileCheck.filetype_str2pywbem("file")
+ ))))
+
+ @staticmethod
+ def filecheck_wrapper(namespace, classname):
+
+ def _filecheck2model_flags(fc):
+ flags = []
+ for k, v in fc._asdict().items():
+ if isinstance(v, tuple):
+ if ( k != "last_modification_time"
+ or fc.file_type[0] == SoftwareFileCheck.filetype_str2pywbem(
+ 'file')):
+ # last_modification_time check is valid only for
+ # regular files
+ flag = fc.exists and v[0] == v[1]
+ else:
+ flag = True
+ flags.append(flag)
+ elif isinstance(v, bool):
+ flags.append(v)
+ return flags
+
+ def filecheck2model(vpkg, fn, env, keys_only=True,
+ model=None, fc=None):
+ if not isinstance(vpkg, yum.packages._RPMVerifyPackage):
+ raise TypeError(
+ "vpkg must be an instance of _RPMVerifyPackage")
+ if not fn in vpkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "File \"%s\" not found among package files" % fn)
+ if model is None:
+ model = pywbem.CIMInstanceName(classname, namespace=namespace)
+ if not keys_only:
+ model = pywbem.CIMInstance(classname, path=model)
+ if fc is not None:
+ if not isinstance(fc, SoftwareFileCheck.FileCheck):
+ raise TypeError("fc must be an instance of FileCheck")
+ pkg = vpkg.po
+ vpf = vpkg._files[fn]
+ model['Name'] = vpf.filename
+ model['SoftwareElementID'] = pkg.nevra
+ model['SoftwareElementState'] = pywbem.Uint16(2)
+ model['TargetOperatingSystem'] = pywbem.Uint16(
+ get_target_operating_system()[0])
+ model['Version'] = pkg.version
+ model['CheckID'] = '%s#%s' % (pkg.name, vpf.filename)
+ if not keys_only:
+ #model['Caption'] = '' # TODO
+ #model['CheckMode'] = bool() # TODO
+ #model['CRC1'] = pywbem.Uint32() # TODO
+ #model['CRC2'] = pywbem.Uint32() # TODO
+ #model['CreateTimeStamp'] = pywbem.CIMDateTime() # TODO
+ #model['Description'] = '' # TODO
+ #model['ElementName'] = '' # TODO
+ #model['InstanceID'] = '' # TODO
+ model['FileName'] = os.path.basename(vpf.filename)
+ model['ChecksumType'] = csumt = \
+ pywbem.Uint16(SoftwareFileCheck.pkg_checksum_type(pkg))
+ if fc is None:
+ fc = SoftwareFileCheck.test_file(env, csumt, vpf)
+ for mattr, fattr in (
+ ('FileType', 'file_type'),
+ ('FileUserID', 'user_id'),
+ ('FileGroupID', 'group_id'),
+ ('FileMode', 'file_mode'),
+ ('LastModificationTime', 'last_modification_time'),
+ ('FileSize', 'file_size'),
+ ('LinkTarget', 'link_target'),
+ ('Checksum', 'checksum')):
+ exp, rea = getattr(fc, fattr)
+ if exp is not None:
+ model['Expected' + mattr] = exp
+ if rea is not None:
+ model[mattr] = rea
+ model['ExpectedFileModeFlags'] = \
+ SoftwareFileCheck.mode2pywbem_flags(fc.file_mode[0])
+ if fc.exists:
+ model['FileModeFlags'] = SoftwareFileCheck.mode2pywbem_flags(
+ fc.file_mode[1])
+ model['FileExists'] = fc.exists
+ if fc.md5_checksum is not None:
+ model['MD5Checksum'] = fc.md5_checksum
+ model['PassedFlags'] = _filecheck2model_flags(fc)
+ model['PassedFlagsDescriptions'] = list(
+ SoftwareFileCheck.passed_flags_descriptions)
+ return model
+
+ return filecheck2model
+
diff --git a/src/software/providers/util/singletonmixin.py b/src/software/providers/util/singletonmixin.py
new file mode 100644
index 0000000..8051695
--- /dev/null
+++ b/src/software/providers/util/singletonmixin.py
@@ -0,0 +1,508 @@
+"""
+A Python Singleton mixin class that makes use of some of the ideas
+found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
+from it and you have a singleton. No code is required in
+subclasses to create singleton behavior -- inheritance from
+Singleton is all that is needed.
+
+Singleton creation is threadsafe.
+
+USAGE:
+
+Just inherit from Singleton. If you need a constructor, include
+an __init__() method in your class as you usually would. However,
+if your class is S, you instantiate the singleton using S.getInstance()
+instead of S(). Repeated calls to S.getInstance() return the
+originally-created instance.
+
+For example:
+
+class S(Singleton):
+
+ def __init__(self, a, b=1):
+ pass
+
+S1 = S.getInstance(1, b=3)
+
+
+Most of the time, that's all you need to know. However, there are some
+other useful behaviors. Read on for a full description:
+
+1) Getting the singleton:
+
+ S.getInstance()
+
+returns the instance of S. If none exists, it is created.
+
+2) The usual idiom to construct an instance by calling the class, i.e.
+
+ S()
+
+is disabled for the sake of clarity.
+
+For one thing, the S() syntax means instantiation, but getInstance()
+usually does not cause instantiation. So the S() syntax would
+be misleading.
+
+Because of that, if S() were allowed, a programmer who didn't
+happen to notice the inheritance from Singleton (or who
+wasn't fully aware of what a Singleton pattern
+does) might think he was creating a new instance,
+which could lead to very unexpected behavior.
+
+So, overall, it is felt that it is better to make things clearer
+by requiring the call of a class method that is defined in
+Singleton. An attempt to instantiate via S() will result
+in a SingletonException being raised.
+
+3) Use __S.__init__() for instantiation processing,
+since S.getInstance() runs S.__init__(), passing it the args it has received.
+
+If no data needs to be passed in at instantiation time, you don't need S.__init__().
+
+4) If S.__init__(.) requires parameters, include them ONLY in the
+first call to S.getInstance(). If subsequent calls have arguments,
+a SingletonException is raised by default.
+
+If you find it more convenient for subsequent calls to be allowed to
+have arguments, but for those argumentsto be ignored, just include
+'ignoreSubsequent = True' in your class definition, i.e.:
+
+ class S(Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, a, b=1):
+ pass
+
+5) For testing, it is sometimes convenient for all existing singleton
+instances to be forgotten, so that new instantiations can occur. For that
+reason, a forgetAllSingletons() function is included. Just call
+
+ forgetAllSingletons()
+
+and it is as if no earlier instantiations have occurred.
+
+6) As an implementation detail, classes that inherit
+from Singleton may not have their own __new__
+methods. To make sure this requirement is followed,
+an exception is raised if a Singleton subclass includ
+es __new__. This happens at subclass instantiation
+time (by means of the MetaSingleton metaclass.
+
+
+By Gary Robinson, grobinson@flyfi.com. No rights reserved --
+placed in the public domain -- which is only reasonable considering
+how much it owes to other people's code and ideas which are in the
+public domain. The idea of using a metaclass came from
+a comment on Gary's blog (see
+http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
+Other improvements came from comments and email from other
+people who saw it online. (See the blog post and comments
+for further credits.)
+
+Not guaranteed to be fit for any particular purpose. Use at your
+own risk.
+"""
+
+import threading
+
+class SingletonException(Exception):
+ pass
+
+_stSingletons = set()
+_lockForSingletons = threading.RLock()
+_lockForSingletonCreation = threading.RLock() # Ensure only one instance of each Singleton
+ # class is created. This is not bound to the
+ # individual Singleton class since we need to
+ # ensure that there is only one mutex for each
+ # Singleton class, which would require having
+ # a lock when setting up the Singleton class,
+ # which is what this is anyway. So, when any
+ # Singleton is created, we lock this lock and
+ # then we don't need to lock it again for that
+ # class.
+
+def _createSingletonInstance(cls, lstArgs, dctKwArgs):
+ _lockForSingletonCreation.acquire()
+ try:
+ if cls._isInstantiated(): # some other thread got here first
+ return
+
+ instance = cls.__new__(cls)
+ try:
+ instance.__init__(*lstArgs, **dctKwArgs)
+ except TypeError, e:
+ if e.message.find('__init__() takes') != -1:
+ raise SingletonException, 'If the singleton requires __init__ args, supply them on first call to getInstance().'
+ else:
+ raise
+ cls.cInstance = instance
+ _addSingleton(cls)
+ finally:
+ _lockForSingletonCreation.release()
+
+def _addSingleton(cls):
+ _lockForSingletons.acquire()
+ try:
+ assert cls not in _stSingletons
+ _stSingletons.add(cls)
+ finally:
+ _lockForSingletons.release()
+
+def _removeSingleton(cls):
+ _lockForSingletons.acquire()
+ try:
+ if cls in _stSingletons:
+ _stSingletons.remove(cls)
+ finally:
+ _lockForSingletons.release()
+
+def forgetAllSingletons():
+ '''This is useful in tests, since it is hard to know which singletons need to be cleared to make a test work.'''
+ _lockForSingletons.acquire()
+ try:
+ for cls in _stSingletons.copy():
+ cls._forgetClassInstanceReferenceForTesting()
+
+ # Might have created some Singletons in the process of tearing down.
+ # Try one more time - there should be a limit to this.
+ iNumSingletons = len(_stSingletons)
+ if len(_stSingletons) > 0:
+ for cls in _stSingletons.copy():
+ cls._forgetClassInstanceReferenceForTesting()
+ iNumSingletons -= 1
+ assert iNumSingletons == len(_stSingletons), 'Added a singleton while destroying ' + str(cls)
+ assert len(_stSingletons) == 0, _stSingletons
+ finally:
+ _lockForSingletons.release()
+
+class MetaSingleton(type):
+ def __new__(metaclass, strName, tupBases, dct):
+ if dct.has_key('__new__'):
+ raise SingletonException, 'Can not override __new__ in a Singleton'
+ return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
+
+ def __call__(cls, *lstArgs, **dictArgs):
+ raise SingletonException, 'Singletons may only be instantiated through getInstance()'
+
+class Singleton(object):
+ __metaclass__ = MetaSingleton
+
+ def getInstance(cls, *lstArgs, **dctKwArgs):
+ """
+ Call this to instantiate an instance or retrieve the existing instance.
+ If the singleton requires args to be instantiated, include them the first
+ time you call getInstance.
+ """
+ if cls._isInstantiated():
+ if (lstArgs or dctKwArgs) and not hasattr(cls, 'ignoreSubsequent'):
+ raise SingletonException, 'Singleton already instantiated, but getInstance() called with args.'
+ else:
+ _createSingletonInstance(cls, lstArgs, dctKwArgs)
+
+ return cls.cInstance
+ getInstance = classmethod(getInstance)
+
+ def _isInstantiated(cls):
+ # Don't use hasattr(cls, 'cInstance'), because that screws things up if there is a singleton that
+ # extends another singleton. hasattr looks in the base class if it doesn't find in subclass.
+ return 'cInstance' in cls.__dict__
+ _isInstantiated = classmethod(_isInstantiated)
+
+ # This can be handy for public use also
+ isInstantiated = _isInstantiated
+
+ def _forgetClassInstanceReferenceForTesting(cls):
+ """
+ This is designed for convenience in testing -- sometimes you
+ want to get rid of a singleton during test code to see what
+ happens when you call getInstance() under a new situation.
+
+ To really delete the object, all external references to it
+ also need to be deleted.
+ """
+ try:
+ if hasattr(cls.cInstance, '_prepareToForgetSingleton'):
+ # tell instance to release anything it might be holding onto.
+ cls.cInstance._prepareToForgetSingleton()
+ del cls.cInstance
+ _removeSingleton(cls)
+ except AttributeError:
+ # run up the chain of base classes until we find the one that has the instance
+ # and then delete it there
+ for baseClass in cls.__bases__:
+ if issubclass(baseClass, Singleton):
+ baseClass._forgetClassInstanceReferenceForTesting()
+ _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
+
+
+if __name__ == '__main__':
+
+ import unittest
+ import time
+
+ class singletonmixin_Public_TestCase(unittest.TestCase):
+ def testReturnsSameObject(self):
+ """
+ Demonstrates normal use -- just call getInstance and it returns a singleton instance
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ a1 = A.getInstance()
+ a2 = A.getInstance()
+ self.assertEquals(id(a1), id(a2))
+
+ def testInstantiateWithMultiArgConstructor(self):
+ """
+ If the singleton needs args to construct, include them in the first
+ call to get instances.
+ """
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ b1 = B.getInstance('arg1 value', 'arg2 value')
+ b2 = B.getInstance()
+ self.assertEquals(b1.arg1, 'arg1 value')
+ self.assertEquals(b1.arg2, 'arg2 value')
+ self.assertEquals(id(b1), id(b2))
+
+ def testInstantiateWithKeywordArg(self):
+
+ class B(Singleton):
+
+ def __init__(self, arg1=5):
+ super(B, self).__init__()
+ self.arg1 = arg1
+
+ b1 = B.getInstance('arg1 value')
+ b2 = B.getInstance()
+ self.assertEquals(b1.arg1, 'arg1 value')
+ self.assertEquals(id(b1), id(b2))
+
+ def testTryToInstantiateWithoutNeededArgs(self):
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ self.assertRaises(SingletonException, B.getInstance)
+
+ def testPassTypeErrorIfAllArgsThere(self):
+ """
+ Make sure the test for capturing missing args doesn't interfere with a normal TypeError.
+ """
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+ raise TypeError, 'some type error'
+
+ self.assertRaises(TypeError, B.getInstance, 1, 2)
+
+ def testTryToInstantiateWithoutGetInstance(self):
+ """
+ Demonstrates that singletons can ONLY be instantiated through
+ getInstance, as long as they call Singleton.__init__ during construction.
+
+ If this check is not required, you don't need to call Singleton.__init__().
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ self.assertRaises(SingletonException, A)
+
+ def testDontAllowNew(self):
+
+ def instantiatedAnIllegalClass():
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ def __new__(metaclass, strName, tupBases, dct):
+ return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
+
+ self.assertRaises(SingletonException, instantiatedAnIllegalClass)
+
+
+ def testDontAllowArgsAfterConstruction(self):
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ B.getInstance('arg1 value', 'arg2 value')
+ self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
+
+ def test_forgetClassInstanceReferenceForTesting(self):
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+ class B(A):
+ def __init__(self):
+ super(B, self).__init__()
+
+ # check that changing the class after forgetting the instance produces
+ # an instance of the new class
+ a = A.getInstance()
+ assert a.__class__.__name__ == 'A'
+ A._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ # check that invoking the 'forget' on a subclass still deletes the instance
+ B._forgetClassInstanceReferenceForTesting()
+ a = A.getInstance()
+ B._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ def test_forgetAllSingletons(self):
+ # Should work if there are no singletons
+ forgetAllSingletons()
+
+ class A(Singleton):
+ ciInitCount = 0
+ def __init__(self):
+ super(A, self).__init__()
+ A.ciInitCount += 1
+
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 1)
+
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 1)
+
+ forgetAllSingletons()
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 2)
+
+ def test_threadedCreation(self):
+ # Check that only one Singleton is created even if multiple
+ # threads try at the same time. If fails, would see assert in _addSingleton
+ class Test_Singleton(Singleton):
+ def __init__(self):
+ super(Test_Singleton, self).__init__()
+
+ class Test_SingletonThread(threading.Thread):
+ def __init__(self, fTargetTime):
+ super(Test_SingletonThread, self).__init__()
+ self._fTargetTime = fTargetTime
+ self._eException = None
+
+ def run(self):
+ try:
+ fSleepTime = self._fTargetTime - time.time()
+ if fSleepTime > 0:
+ time.sleep(fSleepTime)
+ Test_Singleton.getInstance()
+ except Exception, e:
+ self._eException = e
+
+ fTargetTime = time.time() + 0.1
+ lstThreads = []
+ for _ in xrange(100):
+ t = Test_SingletonThread(fTargetTime)
+ t.start()
+ lstThreads.append(t)
+ eException = None
+ for t in lstThreads:
+ t.join()
+ if t._eException and not eException:
+ eException = t._eException
+ if eException:
+ raise eException
+
+ def testNoInit(self):
+ """
+ Demonstrates use with a class not defining __init__
+ """
+
+ class A(Singleton):
+ pass
+
+ #INTENTIONALLY UNDEFINED:
+ #def __init__(self):
+ # super(A, self).__init__()
+
+ A.getInstance() #Make sure no exception is raised
+
+ def testMultipleGetInstancesWithArgs(self):
+
+ class A(Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, a, b=1):
+ pass
+
+ a1 = A.getInstance(1)
+ a2 = A.getInstance(2) # ignores the second call because of ignoreSubsequent
+
+ class B(Singleton):
+
+ def __init__(self, a, b=1):
+ pass
+
+ b1 = B.getInstance(1)
+ self.assertRaises(SingletonException, B.getInstance, 2) # No ignoreSubsequent included
+
+ class C(Singleton):
+
+ def __init__(self, a=1):
+ pass
+
+ c1 = C.getInstance(a=1)
+ self.assertRaises(SingletonException, C.getInstance, a=2) # No ignoreSubsequent included
+
+ def testInheritance(self):
+ """
+ It's sometimes said that you can't subclass a singleton (see, for instance,
+ http://steve.yegge.googlepages.com/singleton-considered-stupid point e). This
+ test shows that at least rudimentary subclassing works fine for us.
+ """
+
+ class A(Singleton):
+
+ def setX(self, x):
+ self.x = x
+
+ def setZ(self, z):
+ raise NotImplementedError
+
+ class B(A):
+
+ def setX(self, x):
+ self.x = -x
+
+ def setY(self, y):
+ self.y = y
+
+ a = A.getInstance()
+ a.setX(5)
+ b = B.getInstance()
+ b.setX(5)
+ b.setY(50)
+ self.assertEqual((a.x, b.x, b.y), (5, -5, 50))
+ self.assertRaises(AttributeError, eval, 'a.setY', {}, locals())
+ self.assertRaises(NotImplementedError, b.setZ, 500)
+
+ unittest.main()
+
diff --git a/src/software/setup.py b/src/software/setup.py
new file mode 100644
index 0000000..4823b59
--- /dev/null
+++ b/src/software/setup.py
@@ -0,0 +1,16 @@
+from distutils.core import setup
+setup(
+ name='cura-yum',
+ description='Software Management providers',
+ author='Michal Minar',
+ author_email='miminar@redhat.com',
+ url='https://fedorahosted.org/cura/',
+ version='0.3',
+ package_dir={'cura.yum': 'providers'},
+ packages=['cura.yum', 'cura.yum.util'],
+ classifiers=[
+ 'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
+ 'Operating System :: POSIX :: Linux',
+ 'Topic :: System :: Systems Administration',
+ ]
+ )
diff --git a/src/software/test/README b/src/software/test/README
new file mode 100644
index 0000000..d60d1e8
--- /dev/null
+++ b/src/software/test/README
@@ -0,0 +1,5 @@
+Tests must be run as root on machine with sfcbd broker with
+test dir as CWD. File common.py contains connection settings and
+also test data.
+This directory should contain all rpm packages, that will take part in testing
+to speed it up. These packages are listed in common.py file.
diff --git a/src/software/test/common.py b/src/software/test/common.py
new file mode 100644
index 0000000..d88b2bb
--- /dev/null
+++ b/src/software/test/common.py
@@ -0,0 +1,109 @@
+from collections import namedtuple
+import os
+import pywbem
+import re
+import subprocess
+import unittest
+
+SCHEMA="http"
+HOSTNAME="localhost"
+PORT=5988
+USER=''
+PASSWORD=''
+
+Package = namedtuple("Package",
+ "name, epoch, ver, rel, arch, "
+ "updatable, up_epoch, up_ver, up_rel")
+# This is a database of packages used in tests. If you modify this, please
+# ensure, that all corresponding rpm packages are present in test directory.
+packages = [ Package(*args) for args in
+ ( ( "python-xlib-doc", "0", "0.15", "0.6.rc1.fc17", "noarch", False
+ , None, None, None)
+ , ( "slv2", "0", "0.6.6", "8.fc17", "x86_64", True
+ , "0", "0.6.6", "9.fc17")
+ ) ]
+
+pkg_files = (
+ ( "bash-completion-1:2.0-1.fc17.noarch",
+ ( "/usr/share/bash-completion/completions/vgs" # symlink
+ , "/usr/share/doc/bash-completion-2.0/README" # file
+ , "/usr/share/doc/bash-completion-2.0" # directory
+ )
+ , )
+ , )
+
+re_nevra = re.compile(r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+
+def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
+ """
+ @param with_epoch may be one of:
+ "NOT_ZERO" - include epoch only if it's not zero
+ "ALWAYS" - include epoch always
+ "NEVER" - do not include epoch at all
+ """
+ estr = ''
+ if with_epoch.lower() == "always":
+ estr = epoch
+ elif with_epoch.lower() == "not_zero":
+ if epoch != "0":
+ estr = epoch
+ if len(estr): estr += ":"
+ return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
+
+def remove_pkg(name, *args):
+ subprocess.call(["rpm", "--quiet"] + list(args) + ["-e", name])
+
+def install_pkg(*args, **kwargs):
+ if len(args) > 1 or len(kwargs) > 0:
+ if kwargs.has_key('nevra'):
+ m = re_nevra.match(kwargs['nevra'])
+ args = [ m.group(k) for k in (
+ 'name', 'epoch', 'ver', 'rel', 'arch') ]
+ nevra = make_nevra(*args, with_epoch="NEVER")
+ rpm_name = nevra + ".rpm"
+ if os.path.exists(rpm_name):
+ subprocess.call(["rpm", "--quiet", "-i", rpm_name])
+ return
+ subprocess.call(["yum", "-q", "-y", "install", args[0]])
+
+def is_installed(*args, **kwargs):
+ if len(args) == 1:
+ return subprocess.call(["rpm", "--quiet", "-q", args[0]]) == 0
+ else:
+ if kwargs.has_key("nevra"):
+ nevra = kwargs["nevra"]
+ name = re_nevra.match(nevra).group('name')
+ else:
+ nevra = make_nevra(*args)
+ name = args[0]
+ try:
+ out = subprocess.check_output(
+ ["rpm", "-q", "--qf", "%{NEVRA}", name])
+ return out == nevra
+ except subprocess.CalledProcessError:
+ return False
+
+def verify_pkg(name):
+ return subprocess.call(["rpm", "--quiet", "-Va", name]) == 0
+
+class SoftwareBaseTestCase(unittest.TestCase):
+
+ def setUp(self):
+ unittest.TestCase.setUp(self)
+ self.url = "%s://%s:%d" % (SCHEMA, HOSTNAME, PORT)
+ self.conn = pywbem.WBEMConnection(self.url, (USER, PASSWORD))
+ self.op = pywbem.CIMInstanceName(
+ namespace="root/cimv2", classname=self.CLASS_NAME)
+
+ def tearDown(self):
+ del self.conn
+
+ def assertIsSubclass(self, cls, base_cls):
+ if not isinstance(cls, basestring):
+ raise TypeError("cls must be a string")
+ if not isinstance(base_cls, basestring):
+ raise TypeError("base_cls must be a string")
+ return self.assertTrue(pywbem.is_subclass(self.conn,
+ "root/cimv2", base_cls, cls))
+
diff --git a/src/software/test/test_software_file_check.py b/src/software/test/test_software_file_check.py
new file mode 100755
index 0000000..66b2452
--- /dev/null
+++ b/src/software/test/test_software_file_check.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python
+
+from common import *
+import hashlib
+import shutil
+import socket
+import stat
+
+re_checksum = re.compile(r'^([0-9a-fA-F]+)\s+.*')
+
+PassedFlags = namedtuple("PassedFlags",
+ "exists, type, size, mode, checksum, dev, ltarget, uid, gid, mtime")
+
+class TestSoftwareInstalledPackage(SoftwareBaseTestCase):
+
+ CLASS_NAME = "LMI_SoftwareFileCheck"
+ KEYS = ( "CheckID", "Name", "SoftwareElementID", "SoftwareElementState"
+ , "TargetOperatingSystem", "Version")
+
+ hash_num2algo = {
+ 1 : hashlib.md5,
+ 2 : hashlib.sha1,
+ 8 : hashlib.sha256,
+ 9 : hashlib.sha384,
+ 10 : hashlib.sha512,
+ 11 : hashlib.sha224
+ }
+
+ hash_num2cmd = {
+ 1 : "md5sum",
+ 2 : "sha1sum",
+ 8 : "sha256sum",
+ 9 : "sha384sum",
+ 10 : "sha512sum",
+ 11 : "sha224sum"
+ }
+
+ hash_num2length = {
+ 1 : 32,
+ 2 : 40,
+ 8 : 64,
+ 9 : 96,
+ 10 : 128,
+ 11 : 56
+ }
+
+ def make_op(self, nevra, filename):
+ op = self.op.copy()
+ m = re_nevra.match(nevra)
+ name, version = [ m.group(k) for k in ("name", "ver") ]
+ op["Name"] = filename
+ op["Version"] = version
+ op["CheckID"] = "%s#%s" % (name, filename)
+ op["SoftwareElementState"] = pywbem.Uint16(2)
+ op["TargetOperatingSystem"] = pywbem.Uint16(36)
+ op["SoftwareElementID"] = nevra
+ return op
+
+ def make_checksum_str(self, csumnum, filename):
+ return re_checksum.match(subprocess.check_output([
+ self.hash_num2cmd[csumnum], filename])).group(1).lower()
+
+ def do_test_symlink(self, op, nevra, f, inst):
+ target = os.readlink(f)
+ stats = os.lstat(f)
+
+ self.assertEqual(inst["FileType"], pywbem.Uint16(3))
+ self.assertEqual(inst["FileUserID"], stats.st_uid)
+ self.assertEqual(inst["FileGroupID"], stats.st_gid)
+ self.assertEqual(inst["FileMode"], stats.st_mode)
+ self.assertEqual(inst["FileSize"], stats.st_size)
+ self.assertEqual(inst["LinkTarget"], target)
+ self.assertEqual(inst["Checksum"],
+ "0"*self.hash_num2length[inst["ChecksumType"]])
+ self.assertEqual(inst["LastModificationTime"],
+ int(stats.st_mtime))
+
+ # modify owner
+ prev_user = inst["FileUserID"]
+ prev_mtime = inst["LastModificationTime"]
+ prev_pflags = PassedFlags(*inst["PassedFlags"])
+ os.lchown(f, stats.st_uid + 1, -1)
+ inst = self.conn.GetInstance(InstanceName=inst.path)
+ self.assertEqual(inst["FileUserID"], inst["ExpectedFileUserID"] + 1)
+ self.assertEqual(inst["FileUserID"], prev_user + 1)
+ self.assertEqual(inst["FileGroupID"], stats.st_gid)
+ cur_pflags = PassedFlags(*inst["PassedFlags"])
+ #self.assertGreater(inst["LastModificationTime"], prev_mtime)
+
+ self.assertTrue(cur_pflags.exists)
+ self.assertTrue(cur_pflags.type)
+ self.assertTrue(cur_pflags.size)
+ self.assertTrue(cur_pflags.mode)
+ self.assertTrue(cur_pflags.checksum)
+ self.assertTrue(cur_pflags.dev)
+ self.assertTrue(cur_pflags.ltarget)
+ self.assertFalse(cur_pflags.uid)
+ self.assertTrue(cur_pflags.gid)
+ self.assertTrue(cur_pflags.mtime)
+
+ # modify link_target
+ os.remove(f)
+ os.symlink("wrong" + "*"*len(inst["ExpectedLinkTarget"]), f)
+ os.lchown(f, inst["ExpectedFileUserID"], inst["ExpectedFileGroupID"])
+
+ inst = self.conn.GetInstance(InstanceName=inst.path)
+ cur_pflags = PassedFlags(*inst["PassedFlags"])
+ self.assertGreater(len(inst["LinkTarget"]),
+ len(inst["ExpectedLinkTarget"]))
+
+ self.assertTrue(cur_pflags.exists)
+ self.assertTrue(cur_pflags.type)
+ self.assertFalse(cur_pflags.size)
+ self.assertTrue(cur_pflags.mode)
+ self.assertTrue(cur_pflags.checksum)
+ self.assertTrue(cur_pflags.dev)
+ self.assertFalse(cur_pflags.ltarget)
+ self.assertTrue(cur_pflags.uid)
+ self.assertTrue(cur_pflags.gid)
+ self.assertTrue(cur_pflags.mtime)
+
+ def do_test_directory(self, op, nevra, f, inst):
+ stats = os.lstat(f)
+
+ self.assertEqual(inst["FileType"], pywbem.Uint16(2))
+ self.assertEqual(inst["FileUserID"], stats.st_uid)
+ self.assertEqual(inst["FileGroupID"], stats.st_gid)
+ self.assertEqual(inst["FileMode"], stats.st_mode)
+ self.assertEqual(inst["FileSize"], stats.st_size)
+ self.assertIs(inst["LinkTarget"], None)
+ self.assertEqual(inst["Checksum"],
+ "0"*self.hash_num2length[inst["ChecksumType"]])
+ self.assertEqual(inst["LastModificationTime"],
+ int(stats.st_mtime))
+
+ def do_test_file(self, op, nevra, f, inst):
+ stats = os.lstat(f)
+
+ self.assertEqual(inst["FileType"], pywbem.Uint16(1))
+ self.assertEqual(inst["FileUserID"], stats.st_uid)
+ self.assertEqual(inst["FileGroupID"], stats.st_gid)
+ self.assertEqual(inst["FileMode"], stats.st_mode)
+ self.assertEqual(inst["FileSize"], stats.st_size)
+ self.assertIs(inst["LinkTarget"], None)
+ csum = self.make_checksum_str(inst['ChecksumType'], f)
+ self.assertEqual(inst["Checksum"].lower(), csum)
+ self.assertEqual(inst["ExpectedLastModificationTime"],
+ inst["LastModificationTime"])
+ self.assertEqual(inst["LastModificationTime"],
+ int(stats.st_mtime))
+
+ # make it longer
+ with open(f, "a+") as fobj:
+ fobj.write("data\n")
+ inst = self.conn.GetInstance(InstanceName=inst.path)
+ cur_pflags = PassedFlags(*inst["PassedFlags"])
+ self.assertGreater(inst["FileSize"], inst["ExpectedFileSize"])
+ self.assertGreater(inst["LastModificationTime"],
+ inst["ExpectedLastModificationTime"])
+
+ self.assertTrue(cur_pflags.exists)
+ self.assertTrue(cur_pflags.type)
+ self.assertFalse(cur_pflags.size)
+ self.assertTrue(cur_pflags.mode)
+ self.assertFalse(cur_pflags.checksum)
+ self.assertTrue(cur_pflags.dev)
+ self.assertTrue(cur_pflags.ltarget)
+ self.assertTrue(cur_pflags.uid)
+ self.assertTrue(cur_pflags.gid)
+ self.assertFalse(cur_pflags.mtime)
+
+ # change file type
+ os.remove(f)
+ os.symlink(f, f)
+ os.lchown(f, inst["ExpectedFileUserID"], inst["ExpectedFileGroupID"])
+ inst = self.conn.GetInstance(InstanceName=inst.path)
+ cur_pflags = PassedFlags(*inst["PassedFlags"])
+ self.assertNotEqual(inst["LinkTarget"], inst["ExpectedLinkTarget"])
+ self.assertNotEqual(inst["FileSize"], inst["ExpectedFileSize"])
+ self.assertGreater(inst["LastModificationTime"],
+ inst["ExpectedLastModificationTime"])
+ self.assertNotEqual(inst["FileType"], inst["ExpectedFileType"])
+ self.assertEqual(inst["FileType"], pywbem.Uint16(3))
+
+ self.assertTrue(cur_pflags.exists)
+ self.assertFalse(cur_pflags.type)
+ self.assertFalse(cur_pflags.size)
+ self.assertFalse(cur_pflags.mode)
+ self.assertFalse(cur_pflags.checksum)
+ self.assertTrue(cur_pflags.dev)
+ self.assertFalse(cur_pflags.ltarget)
+ self.assertTrue(cur_pflags.uid)
+ self.assertTrue(cur_pflags.gid)
+ self.assertFalse(cur_pflags.mtime)
+
+ # remove it
+ os.remove(f)
+ inst = self.conn.GetInstance(InstanceName=inst.path)
+ cur_pflags = PassedFlags(*inst["PassedFlags"])
+ self.assertEqual(inst["LinkTarget"], inst["ExpectedLinkTarget"])
+ self.assertNotEqual(inst["FileSize"], inst["ExpectedFileSize"])
+ self.assertIsNone(inst["LastModificationTime"])
+ self.assertIsNone(inst["FileType"])
+ self.assertIsNone(inst["Checksum"])
+ self.assertIsNone(inst["FileMode"])
+ self.assertIsNone(inst["FileUserID"])
+ self.assertIsNone(inst["FileGroupID"])
+
+ self.assertFalse(cur_pflags.exists)
+ self.assertFalse(cur_pflags.type)
+ self.assertFalse(cur_pflags.size)
+ self.assertFalse(cur_pflags.mode)
+ self.assertFalse(cur_pflags.checksum)
+ self.assertFalse(cur_pflags.dev)
+ self.assertFalse(cur_pflags.ltarget)
+ self.assertFalse(cur_pflags.uid)
+ self.assertFalse(cur_pflags.gid)
+ self.assertFalse(cur_pflags.mtime)
+
+ def test_get_instance(self):
+ for nevra, files in pkg_files:
+ m = re_nevra.match(nevra)
+ name, version = [ m.group(k) for k in ("name", "ver") ]
+ if is_installed(nevra=nevra) and not verify_pkg(name):
+ remove_pkg(name)
+ if not is_installed(name):
+ install_pkg(nevra=nevra)
+ self.assertTrue(is_installed(nevra=nevra))
+ for f in files:
+ op = self.make_op(nevra, f)
+
+ inst = self.conn.GetInstance(InstanceName=op, LocalOnly=False)
+ self.assertIsInstance(inst, pywbem.CIMInstance)
+ self.assertEqual(inst.path, op)
+ for key in self.KEYS:
+ self.assertEqual(inst[key], op[key])
+
+ self.assertTrue(inst["FileExists"])
+ self.assertEqual(len(inst["PassedFlags"]), 10)
+ self.assertTrue(all(f is True for f in inst["PassedFlags"]))
+ for prop in ( "FileType", "FileUserID", "FileGroupID"
+ , "FileMode", "FileSize", "LinkTarget"
+ , "Checksum", "FileModeFlags"):
+ self.assertEqual(inst["Expected"+prop], inst[prop])
+ if os.path.islink(f):
+ self.do_test_symlink(op, nevra, f, inst)
+ elif os.path.isdir(f):
+ self.do_test_directory(op, nevra, f, inst)
+ elif os.path.isfile(f):
+ self.do_test_file(op, nevra, f, inst)
+
+ def test_invoke_method(self):
+ for nevra, files in pkg_files:
+ m = re_nevra.match(nevra)
+ name, version = [ m.group(k) for k in ("name", "ver") ]
+ if is_installed(nevra=nevra) and not verify_pkg(name):
+ remove_pkg(name)
+ if not is_installed(name):
+ install_pkg(nevra=nevra)
+ self.assertTrue(is_installed(nevra=nevra))
+ for f in files:
+ op = self.make_op(nevra, f)
+
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Invoke",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(0))
+
+ # modify file
+ if os.path.isfile(f):
+ os.remove(f)
+ else:
+ os.lchown(f, os.stat(f).st_uid + 1, -1)
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Invoke",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(2))
+
+ @classmethod
+ def tearDownClass(cls):
+ for pkg_nevra, files in pkg_files:
+ name = re_nevra.match(pkg_nevra).group('name')
+ if is_installed(name) and not verify_pkg(name):
+ remove_pkg(name)
+ if not is_installed(name):
+ install_pkg(nevra=pkg_nevra)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/software/test/test_software_installed_package.py b/src/software/test/test_software_installed_package.py
new file mode 100755
index 0000000..5bcb9d2
--- /dev/null
+++ b/src/software/test/test_software_installed_package.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+
+from common import *
+import shutil
+import socket
+import stat
+
+class TestSoftwareInstalledPackage(SoftwareBaseTestCase):
+
+ CLASS_NAME = "LMI_SoftwareInstalledPackage"
+ KEYS = ( "Software", "System")
+
+ def make_op(self, name, epoch, ver, rel, arch, ses=2):
+ op = self.op.copy()
+ pkg_op = pywbem.CIMInstanceName(
+ classname="LMI_SoftwarePackage", namespace="root/cimv2",
+ keybindings={
+ "Name" : name,
+ "SoftwareElementID" : make_nevra(
+ name, epoch, ver, rel, arch, "ALWAYS"),
+ "SoftwareElementState" : pywbem.Uint16(ses),
+ "TargetOperatingSystem" : pywbem.Uint16(36),
+ "Version" : ver })
+ system_op = pywbem.CIMInstanceName(
+ classname="CIM_ComputerSystem", namespace="root/cimv2",
+ keybindings={
+ "CreationClassName" : "CIM_ComputerSystem",
+ "Name" : socket.gethostname() })
+ op["Software"] = pkg_op
+ op["System"] = system_op
+ return op
+
+ def test_get_instance(self):
+ for pkg in packages:
+ if not is_installed(*pkg[:5]):
+ install_pkg(*pkg[:5])
+ op = self.make_op(*pkg[:5])
+ inst = self.conn.GetInstance(InstanceName=op, LocalOnly=False)
+ self.assertIsSubclass(inst.path.classname, self.CLASS_NAME)
+ self.assertIsSubclass(
+ inst.path['System'].classname, op['System'].classname)
+ self.assertIsSubclass(
+ inst.path['Software'].classname, op['Software'].classname)
+ self.assertEqual(len(inst.path.keys()), 2)
+ for key in self.KEYS:
+ self.assertEqual(inst[key], inst.path[key])
+ self.assertEqual(inst['Software'], op['Software'])
+ remove_pkg(pkg.name)
+ op['Software']['SoftwareElementState'] = pywbem.Uint16(1)
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.GetInstance(InstanceName=op, LocalOnly=False)
+ self.assertEqual(cm.exception.args[0], pywbem.CIM_ERR_NOT_FOUND)
+
+ def test_enum_instances(self):
+ pkg = packages[0]
+ if is_installed(*pkg[:5]):
+ remove_pkg(pkg.name)
+ insts1 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ install_pkg(*pkg[:5])
+ insts2 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertEqual(len(insts1) + 1, len(insts2))
+
+ op = self.make_op(*pkg[:5])
+ self.assertIn(op['Software'], (inst['Software'] for inst in insts2))
+ self.assertTrue(all(isinstance(inst, pywbem.CIMInstanceName)
+ for inst in insts1))
+
+ insts1 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ remove_pkg(pkg.name)
+ insts2 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+
+ self.assertEqual(len(insts2) + 1, len(insts1))
+ self.assertIn(op['Software'], (inst['Software'] for inst in insts1))
+ self.assertTrue(all(inst['Software'] == inst.path['Software']
+ for inst in insts1))
+ self.assertTrue(all(inst['System'] == inst.path['System']
+ for inst in insts1))
+
+ def test_create_instance(self):
+ for pkg in packages:
+ if is_installed(pkg.name):
+ remove_pkg(pkg.name)
+ self.assertFalse(is_installed(pkg.name))
+ op = self.make_op(*(list(pkg[:5]) + [1]))
+ inst = pywbem.CIMInstance(classname=op.classname, path=op)
+ inst["Software"] = op["Software"]
+ inst["System"] = op["System"]
+ iname = self.conn.CreateInstance(NewInstance=inst)
+ self.assertIsInstance(iname, pywbem.CIMInstanceName)
+ op["Software"]["SoftwareElementState"] = pywbem.Uint16(2)
+ self.assertEqual(iname["Software"], op["Software"])
+ self.assertIsInstance(iname["System"], pywbem.CIMInstanceName)
+ self.assertIsSubclass(iname["System"].classname,
+ op["System"].classname)
+ self.assertEqual(set(iname.keys()), set(op.keys()))
+ self.assertTrue(is_installed(pkg.name))
+
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.CreateInstance(NewInstance=inst)
+ self.assertEqual(cm.exception.args[0],
+ pywbem.CIM_ERR_ALREADY_EXISTS)
+
+ def test_delete_instance(self):
+ for pkg in packages:
+ if not is_installed(pkg.name):
+ install_pkg(*pkg[:5])
+ self.assertTrue(is_installed(pkg.name))
+ op = self.make_op(*pkg[:5])
+ self.conn.DeleteInstance(op)
+ self.assertFalse(is_installed(pkg.name))
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.DeleteInstance(op)
+ self.assertEqual(cm.exception.args[0], pywbem.CIM_ERR_NOT_FOUND)
+
+ def test_check_integrity(self):
+ for pkg_nevra, files in pkg_files:
+ m = re_nevra.match(pkg_nevra)
+ name, epoch, ver, rel, arch = [ m.group(k) for k in (
+ "name", "epoch", "ver", "rel", "arch") ]
+ if ( (is_installed(nevra=pkg_nevra) and not verify_pkg(name))
+ or (is_installed(name) and not is_installed(nevra=pkg_nevra))) :
+ remove_pkg(name)
+ if not is_installed(name):
+ install_pkg(name, epoch, ver, rel, arch)
+ self.assertTrue(is_installed(nevra=pkg_nevra))
+
+ op = self.make_op(name, epoch, ver, rel, arch)
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="CheckIntegrity",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(0)) # check passed
+ self.assertEqual(len(oparms), 1)
+ self.assertIn("Failed", oparms)
+ self.assertEqual(len(oparms["Failed"]), 0)
+
+ cnt_bad = 0
+ for f in files:
+ stats = os.lstat(f)
+ if os.path.islink(f): # modify symbolic link
+ target = os.readlink(f)
+ os.remove(f)
+ os.symlink(target, f) # just touch symlink
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="CheckIntegrity",
+ ObjectName=op)
+ # symlink must pass
+ self.assertEqual(len(oparms["Failed"]), cnt_bad)
+ os.remove(f)
+ # now change target
+ os.symlink("wrong_link_target", f)
+ elif os.path.isdir(f): # check directory
+ os.chmod(f, stats.st_mode) # just touch dir
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="CheckIntegrity",
+ ObjectName=op)
+ # dir must pass
+ self.assertEqual(len(oparms["Failed"]), cnt_bad)
+ # modify read access of directory
+ os.chmod(f, stats.st_mode ^ stat.S_IROTH)
+ else: # modify regular file
+ # just touch file - this is enough to make it fail
+ with open(f, "w+") as fobj: pass
+ cnt_bad += 1
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="CheckIntegrity",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(1))
+ self.assertEqual(len(oparms), 1)
+ self.assertIn("Failed", oparms)
+ self.assertEqual(len(oparms["Failed"]), cnt_bad)
+ self.assertIn(f, (p["Name"] for p in oparms["Failed"]))
+
+ def test_method_update(self):
+ for pkg in packages:
+ if not pkg.updatable: continue
+ if is_installed(pkg.name) and not is_installed(*pkg[:5]):
+ remove_pkg(pkg.name)
+ if not is_installed(pkg.name):
+ install_pkg(*pkg[:5])
+ self.assertTrue(is_installed(*pkg[:5]))
+
+ op = self.make_op(*pkg[:5])
+ op_up = self.make_op(pkg.name, pkg.up_epoch,
+ pkg.up_ver, pkg.up_rel, pkg.arch)
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Update",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint16(1))
+ self.assertEqual(len(oparms), 1)
+ self.assertIn("Installed", oparms)
+ self.assertEqual(oparms["Installed"], op_up["Software"])
+ self.assertTrue(is_installed(pkg.name, pkg.up_epoch,
+ pkg.up_ver, pkg.up_rel, pkg.arch))
+
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Update",
+ ObjectName=op_up)
+ self.assertEqual(rval, pywbem.Uint16(0))
+ self.assertEqual(len(oparms), 1)
+ self.assertEqual(oparms["Installed"], op_up["Software"])
+
+ with self.assertRaises(pywbem.CIMError) as cm:
+ self.conn.InvokeMethod(
+ MethodName="Update",
+ ObjectName=op)
+ self.assertEqual(cm.exception.args[0],
+ pywbem.CIM_ERR_NOT_FOUND)
+
+ remove_pkg(pkg.name)
+ install_pkg(*pkg[:5])
+ self.assertTrue(is_installed(*pkg[:5]))
+
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Update",
+ ObjectName=op,
+ Epoch=pkg.epoch,
+ Version=pkg.ver,
+ Release=pkg.rel)
+ self.assertEqual(rval, pywbem.Uint16(0))
+ self.assertEqual(oparms["Installed"], op["Software"])
+
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Update",
+ ObjectName=op,
+ Epoch=pkg.up_epoch,
+ Version=pkg.up_ver,
+ Release=pkg.up_rel)
+ self.assertEqual(rval, pywbem.Uint16(1))
+ self.assertEqual(oparms["Installed"], op_up["Software"])
+
+ @classmethod
+ def tearDownClass(cls):
+ for pkg_nevra, files in pkg_files:
+ name = re_nevra.match(pkg_nevra).group('name')
+ if is_installed(name) and not verify_pkg(name):
+ remove_pkg(name)
+ if not is_installed(name):
+ install_pkg(nevra=pkg_nevra)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/software/test/test_software_package.py b/src/software/test/test_software_package.py
new file mode 100755
index 0000000..cdaf04d
--- /dev/null
+++ b/src/software/test/test_software_package.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+from common import *
+
+class TestSoftwarePackage(SoftwareBaseTestCase):
+
+ CLASS_NAME = "LMI_SoftwarePackage"
+ KEYS = ( "Name", "SoftwareElementID", "SoftwareElementState"
+ , "TargetOperatingSystem", "Version")
+
+ def make_op(self, name, epoch, ver, rel, arch, ses=2):
+ op = self.op.copy()
+ op["Name"] = name
+ op["SoftwareElementID"] = make_nevra(
+ name, epoch, ver, rel, arch, "ALWAYS")
+ op["SoftwareElementState"] = pywbem.Uint16(ses)
+ op["TargetOperatingSystem"] = pywbem.Uint16(36)
+ op["Version"] = ver
+ return op
+
+ def test_get_instance(self):
+ for pkg in packages:
+ if is_installed(pkg.name):
+ remove_pkg(pkg.name)
+ op = self.make_op(pkg.name, pkg.epoch, pkg.ver,
+ pkg.rel, pkg.arch, 1)
+ inst = self.conn.GetInstance(InstanceName=op, LocalOnly=False)
+ self.assertEqual(inst.path, op)
+ for key in self.KEYS:
+ self.assertTrue(inst.properties.has_key(key))
+ self.assertEqual(inst.path[key], inst[key])
+ self.assertEqual(inst['Release'], pkg.rel)
+ install_pkg(*pkg[:5])
+ op['SoftwareElementState'] = pywbem.Uint16(2)
+ inst = self.conn.GetInstance(InstanceName=op, LocalOnly=False)
+ self.assertEqual(inst.path, op)
+
+ # try to leave out epoch part
+ if pkg.epoch == "0":
+ op_no_epoch = op.copy()
+ op_no_epoch["SoftwareElementID"] = make_nevra(pkg.name, pkg.epoch,
+ pkg.ver, pkg.rel, pkg.arch, "NEVER")
+ self.assertNotIn(":", op_no_epoch["SoftwareElementID"])
+ inst = self.conn.GetInstance(
+ InstanceName=op_no_epoch, LocalOnly=False)
+ self.assertIn(inst.path, (op, op_no_epoch))
+
+ def test_method_install(self):
+ for pkg in packages:
+ if is_installed(pkg.name):
+ remove_pkg(pkg.name)
+ op = self.make_op(pkg.name, pkg.epoch, pkg.ver,
+ pkg.rel, pkg.arch, 1)
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Install",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(1))
+ self.assertEqual(len(oparms), 1)
+ self.assertTrue(oparms.has_key('Installed'))
+ op['SoftwareElementState'] = pywbem.Uint16(2)
+ self.assertEqual(oparms['Installed'], op)
+ self.assertTrue(is_installed(pkg.name))
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Install",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(0))
+ self.assertEqual(len(oparms), 1)
+ self.assertEqual(oparms['Installed'], op)
+
+ def test_method_remove(self):
+ for pkg in packages:
+ if not is_installed(pkg.name):
+ install_pkg(*pkg[:5])
+ op = self.make_op(*pkg[:5])
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Remove",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint16(1))
+ self.assertEqual(len(oparms), 0)
+ self.assertFalse(is_installed(pkg.name))
+ op["SoftwareElementState"] = pywbem.Uint16(1)
+ (rval, oparms) = self.conn.InvokeMethod(
+ MethodName="Remove",
+ ObjectName=op)
+ self.assertEqual(rval, pywbem.Uint32(0))
+ self.assertEqual(len(oparms), 0)
+
+if __name__ == '__main__':
+ unittest.main()