summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichal Minar <miminar@redhat.com>2013-02-04 12:37:58 +0100
committerMichal Minar <miminar@redhat.com>2013-02-04 14:51:03 +0100
commit775290a2c13b801c84fefdc4538fe110c4df6c24 (patch)
tree04427c4859b874ed4346a042f3d097ba51fa50d7 /src
parent861a85d3b8e7ed6eaa68edc6d03f1192d46272e1 (diff)
downloadopenlmi-providers-775290a2c13b801c84fefdc4538fe110c4df6c24.tar.gz
openlmi-providers-775290a2c13b801c84fefdc4538fe110c4df6c24.tar.xz
openlmi-providers-775290a2c13b801c84fefdc4538fe110c4df6c24.zip
rewritten for safe execution of transactions
Made separate process openlmi.software.yumdb.process.YumWorker for calls to yum API. Its client openlmi.software.yumdb.YumDB communicates with it via synchronnous queues - uplink and downlink. Resolves: #63 in openlmi trac -- yum API not useable, while changing thread_id) Resolves: #33 in openlmi trac -- Install/remove package Common functionality of providers moved under openlmi.software.core subpackage to make them easily accessible from other providers without cyclic dependencies. Improved logging with cmpi_logging module. openlmi.software.cimom_entry module now is the only module loadable by cmpi-bindings. It sets up providers and maps them by their name. New subpackages: openlmi.software.core openlmi.software.yumdb
Diffstat (limited to 'src')
-rw-r--r--src/software/openlmi/software/LMI_SoftwareFileCheck.py203
-rw-r--r--src/software/openlmi/software/LMI_SoftwareInstalledPackage.py288
-rw-r--r--src/software/openlmi/software/LMI_SoftwarePackage.py353
-rw-r--r--src/software/openlmi/software/LMI_SoftwarePackageChecks.py83
-rw-r--r--src/software/openlmi/software/cimom_entry.py52
-rw-r--r--src/software/openlmi/software/core/ComputerSystem.py73
-rw-r--r--src/software/openlmi/software/core/SoftwareFileCheck.py515
-rw-r--r--src/software/openlmi/software/core/SoftwareInstalledPackage.py39
-rw-r--r--src/software/openlmi/software/core/SoftwarePackage.py412
-rw-r--r--src/software/openlmi/software/core/SoftwarePackageChecks.py32
-rw-r--r--src/software/openlmi/software/core/__init__.py20
-rw-r--r--src/software/openlmi/software/util/__init__.py134
-rw-r--r--src/software/openlmi/software/util/cmpi_logging.py203
-rw-r--r--src/software/openlmi/software/util/common.py825
-rw-r--r--src/software/openlmi/software/yumdb/__init__.py307
-rw-r--r--src/software/openlmi/software/yumdb/errors.py43
-rw-r--r--src/software/openlmi/software/yumdb/jobs.py234
-rw-r--r--src/software/openlmi/software/yumdb/packagecheck.py188
-rw-r--r--src/software/openlmi/software/yumdb/packageinfo.py171
-rw-r--r--src/software/openlmi/software/yumdb/process.py605
-rw-r--r--src/software/setup.py8
-rw-r--r--src/software/test/common.py34
-rw-r--r--src/software/test/rpmcache.py52
-rwxr-xr-xsrc/software/test/run.py23
-rwxr-xr-xsrc/software/test/test_software_file_check.py25
-rwxr-xr-xsrc/software/test/test_software_installed_package.py71
-rwxr-xr-xsrc/software/test/test_software_package.py11
27 files changed, 3402 insertions, 1602 deletions
diff --git a/src/software/openlmi/software/LMI_SoftwareFileCheck.py b/src/software/openlmi/software/LMI_SoftwareFileCheck.py
index 63070fc..7d4aea6 100644
--- a/src/software/openlmi/software/LMI_SoftwareFileCheck.py
+++ b/src/software/openlmi/software/LMI_SoftwareFileCheck.py
@@ -27,8 +27,10 @@ Instruments the CIM class LMI_SoftwareFileCheck
import pywbem
from pywbem.cim_provider2 import CIMProvider2
-from openlmi.software.util import common
-from openlmi.software.util.common import SoftwareFileCheck
+
+from openlmi.software.core import SoftwareFileCheck
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
"""Instrument the CIM class LMI_SoftwareFileCheck
@@ -39,12 +41,11 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
"""
- def __init__(self, env):
- logger = env.get_logger()
- logger.log_debug('Initializing provider %s from %s' \
+ def __init__(self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
% (self.__class__.__name__, __file__))
-
+ @cmpi_logging.trace_method
def get_instance(self, env, model):
"""Return an instance.
@@ -67,16 +68,13 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.get_instance()' \
- % self.__class__.__name__)
-
- with common.YumDB.getInstance(env):
- vpkg = SoftwareFileCheck.object_path2yumcheck(env, model.path)
+ with YumDB.getInstance():
+ pkg_info, pkg_check, pkg_file = \
+ SoftwareFileCheck.object_path2pkg_file(model.path)
return SoftwareFileCheck.filecheck2model(
- vpkg, model['Name'], env, False)
+ pkg_info, pkg_check, pkg_file.path, keys_only=False)
+ @cmpi_logging.trace_method
def enum_instances(self, env, model, keys_only):
"""Enumerate instances.
@@ -99,13 +97,10 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.enum_instances()' \
- % self.__class__.__name__)
# this won't be supported because of enormous amount of data
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
"""Return a newly created or modified instance.
@@ -132,12 +127,9 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.set_instance()' \
- % self.__class__.__name__)
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def delete_instance(self, env, instance_name):
"""Delete an instance.
@@ -159,13 +151,9 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.delete_instance()' \
- % self.__class__.__name__)
-
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def cim_method_invoke(self, env, object_name):
"""Implements LMI_SoftwareFileCheck.Invoke()
@@ -203,23 +191,18 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.cim_method_invoke()' \
- % self.__class__.__name__)
-
- with common.YumDB.getInstance(env):
- vpkg = SoftwareFileCheck.object_path2yumcheck(env, object_name)
- sfc = SoftwareFileCheck.test_file(env,
- SoftwareFileCheck.pkg_checksum_type(vpkg.po),
- vpkg._files[object_name["Name"]])
+ with YumDB.getInstance():
+ _, pkg_check, pkg_file = \
+ SoftwareFileCheck.object_path2pkg_file(object_name)
+ sfc = SoftwareFileCheck.test_file(
+ pkg_check.file_checksum_type, pkg_file)
out_params = []
ret = 0 if SoftwareFileCheck.filecheck_passed(sfc) else 2
return (pywbem.Uint32(ret), out_params)
- def cim_method_invokeonsystem(self, env,
- object_name, #pylint: disable=W0613
- param_targetsystem=None): #pylint: disable=W0613
+ @cmpi_logging.trace_method
+ 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
@@ -259,146 +242,6 @@ class LMI_SoftwareFileCheck(CIMProvider2): # pylint: disable=R0904
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)
- 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
-
-def get_providers(env):
- """for associating CIM Class Name to python provider class name"""
- lmi_softwarefilecheck_prov = LMI_SoftwareFileCheck(env)
- return {'LMI_SoftwareFileCheck': lmi_softwarefilecheck_prov}
diff --git a/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py b/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py
index 8914c47..d82a5a2 100644
--- a/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py
+++ b/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py
@@ -26,14 +26,14 @@ Instruments the CIM class LMI_SoftwareInstalledPackage
"""
-import itertools
import pywbem
-import yum
from pywbem.cim_provider2 import CIMProvider2
-from openlmi.software.LMI_SoftwarePackage import LMI_SoftwarePackage
-from openlmi.software.util import common
-from openlmi.software.util.common import (
- YumDB, SoftwarePackage, SoftwareFileCheck)
+
+from openlmi.software.core import (
+ ComputerSystem, SoftwareFileCheck,
+ SoftwareInstalledPackage, SoftwarePackage)
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
"""Instrument the CIM class LMI_SoftwareInstalledPackage
@@ -43,11 +43,11 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
"""
- def __init__ (self, env):
- logger = env.get_logger()
- logger.log_debug('Initializing provider %s from %s' \
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
% (self.__class__.__name__, __file__))
+ @cmpi_logging.trace_method
def get_instance(self, env, model):
"""Return an instance.
@@ -70,24 +70,22 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.get_instance()' \
- % self.__class__.__name__)
-
if not "Software" in model:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Software property.")
if not "System" in model:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing System property.")
- common.check_computer_system_op(env, model['System'])
- model['System'] = common.get_computer_system_op()
- with YumDB.getInstance(env):
- pkg = SoftwarePackage.object_path2pkg(env, model['Software'])
- model['Software'] = SoftwarePackage.pkg2model(env, pkg, True)
+ ComputerSystem.check_path_property(env, model, 'System')
+ model['System'] = ComputerSystem.get_path()
+ with YumDB.getInstance():
+ pkg_info = SoftwarePackage.object_path2pkg(model['Software'],
+ kind="installed")
+ model['Software'] = SoftwarePackage.pkg2model(
+ pkg_info, keys_only=True)
return model
+ @cmpi_logging.trace_method
def enum_instances(self, env, model, keys_only):
"""Enumerate instances.
@@ -110,11 +108,6 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
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.
@@ -123,22 +116,15 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
yum_package_path = pywbem.CIMInstanceName("LMI_SoftwarePackage",
namespace=model.path.namespace,
host=model.path.host)
- model['System'] = common.get_computer_system_op()
- with YumDB.getInstance(env) as ydb:
- for pkg in ydb.rpmdb:
- iname = SoftwarePackage.pkg2model(env, pkg,
- True, yum_package_path)
+ model['System'] = ComputerSystem.get_path()
+ with YumDB.getInstance() as ydb:
+ pkglist = ydb.get_package_list('installed')
+ for pkg in pkglist:
+ iname = SoftwarePackage.pkg2model(pkg, model=yum_package_path)
model['Software'] = iname
- if keys_only:
- yield model
- else:
- try:
- yield self.get_instance(env, model)
- except pywbem.CIMError as error:
- if error.args[0] not in (pywbem.CIM_ERR_NOT_FOUND,
- pywbem.CIM_ERR_ACCESS_DENIED):
- raise
+ yield model
+ @cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
"""Return a newly created or modified instance.
@@ -165,11 +151,6 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
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_SUPPORTED,
@@ -181,29 +162,24 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
if not "System" in instance:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing System property.")
- common.check_computer_system_op(env, instance['System'])
+ ComputerSystem.check_path_property(env, instance, 'System')
+
+ with YumDB.getInstance() as ydb:
+ pkg_info = SoftwarePackage.object_path2pkg_search(
+ instance['Software'])
- with YumDB.getInstance(env) as ydb:
- pkg = SoftwarePackage.object_path2pkg_search(
- env, instance['Software'])
- if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ if pkg_info.installed:
raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
- "Package is already installed.")
-
- logger.log_info('installing package {}'.format(pkg.nevra))
- # install
- ydb.install(pkg)
- ydb.buildTransaction()
- ydb.processTransaction()
- logger.log_info('package installed'.format(pkg.nevra))
-
- # return instance
- pkg_iname = SoftwarePackage.pkg2model(env, pkg, True)
- pkg_iname["SoftwareElementState"] = \
- LMI_SoftwarePackage.Values.SoftwareElementState.Executable
- instance["Software"] = pkg_iname
- return self.get_instance(env, instance)
+ "Package %s is already installed." % pkg_info)
+
+ cmpi_logging.logger.info('installing package %s' % pkg_info)
+ installed_pkg = ydb.install_package(pkg_info)
+ cmpi_logging.logger.info('package %s installed' % pkg_info)
+ instance["Software"] = SoftwarePackage.pkg2model(installed_pkg)
+ return instance
+
+ @cmpi_logging.trace_method
def delete_instance(self, env, instance_name):
"""Delete an instance.
@@ -225,27 +201,22 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.delete_instance()' \
- % self.__class__.__name__)
-
if not "Software" in instance_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Software property.")
if not "System" in instance_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing System property.")
- common.check_computer_system_op(env, instance_name['System'])
- with YumDB.getInstance(env) as ydb:
- pkg = SoftwarePackage.object_path2pkg(
- env, instance_name["Software"])
- logger.log_info('removing package "%s"' % pkg.nevra)
- ydb.remove(pkg)
- ydb.buildTransaction()
- ydb.processTransaction()
- logger.log_info('package "%s" removed' % pkg.nevra)
+ ComputerSystem.check_path_property(env, instance_name, 'System')
+
+ with YumDB.getInstance() as ydb:
+ pkg_info = SoftwarePackage.object_path2pkg(
+ instance_name["Software"], kind="installed")
+ cmpi_logging.logger.info('removing package "%s"' % pkg_info)
+ ydb.remove_package(pkg_info)
+ cmpi_logging.logger.info('package "%s" removed' % pkg_info)
+ @cmpi_logging.trace_method
def references(self, env, object_name, model, result_class_name, role,
result_role, keys_only):
"""Instrument Associations.
@@ -304,10 +275,6 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.references()' \
- % self.__class__.__name__)
cimhandle = env.get_cimom_handle()
# If you want to get references for free, implemented in terms
@@ -321,6 +288,7 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
return self.simple_refs(env, object_name, model,
result_class_name, role, result_role, keys_only)
+ @cmpi_logging.trace_method
def cim_method_checkintegrity(self, env, object_name):
"""Implements LMI_SoftwarePackage.CheckIntegrity()
@@ -337,7 +305,7 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
should be invoked.
Returns a two-tuple containing the return value (
- type pywbem.Uint32 self.Values.CheckIntegrity)
+ type pywbem.Uint32 SoftwareInstalledPackage.Values.CheckIntegrity)
and a list of CIMParameter objects representing the output parameters
Output parameters:
@@ -357,36 +325,33 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.cim_method_checkintegrity()' \
- % self.__class__.__name__)
-
- failed = []
if not "Software" in object_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Software property.")
if not "System" in object_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing System property.")
- with YumDB.getInstance(env):
- 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:
- file_check = SoftwareFileCheck.test_file(env, csum, vpf)
+
+ failed = []
+ with YumDB.getInstance() as ydb:
+ pkg_info = SoftwarePackage.object_path2pkg(object_name['Software'],
+ kind="installed")
+ pkg_check = ydb.check_package(pkg_info)
+ for pkg_file in pkg_check.files.values():
+ file_check = SoftwareFileCheck.test_file(
+ pkg_check.file_checksum_type, pkg_file)
if SoftwareFileCheck.filecheck_passed(file_check):
continue
failed.append(SoftwareFileCheck.filecheck2model(
- vpkg, vpf.filename, env, keys_only=True,
- file_check=file_check))
+ pkg_info, pkg_check, pkg_file.path,
+ keys_only=True, file_check=file_check))
out_params = [ pywbem.CIMParameter('Failed', type='reference',
value=failed) ]
- return ( getattr(self.Values.CheckIntegrity,
+ return ( getattr(SoftwareInstalledPackage.Values.CheckIntegrity,
'Pass' if len(failed) == 0 else 'Not_passed')
, out_params )
+ @cmpi_logging.trace_method
def cim_method_update(self, env, object_name,
param_epoch=None,
param_release=None,
@@ -410,7 +375,7 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
newest, when empty
Returns a two-tuple containing the return value (
- type pywbem.Uint16 self.Values.Update)
+ type pywbem.Uint16 SoftwareInstalledPackage.Values.Update)
and a list of CIMParameter objects representing the output parameters
Output parameters:
@@ -430,86 +395,57 @@ class LMI_SoftwareInstalledPackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.cim_method_update()' \
- % self.__class__.__name__)
-
if not "Software" in object_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Software property.")
if not "System" in object_name:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing System property.")
- common.check_computer_system_op(env, object_name['System'])
-
- with YumDB.getInstance(env) as ydb:
- 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"]))
-
- pkglist = ydb.doPackageLists('all', showdups=True)
- # NOTE: available ∩ installed = ∅
- exact, _, _ = yum.packages.parsePackages(
- itertools.chain(pkglist.available, pkglist.installed),
- [orig.name])
- try:
- pkg = [ p for p in sorted(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=SoftwarePackage.pkg2model(env, pkg, True))]
- if orig.evra == pkg.evra:
- logger.log_info('already up to date')
- return (self.Values.Update.Already_newest, out_params)
-
- ydb.update(update_to=True,
- name=pkg.name,
- epoch=pkg.epoch,
- version=pkg.version,
- release=pkg.release)
- ydb.buildTransaction()
- ydb.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
-
-
-def get_providers(env):
- """Associates CIM Class Name to python provider class name"""
- lmi_sip_prov = LMI_SoftwareInstalledPackage(env)
- return {'LMI_SoftwareInstalledPackage': lmi_sip_prov}
+ ComputerSystem.check_path_property(env, object_name, 'System')
+
+ with YumDB.getInstance() as ydb:
+ orig = SoftwarePackage.object_path2pkg(object_name['Software'],
+ kind='installed')
+
+ # NOTE: that we need to obtain all available and installed packages
+ # because they are disjunctive and we want to select package
+ # with highest version. Let the yum do the sorting of packages.
+ pkglist = ydb.filter_packages('all',
+ allow_duplicates=True, sort=True,
+ name=orig.name,
+ epoch=param_epoch,
+ version=param_version,
+ release=param_release,
+ arch=orig.arch)
+
+ if len(pkglist) < 1:
+ cmpi_logging.logger.error(
+ "desired package matching evr not found")
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "package matching desired evr not found among"
+ " available packages")
+ if len(pkglist) > 1:
+ cmpi_logging.logger.info("multiple packages matching"
+ " evr - selecting newest")
+ cmpi_logging.logger.debug("matching packages (from oldest):"
+ " [%s]" % ", ".join([str(p) for p in pkglist]))
+ desired_pkg = pkglist[-1]
+
+ out_params = [pywbem.CIMParameter('Installed', type='reference')]
+ if desired_pkg.installed:
+ out_params[0].value = SoftwarePackage.pkg2model(desired_pkg)
+ cmpi_logging.logger.info('already up to date')
+ return ( SoftwareInstalledPackage.Values.Update.Already_newest
+ , out_params)
+
+ cmpi_logging.logger.info(
+ 'trying to update package \"%s\" to "%s"' % (
+ orig, desired_pkg))
+ installed_pkg = ydb.update_to_package(desired_pkg)
+ cmpi_logging.logger.info('update successful')
+
+ out_params[0].value = SoftwarePackage.pkg2model(installed_pkg)
+
+ return ( SoftwareInstalledPackage.Values.Update.Successful_installation
+ , out_params)
+
diff --git a/src/software/openlmi/software/LMI_SoftwarePackage.py b/src/software/openlmi/software/LMI_SoftwarePackage.py
index b595600..e031998 100644
--- a/src/software/openlmi/software/LMI_SoftwarePackage.py
+++ b/src/software/openlmi/software/LMI_SoftwarePackage.py
@@ -26,13 +26,14 @@ Instruments the CIM class LMI_SoftwarePackage
"""
-import itertools
import pywbem
-import yum
from pywbem.cim_provider2 import CIMProvider2
-from openlmi.software.util.common import (YumDB, SoftwarePackage)
-class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
+from openlmi.software.core import SoftwarePackage
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
+
+class LMI_SoftwarePackage(CIMProvider2):
"""Instrument the CIM class LMI_SoftwarePackage
RPM package installed on particular computer system with YUM (The
@@ -40,11 +41,11 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
"""
- def __init__(self, env):
- logger = env.get_logger()
- logger.log_debug('Initializing provider %s from %s' \
+ def __init__(self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
% (self.__class__.__name__, __file__))
+ @cmpi_logging.trace_method
def get_instance(self, env, model):
"""Return an instance.
@@ -67,16 +68,12 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
+ with YumDB.getInstance():
+ pkg_info = SoftwarePackage.object_path2pkg(model.path, 'all')
+ return SoftwarePackage.pkg2model(
+ pkg_info, keys_only=False, model=model)
- 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 SoftwarePackage.pkg2model(env, pkg,
- keys_only=False, model=model)
-
+ @cmpi_logging.trace_method
def enum_instances(self, env, model, keys_only):
"""Enumerate instances.
@@ -99,11 +96,6 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
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.
@@ -111,14 +103,15 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
'SoftwareElementState': None, 'Name': None,
'SoftwareElementID': None})
- with YumDB.getInstance(env) as ydb:
+ with YumDB.getInstance() as ydb:
# get all packages
- pkglist = ydb.doPackageLists('all', showdups=True)
- pkglist = itertools.chain(pkglist.installed, pkglist.available)
- # NOTE: available ∩ installed = ∅
- for pkg in sorted(pkglist):
- yield SoftwarePackage.pkg2model(env, pkg, keys_only, model)
+ pkglist = ydb.get_package_list('all',
+ allow_duplicates=True, sort=True)
+ for pkg in pkglist:
+ yield SoftwarePackage.pkg2model(pkg,
+ keys_only=keys_only, model=model)
+ @cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
"""Return a newly created or modified instance.
@@ -145,12 +138,9 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.set_instance()' \
- % self.__class__.__name__)
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def delete_instance(self, env, instance_name):
"""Delete an instance.
@@ -172,12 +162,9 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.delete_instance()' \
- % self.__class__.__name__)
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def cim_method_install(self, env, object_name):
"""Implements LMI_SoftwarePackage.Install()
@@ -189,7 +176,8 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
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)
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 SoftwarePackage.Values.Install)
and a list of CIMParameter objects representing the output parameters
Output parameters:
@@ -209,31 +197,26 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
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 ydb:
+ with YumDB.getInstance() as ydb:
# get available packages
- pkg = SoftwarePackage.object_path2pkg_search(env, object_name)
+ pkg_info = SoftwarePackage.object_path2pkg_search(object_name)
out_params = [ pywbem.CIMParameter('Installed', type='reference') ]
- if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
- out_params[0].value = SoftwarePackage.pkg2model(env, pkg, True)
- return (self.Values.Install.Already_installed, out_params)
+ if pkg_info.installed:
+ out_params[0].value = SoftwarePackage.pkg2model(
+ pkg_info, keys_only=True)
+ return ( SoftwarePackage.Values.Install.Already_installed
+ , out_params)
- logger.log_info('installing package {}'.format(pkg.nevra))
- # install
- ydb.install(pkg)
- ydb.buildTransaction()
- ydb.processTransaction()
- logger.log_info('package installed'.format(pkg.nevra))
+ cmpi_logging.logger.info('installing package %s' % pkg_info)
+ installed_pkg = ydb.install_package(pkg_info)
+ cmpi_logging.logger.info('package %s installed' % pkg_info)
out_params[0].value = SoftwarePackage.pkg2model(
- env, pkg, True, object_name)
- out_params[0].value['SoftwareElementState'] = \
- self.Values.SoftwareElementState.Executable
- return (self.Values.Install.Successful_installation, out_params)
+ installed_pkg, keys_only=True)
+ return ( SoftwarePackage.Values.Install.Successful_installation
+ , out_params)
+ @cmpi_logging.trace_method
def cim_method_remove(self, env, object_name):
"""Implements LMI_SoftwarePackage.Remove()
@@ -245,7 +228,8 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
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)
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 SoftwarePackage.Values.Remove)
and a list of CIMParameter objects representing the output parameters
Output parameters: none
@@ -261,255 +245,14 @@ class LMI_SoftwarePackage(CIMProvider2): #pylint: disable=R0904
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 ydb:
- pkg = SoftwarePackage.object_path2pkg(env, object_name, 'all')
- if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
- logger.log_info('removing package "%s"' % pkg.nevra)
- ydb.remove(pkg)
- ydb.buildTransaction()
- ydb.processTransaction()
- logger.log_info('package "%s" removed' % pkg.nevra)
- rval = self.Values.Remove.Successful_removal
+ with YumDB.getInstance() as ydb:
+ pkg_info = SoftwarePackage.object_path2pkg(object_name, 'all')
+ if pkg_info.installed:
+ cmpi_logging.logger.info('removing package %s' % pkg_info)
+ ydb.remove_package(pkg_info)
+ cmpi_logging.logger.info('package %s removed' % pkg_info)
+ rval = SoftwarePackage.Values.Remove.Successful_removal
else:
- rval = self.Values.Remove.Not_installed
+ rval = SoftwarePackage.Values.Remove.Not_installed
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
-
-def get_providers(env):
- """Associates CIM Class Name to python provider class name"""
- lmi_softwarepackage_prov = LMI_SoftwarePackage(env)
- return {'LMI_SoftwarePackage': lmi_softwarepackage_prov}
diff --git a/src/software/openlmi/software/LMI_SoftwarePackageChecks.py b/src/software/openlmi/software/LMI_SoftwarePackageChecks.py
index facf90e..76befe0 100644
--- a/src/software/openlmi/software/LMI_SoftwarePackageChecks.py
+++ b/src/software/openlmi/software/LMI_SoftwarePackageChecks.py
@@ -26,10 +26,11 @@ Instruments the CIM class LMI_SoftwarePackageChecks
"""
import pywbem
-import yum
from pywbem.cim_provider2 import CIMProvider2
-from openlmi.software.util import common
-from openlmi.software.util.common import (SoftwarePackage, SoftwareFileCheck)
+
+from openlmi.software.core import (SoftwarePackage, SoftwareFileCheck)
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
"""Instrument the CIM class LMI_SoftwarePackageChecks
@@ -42,11 +43,11 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
"""
- def __init__ (self, env):
- logger = env.get_logger()
- logger.log_debug('Initializing provider %s from %s' \
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
% (self.__class__.__name__, __file__))
+ @cmpi_logging.trace_method
def get_instance(self, env, model):
"""Return an instance.
@@ -69,25 +70,21 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.get_instance()' \
- % self.__class__.__name__)
-
if not "Check" in model:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Check property.")
if not "Element" in model:
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
"Missing Element property.")
- vpkg = SoftwareFileCheck.object_path2yumcheck(env, model['Check'])
+
+ pkg_info, pkg_check, pkg_file = \
+ SoftwareFileCheck.object_path2pkg_file(model['Check'])
model['Check'] = SoftwareFileCheck.filecheck2model(
- vpkg, model['Check']['Name'],
- env, keys_only=True)
- model['Element'] = SoftwarePackage.pkg2model(
- env, vpkg.po, keys_only=True)
+ pkg_info, pkg_check, pkg_file.path)
+ model['Element'] = SoftwarePackage.pkg2model(pkg_info)
return model
+ @cmpi_logging.trace_method
def enum_instances(self, env, model, keys_only):
"""Enumerate instances.
@@ -110,13 +107,10 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
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,
"Enumeration of instances is not supported.")
+ @cmpi_logging.trace_method
def set_instance(self, env, instance, modify_existing):
"""Return a newly created or modified instance.
@@ -143,12 +137,9 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.set_instance()' \
- % self.__class__.__name__)
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def delete_instance(self, env, instance_name):
"""Delete an instance.
@@ -170,12 +161,9 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.delete_instance()' \
- % self.__class__.__name__)
raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+ @cmpi_logging.trace_method
def references(self, env, object_name, model, result_class_name, role,
result_role, keys_only):
"""Instrument Associations.
@@ -234,10 +222,6 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
CIM_ERR_FAILED (some other unspecified error occurred)
"""
-
- logger = env.get_logger()
- logger.log_debug('Entering %s.references()' \
- % self.__class__.__name__)
cimhandle = env.get_cimom_handle()
# Prime model.path with knowledge of the keys, so key values on
@@ -245,7 +229,7 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
# we set property values on the model.
model.path.update({'Check': None, 'Element': None})
- with common.YumDB.getInstance(env):
+ with YumDB.getInstance() as ydb:
if ( (not role or role.lower() == 'element')
and cimhandle.is_subclass(object_name.namespace,
sub=object_name.classname,
@@ -254,15 +238,14 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
classname='LMI_SoftwareFileCheck',
namespace="root/cimv2",
host=model.path.host)
- model['Element'] = object_name
+ pkg_info = SoftwarePackage.object_path2pkg(object_name,
+ kind="installed")
+ model['Element'] = SoftwarePackage.pkg2model(pkg_info)
- pkg = SoftwarePackage.object_path2pkg(env, object_name)
- vpkg = yum.packages._RPMVerifyPackage(
- pkg, pkg.hdr.fiFromHeader(),
- SoftwareFileCheck.pkg_checksum_type(pkg), [], True)
- for file_check in vpkg:
+ pkg_check = ydb.check_package(pkg_info)
+ for file_name in pkg_check:
model['Check'] = SoftwareFileCheck.filecheck2model(
- vpkg, file_check.filename, env, keys_only=True,
+ pkg_info, pkg_check, file_name,
model=filecheck_model)
yield model
@@ -270,21 +253,11 @@ class LMI_SoftwarePackageChecks(CIMProvider2): #pylint: disable=R0904
and cimhandle.is_subclass(object_name.namespace,
sub=object_name.classname,
super='LMI_SoftwareFileCheck')):
- model['Check'] = object_name
+ pkg_info, pkg_check, pkg_file = \
+ SoftwareFileCheck.object_path2pkg_file(object_name)
+ model['Check'] = SoftwareFileCheck.filecheck2model(
+ pkg_info, pkg_check, pkg_file.path)
- vpkg = SoftwareFileCheck.object_path2yumcheck(env, object_name)
- model['Element'] = SoftwarePackage.pkg2model(
- env, vpkg.po, keys_only=True)
+ model['Element'] = SoftwarePackage.pkg2model(pkg_info)
yield model
- class Values(object):
- class Phase(object):
- In_State = pywbem.Uint16(0)
- Next_State = pywbem.Uint16(1)
-
-## end of class LMI_SoftwarePackageChecksProvider
-
-def get_providers(env):
- """Associates CIM Class Name to python provider class name"""
- lmi_softwarepackagechecks_prov = LMI_SoftwarePackageChecks(env)
- return {'LMI_SoftwarePackageChecks': lmi_softwarepackagechecks_prov}
diff --git a/src/software/openlmi/software/cimom_entry.py b/src/software/openlmi/software/cimom_entry.py
new file mode 100644
index 0000000..59c01ee
--- /dev/null
+++ b/src/software/openlmi/software/cimom_entry.py
@@ -0,0 +1,52 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Entry module for OpenLMI Software proviers.
+"""
+
+from openlmi.software.LMI_SoftwarePackage import LMI_SoftwarePackage
+from openlmi.software.LMI_SoftwareInstalledPackage import \
+ LMI_SoftwareInstalledPackage
+from openlmi.software.LMI_SoftwareFileCheck import LMI_SoftwareFileCheck
+from openlmi.software.LMI_SoftwarePackageChecks import \
+ LMI_SoftwarePackageChecks
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
+
+def get_providers(env):
+ cmpi_logging.LogManager(env)
+
+ providers = {
+ "LMI_SoftwarePackage" : LMI_SoftwarePackage(env),
+ "LMI_SoftwareInstalledPackage" : LMI_SoftwareInstalledPackage(env),
+ "LMI_SoftwareFileCheck" : LMI_SoftwareFileCheck(env),
+ "LMI_SoftwarePackageChecks" : LMI_SoftwarePackageChecks(env)
+ }
+
+ return providers
+
+def can_unload(_env):
+ return True
+
+def shutdown(_env):
+ YumDB.getInstance().clean_up()
diff --git a/src/software/openlmi/software/core/ComputerSystem.py b/src/software/openlmi/software/core/ComputerSystem.py
new file mode 100644
index 0000000..90699a0
--- /dev/null
+++ b/src/software/openlmi/software/core/ComputerSystem.py
@@ -0,0 +1,73 @@
+# -*- 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/>.
+
+"""
+Just a common functionality related to associated class Linux_ComputerSystem.
+"""
+
+import pywbem
+import socket
+
+def get_path(prefix='Linux'):
+ """
+ @return object path of Linux_ComputerSystem
+ """
+ op = pywbem.CIMInstanceName(
+ classname='%s_ComputerSystem' % prefix,
+ namespace="root/cimv2")
+ op["CreationClassName"] = "Linux_ComputerSystem"
+ op["Name"] = socket.gethostname()
+ return op
+
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether object path contains correct instance name of
+ Linux_ComputerSystem corresponding to this system.
+ If not, an exception is raised.
+ """
+ system = op[prop_name]
+ if not isinstance(system, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_system = get_path(prefix='CIM')
+ ch = env.get_cimom_handle()
+ if system.namespace != our_system.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_system.namespace))
+ if not ch.is_subclass(our_system.namespace,
+ sub=system.classname,
+ super=our_system.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a sublass of %s" % (
+ prop_name, our_system.classname))
+ if not 'CreationClassName' in system or not 'Name' in system:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" is missing one of keys", prop_name)
+ if not ch.is_subclass(our_system.namespace,
+ sub=system['CreationClassName'],
+ super=our_system.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "CreationClassName of \"%s\" must be a sublass of %s" % (
+ prop_name, our_system.classname))
+ if system['Name'] != our_system['Name']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Name of \"%s\" does not match \"%s\"" %
+ prop_name, our_system['Name'])
+ return True
+
diff --git a/src/software/openlmi/software/core/SoftwareFileCheck.py b/src/software/openlmi/software/core/SoftwareFileCheck.py
new file mode 100644
index 0000000..fe844c1
--- /dev/null
+++ b/src/software/openlmi/software/core/SoftwareFileCheck.py
@@ -0,0 +1,515 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Just a common functionality related to SoftwareFileCheck provider.
+"""
+
+import collections
+import hashlib
+import os
+import pywbem
+import stat
+import yum
+
+from openlmi.software import util
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import YumDB
+from openlmi.software.yumdb import packageinfo
+from openlmi.software.yumdb import packagecheck
+
+PASSED_FLAGS_DESCRIPTIONS = (
+ "Existence",
+ "File Type",
+ "File Size",
+ "File Mode",
+ "File Checksum",
+ "Device major/minor number",
+ "Symlink Target",
+ "User Ownership", "Group Ownership",
+ "Modify Time")
+
+# Named tuple to store results of rpm file check as pywbem values, all results
+# are in the 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', #pylint: disable=C0103
+ 'exists, md5_checksum, file_type, file_size, file_mode, '
+ 'file_checksum, device, link_target, user_id, group_id, '
+ 'last_modification_time')
+
+@cmpi_logging.trace_function
+def checksumtype_num2hash(csumt):
+ """
+ @param csumt checksum type as a number obtained from package
+ @return hash function object corresponding to csumt
+ """
+ return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
+
+@cmpi_logging.trace_function
+def checksumtype_str2pywbem(alg):
+ """
+ @param alg is a name of algorithm used for checksum
+ @return pywbem number corresponding to given alg
+ """
+ try:
+ res = packagecheck.CHECKSUMTYPE_STR2NUM[alg.lower()]
+ except KeyError:
+ res = 0
+ return pywbem.Uint16(res)
+
+@cmpi_logging.trace_function
+def filetype_str2pywbem(file_type):
+ """
+ @param file_type is a name of file type obtained from pkg headers
+ @return pywbem number corresponding to thus file type
+ """
+ try:
+ return pywbem.Uint16(
+ { 'file' : Values.FileType.File
+ , 'directory' : Values.FileType.Directory
+ , 'symlink' : Values.FileType.Symlink
+ , 'fifo' : Values.FileType.FIFO
+ , 'character device' : Values.FileType.Character_Device
+ , 'block device' : Values.FileType.Block_Device
+ }[file_type])
+ except KeyError:
+ return Values.FileType.Unknown
+
+@cmpi_logging.trace_function
+def filetype_mode2pywbem(mode):
+ """
+ @param mode is a raw file mode as integer
+ @return pywbem numeric value of file's type
+ """
+ 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)
+
+@cmpi_logging.trace_function
+def mode2pywbem_flags(mode):
+ """
+ @param mode if None, file does not exist
+ @return list of integer flags describing file's access permissions
+ """
+ if mode is None:
+ return None
+ flags = []
+ for i, flag 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 flag & mode:
+ flags.append(pywbem.Uint8(i))
+ return flags
+
+@cmpi_logging.trace_function
+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 hashfunc in hashers:
+ hashfunc.update(buf)
+ buf = afile.read(blocksize)
+ return [ hashfunc.hexdigest() for hashfunc in hashers ]
+
+@cmpi_logging.trace_function
+def compute_checksums(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()] #pylint: disable=E1101
+ if checksum_type != packagecheck.CHECKSUMTYPE_STR2NUM["md5"]:
+ hashers.append(checksumtype_num2hash(checksum_type)())
+ if file_type != filetype_str2pywbem('file'):
+ rslts = ['0'*len(h.hexdigest()) for h in hashers]
+ else:
+ try:
+ with open(file_path, 'rb') as fobj:
+ rslts = hashfile(fobj, hashers)
+ except (OSError, IOError) as exc:
+ cmpi_logging.logger.error("could not open file \"%s\""
+ " for reading: %s", file_path, exc)
+ return None, None
+ return (rslts[0], rslts[1] if len(rslts) > 1 else rslts[0]*2)
+
+@cmpi_logging.trace_function
+def object_path2pkg_file(objpath):
+ """
+ @return (package_info, package_check)
+ """
+ if not isinstance(objpath, pywbem.CIMInstanceName):
+ raise TypeError("objpath must be instance of CIMInstanceName, "
+ "not \"%s\"" % objpath.__class__.__name__)
+
+ if ( not objpath['Name'] or not objpath['SoftwareElementID']
+ or not objpath['CheckID']
+ or not objpath['CheckID'].endswith('#'+objpath['Name'])
+ or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+ if objpath['SoftwareElementState'] not in ("2", 2):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Only \"Executable\" software element state supported")
+ if not util.check_target_operating_system(objpath['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not objpath['Name'] or not objpath['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ match = util.RE_NEVRA_OPT_EPOCH.match(objpath['SoftwareElementID'])
+ if not match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-epoch:version-release.arch).")
+ if objpath['Version'] != match.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+
+ with YumDB.getInstance() as ydb:
+ pkglist = ydb.filter_packages('installed', **util.nevra2filter(match))
+ if len(pkglist) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package installed.")
+ pkg = pkglist[0]
+ pkg_check = ydb.check_package(pkg)
+ return (pkg, pkg_check, pkg_check[objpath["Name"]])
+
+@cmpi_logging.trace_function
+def test_file(checksum_type, package_file):
+ """
+ @param checksum type is a pywbem value for ChecksumType property
+ @return instance of FileCheck
+ """
+ if not isinstance(package_file, packagecheck.PackageFile):
+ raise TypeError("package_file must be an instance of PackageFile"
+ " not \"%s\"" % package_file.__class__.__name__)
+ exists = os.path.lexists(package_file.path)
+ md5_checksum = None
+ expected = {
+ "file_type" : filetype_str2pywbem(package_file.file_type),
+ "user_id" : pywbem.Uint32(package_file.uid),
+ "group_id" : pywbem.Uint32(package_file.gid),
+ "file_mode" : pywbem.Uint32(package_file.mode),
+ "file_size" : pywbem.Uint64(package_file.size),
+ "link_target" : package_file.link_target,
+ "file_checksum" : package_file.checksum,
+ "device" : pywbem.Uint64(package_file.device)
+ if package_file.device is not None else None,
+ "last_modification_time" : pywbem.Uint64(package_file.mtime)
+ }
+ if not exists:
+ reality = collections.defaultdict(lambda: None)
+ else:
+ fstat = os.lstat(package_file.path)
+ reality = {
+ "file_type" : 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'] == filetype_str2pywbem("device")
+ else None)
+ reality["link_target"] = (os.readlink(package_file.path)
+ if os.path.islink(package_file.path) else None)
+ md5_checksum, checksum = compute_checksums(
+ checksum_type, reality["file_type"], package_file.path)
+ reality["file_checksum"] = checksum
+ kwargs = dict(exists=exists, md5_checksum=md5_checksum,
+ **dict((k, (expected[k], reality[k])) for k in expected))
+ return FileCheck(**kwargs)
+
+@cmpi_logging.trace_function
+def _filecheck2model_flags(file_check):
+ """
+ @param file_check is an instance of FileCheck
+ @return pywbem value for PassedFlags property
+ """
+ if not isinstance(file_check, FileCheck):
+ raise TypeError("file_check must be an instance of FileCheck")
+ flags = []
+ for k, value in file_check._asdict().items(): #pylint: disable=W0212
+ if isinstance(value, tuple):
+ if ( k in ("last_modification_time", "file_size")
+ and file_check.file_type[0] != filetype_str2pywbem('file')):
+ # last_modification_time check is valid only for
+ # regular files
+ flag = file_check.exists
+ elif ( k == "file_mode"
+ and file_check.file_type[0] == filetype_str2pywbem('symlink')):
+ # do not check mode of symlinks
+ flag = ( file_check.exists
+ and file_check.file_type[0] == file_check.file_type[1])
+ else:
+ flag = file_check.exists and value[0] == value[1]
+ flags.append(flag)
+ elif isinstance(value, bool):
+ flags.append(value)
+ return flags
+
+@cmpi_logging.trace_function
+def filecheck_passed(file_check):
+ """
+ @return True if installed file passed all checks.
+ """
+ return all(_filecheck2model_flags(file_check))
+
+@cmpi_logging.trace_function
+def _fill_non_key_values(model, pkg_check, pkg_file, file_check=None):
+ """
+ Fills a non key values into instance of SoftwareFileCheck.
+ """
+ model['FileName'] = os.path.basename(pkg_file.path)
+ model['FileChecksumType'] = csumt = pywbem.Uint16(
+ pkg_check.file_checksum_type)
+ if file_check is None:
+ file_check = test_file(csumt, pkg_file)
+ 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'),
+ ('FileChecksum', 'file_checksum')):
+ exp, rea = getattr(file_check, fattr)
+ if exp is not None:
+ model['Expected' + mattr] = exp
+ if rea is not None:
+ model[mattr] = rea
+ model['ExpectedFileModeFlags'] = mode2pywbem_flags(file_check.file_mode[0])
+ if file_check.exists:
+ model['FileModeFlags'] = mode2pywbem_flags(file_check.file_mode[1])
+ model['FileExists'] = file_check.exists
+ if file_check.md5_checksum is not None:
+ model['MD5Checksum'] = file_check.md5_checksum
+ model['PassedFlags'] = _filecheck2model_flags(file_check)
+ model['PassedFlagsDescriptions'] = list(PASSED_FLAGS_DESCRIPTIONS)
+
+@cmpi_logging.trace_function
+def filecheck2model(package_info, package_check, file_name, keys_only=True,
+ model=None, file_check=None):
+ """
+ @param package_file is an instance of yumdb.PackageFile
+ @param file_name a absolute file path contained in package
+ @param keys_only if True, then only key values will be filed
+ @param model if given, then this instance will be modified and
+ returned
+ @param file_check if not given, it will be computed
+ @return instance of LMI_SoftwareFileCheck class with all desired
+ values filed
+ """
+ if not isinstance(package_info, packageinfo.PackageInfo):
+ raise TypeError(
+ "package_info must be an instance ofyumdb.PackageInfo")
+ if not isinstance(package_check, packagecheck.PackageCheck):
+ raise TypeError(
+ "package_check must be an instance of yumdb.PackageFile")
+ if not file_name in package_check:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "File \"%s\" not found among package files" % file_name)
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareFileCheck",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareFileCheck", path=model)
+ package_file = package_check[file_name]
+ model['Name'] = package_file.path
+ model['SoftwareElementID'] = package_info.nevra
+ model['SoftwareElementState'] = Values.SoftwareElementState.Executable
+ model['TargetOperatingSystem'] = pywbem.Uint16(
+ util.get_target_operating_system()[0])
+ model['Version'] = package_info.version
+ model['CheckID'] = '%s#%s' % (package_info.name, package_file.path)
+ if not keys_only:
+ if file_check is not None:
+ if not isinstance(file_check, FileCheck):
+ raise TypeError("file_check must be an instance of FileCheck")
+ _fill_non_key_values(model, package_check, package_file, file_check)
+ return model
+
+class Values(object):
+ """
+ Enumerations of LMI_SoftwareFileCheck class properties.
+ """
+ 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)
+
diff --git a/src/software/openlmi/software/core/SoftwareInstalledPackage.py b/src/software/openlmi/software/core/SoftwareInstalledPackage.py
new file mode 100644
index 0000000..f91fb56
--- /dev/null
+++ b/src/software/openlmi/software/core/SoftwareInstalledPackage.py
@@ -0,0 +1,39 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Just a common functionality related to LMI_SoftwarePackage provider.
+"""
+
+import pywbem
+
+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)
+
diff --git a/src/software/openlmi/software/core/SoftwarePackage.py b/src/software/openlmi/software/core/SoftwarePackage.py
new file mode 100644
index 0000000..3e7284f
--- /dev/null
+++ b/src/software/openlmi/software/core/SoftwarePackage.py
@@ -0,0 +1,412 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Just a common functionality related to LMI_SoftwarePackage provider.
+"""
+
+import pywbem
+
+from openlmi.software import util
+from openlmi.software.util import cmpi_logging
+from openlmi.software.yumdb import PackageInfo, YumDB
+
+@cmpi_logging.trace_function
+def object_path2pkg(objpath, kind='installed'):
+ """
+ @param objpath must contain precise information of package,
+ otherwise a CIM_ERR_NOT_FOUND error is raised
+ @param kind one of {'installed', 'all', 'available'}
+ says, where to look for given package
+ """
+ if not isinstance(objpath, pywbem.CIMInstanceName):
+ raise TypeError("objpath must be an instance of CIMInstanceName")
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+ if not kind in ('installed', 'all', 'available'):
+ raise ValueError('unsupported package list "%s"' % kind)
+
+ if ( not objpath['Name'] or not objpath['SoftwareElementID']
+ or not objpath['SoftwareElementID'].startswith(objpath['Name'])
+ or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+ if not util.check_target_operating_system(objpath['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not objpath['Name'] or not objpath['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ match = util.RE_NEVRA_OPT_EPOCH.match(objpath['SoftwareElementID'])
+ if not match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-[epoch:]version-release.arch).")
+ if objpath['Version'] != match.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+ pkglist = YumDB.getInstance().filter_packages(kind,
+ allow_duplicates=kind != 'installed',
+ **util.nevra2filter(match))
+ if len(pkglist) > 0:
+ return pkglist[0]
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package found.")
+
+@cmpi_logging.trace_function
+def object_path2pkg_search(objpath):
+ """
+ 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 objpath may be object of CIMInstance or CIMInstanceName and
+ must contain at least \"Name\" or \"SoftwareElementID\"
+ @return instance PackageInfo
+ """
+ if isinstance(objpath, pywbem.CIMInstance):
+ def _get_key(k):
+ """@return value of instance's key"""
+ value = objpath.properties.get(k, None)
+ if isinstance(value, pywbem.CIMProperty):
+ return value.value
+ if value is not None:
+ return value
+ cmpi_logging.logger.error(
+ 'missing key "%s" in inst.props', k)
+ return objpath.path[k] if k in objpath.path else None
+ elif isinstance(objpath, pywbem.CIMInstanceName):
+ _get_key = lambda k: objpath[k] if k in objpath else None
+ else:
+ raise TypeError("objpath must be either CIMInstance"
+ "or CIMInstanceName")
+
+ # parse and check arguments
+ filters = {}
+ if _get_key('SoftwareElementID'):
+ match = util.RE_NEVRA_OPT_EPOCH.match(_get_key('SoftwareElementID'))
+ if not match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "SoftwareElementID could not be parsed.")
+ filters = util.nevra2filter(match)
+ else:
+ for k in ('name', 'epoch', 'version', 'release', 'arch'):
+ ikey = k if k != 'arch' else "architecture"
+ if _get_key(ikey):
+ filters[k] = _get_key(ikey)
+
+ if not filters:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Too few key values given (give at least a Name).")
+ if not 'name' in filters:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Missing either Name or SoftwareElementID property.")
+
+ pkglist = YumDB.getInstance().filter_packages('all',
+ allow_duplicates=True, sort=True, **filters)
+ if len(pkglist) == 0:
+ cmpi_logging.logger.error(
+ 'could not find any matching package in list: %s',
+ [p.nevra for p in pkglist])
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package found.")
+ for pkg in pkglist: # check, whether package is already installed
+ if pkg.installed:
+ return pkg
+ cmpi_logging.logger.info(
+ ( 'found multiple matching packages'
+ if len(pkglist) > 1 else 'exact match found'))
+ return pkglist[-1] # select highest version
+
+@cmpi_logging.trace_function
+def pkg2model(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, PackageInfo):
+ raise TypeError("pkg must be an instance of PackageInfo")
+ if model is None:
+ model = pywbem.CIMInstanceName('LMI_SoftwarePackage',
+ namespace='root/cimv2')
+ if not keys_only:
+ model = pywbem.CIMInstance('LMI_SoftwarePackage', path=model)
+ if isinstance(model, pywbem.CIMInstance):
+ def _set_key(k, value):
+ """Sets the value of key property of cim instance"""
+ model[k] = value
+ model.path[k] = value #pylint: disable=E1103
+ else:
+ _set_key = model.__setitem__
+ _set_key('Name', pkg.name)
+ _set_key('SoftwareElementID', pkg.nevra)
+ _set_key('SoftwareElementState',
+ Values.SoftwareElementState.Executable
+ if pkg.installed
+ else Values.SoftwareElementState.Installable)
+ _set_key('TargetOperatingSystem',
+ pywbem.Uint16(util.get_target_operating_system()[0]))
+ _set_key('Version', pkg.version)
+ if not keys_only:
+ model['Caption'] = pkg.summary
+ model['Description'] = pkg.description
+ if pkg.installed:
+ model['InstallDate'] = pywbem.CIMDateTime(pkg.install_time)
+ 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
+
+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)
+
diff --git a/src/software/openlmi/software/core/SoftwarePackageChecks.py b/src/software/openlmi/software/core/SoftwarePackageChecks.py
new file mode 100644
index 0000000..6d39294
--- /dev/null
+++ b/src/software/openlmi/software/core/SoftwarePackageChecks.py
@@ -0,0 +1,32 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Just a common functionality related to LMI_SoftwarePackageChecks provider.
+"""
+
+import pywbem
+
+class Values(object):
+ class Phase(object):
+ In_State = pywbem.Uint16(0)
+ Next_State = pywbem.Uint16(1)
diff --git a/src/software/openlmi/software/core/__init__.py b/src/software/openlmi/software/core/__init__.py
new file mode 100644
index 0000000..2ebe827
--- /dev/null
+++ b/src/software/openlmi/software/core/__init__.py
@@ -0,0 +1,20 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
diff --git a/src/software/openlmi/software/util/__init__.py b/src/software/openlmi/software/util/__init__.py
index 2ebe827..e48ea29 100644
--- a/src/software/openlmi/software/util/__init__.py
+++ b/src/software/openlmi/software/util/__init__.py
@@ -1,3 +1,4 @@
+# -*- encoding: utf-8 -*-
# Software Management Providers
#
# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
@@ -18,3 +19,136 @@
#
# Authors: Michal Minar <miminar@redhat.com>
#
+
+"""Common utilities for LMI_Software* providers
+"""
+
+import platform
+import re
+
+RE_EVRA = re.compile(
+ r'^(?P<epoch>\d+):(?P<ver>[^-]+)-(?P<rel>.+)\.(?P<arch>[^.]+)$')
+RE_NEVRA = re.compile(
+ r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+RE_NEVRA_OPT_EPOCH = re.compile(
+ r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+RE_ENVRA = re.compile(
+ r'^(?P<epoch>\d+):(?P<name>.+)-(?P<evra>(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+
+def _get_distname():
+ """
+ @return name of linux distribution
+ """
+ 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 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 %s" %
+ 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 nevra2filter(nevra):
+ """
+ Takes either regexp match object resulting from RE_NEVRA match or
+ a nevra string.
+ @return dictionary with package filter key-value pairs made from nevra
+ """
+ if isinstance(nevra, basestring):
+ match = RE_NEVRA_OPT_EPOCH.match(nevra)
+ elif nevra.__class__.__name__.lower() == "sre_match":
+ match = nevra
+ else:
+ raise TypeError("nevra must be either string or regexp match object")
+ epoch = match.group("epoch")
+ if not epoch or match.group("epoch") == "(none)":
+ epoch = "0"
+ return { "name" : match.group("name")
+ , "epoch" : epoch
+ , "version" : match.group("ver")
+ , "release" : match.group("rel")
+ , "arch" : match.group("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 pkg2nevra(pkg, with_epoch='NOT_ZERO'):
+ """
+ @return nevra string made of pkg
+ """
+ return make_nevra(pkg.name, pkg.epoch, pkg.version,
+ pkg.release, pkg.arch, with_epoch)
diff --git a/src/software/openlmi/software/util/cmpi_logging.py b/src/software/openlmi/software/util/cmpi_logging.py
new file mode 100644
index 0000000..2db14b6
--- /dev/null
+++ b/src/software/openlmi/software/util/cmpi_logging.py
@@ -0,0 +1,203 @@
+# -*- Coding:utf-8 -*-
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Jan Safranek <jsafrane@redhat.com>
+
+
+import logging
+import inspect
+import traceback
+
+TRACE_WARNING = logging.INFO - 1
+TRACE_INFO = logging.INFO - 2
+TRACE_VERBOSE = logging.DEBUG
+
+class CMPILogHandler(logging.Handler):
+ """
+ A handler class, which sends log messages to CMPI log.
+ """
+
+ def __init__(self, cmpi_logger, *args, **kwargs):
+ self.cmpi_logger = cmpi_logger
+ super(CMPILogHandler, self).__init__(*args, **kwargs)
+
+ def emit(self, record):
+ msg = self.format(record)
+ if record.levelno >= logging.ERROR:
+ self.cmpi_logger.log_error(msg)
+ elif record.levelno >= logging.WARNING:
+ self.cmpi_logger.log_warn(msg)
+ elif record.levelno >= logging.INFO:
+ self.cmpi_logger.log_info(msg)
+ elif record.levelno >= TRACE_WARNING:
+ self.cmpi_logger.trace_warn(record.filename, msg)
+ elif record.levelno >= TRACE_INFO:
+ self.cmpi_logger.trace_info(record.filename, msg)
+ elif record.levelno >= logging.DEBUG:
+ self.cmpi_logger.trace_verbose(record.filename, msg)
+
+class CMPILogger(logging.getLoggerClass()):
+ """
+ A logger class, which adds trace_method level log methods.
+ """
+ def trace_warn(self, msg, *args, **kwargs):
+ """ Log message with TRACE_WARNING severity. """
+ self.log(TRACE_WARNING, msg, *args, **kwargs)
+
+ def trace_info(self, msg, *args, **kwargs):
+ """ Log message with TRACE_INFO severity. """
+ self.log(TRACE_INFO, msg, *args, **kwargs)
+
+ def trace_verbose(self, msg, *args, **kwargs):
+ """ Log message with TRACE_VERBOSE severity. """
+ self.log(TRACE_VERBOSE, msg, *args, **kwargs)
+
+logging.setLoggerClass(CMPILogger)
+
+def trace_method(func):
+ """ Decorator, trace entry and exit for a class method. """
+ classname = inspect.getouterframes(inspect.currentframe())[1][3]
+ def helper_func(*args, **kwargs):
+ """
+ Helper function, wrapping real function by trace_method decorator.
+ """
+ logger.log(TRACE_VERBOSE, "Entering %s.%s", classname, func.__name__)
+ try:
+ ret = func(*args, **kwargs)
+ except Exception as exc:
+ if getattr(exc, "tb_printed", False) is False:
+ logger.exception("full traceback")
+ logger.log(TRACE_VERBOSE, "traceback: %s",
+ traceback.format_exc())
+ exc.tb_printed = True
+ logger.log(TRACE_WARNING, "%s.%s threw exception %s",
+ classname, func.__name__, str(exc))
+ raise
+ logger.log(TRACE_VERBOSE, "Exiting %s.%s", classname, func.__name__)
+ return ret
+ helper_func.__name__ = func.__name__
+ helper_func.__doc__ = func.__doc__
+ helper_func.__module__ = func.__module__
+ return helper_func
+
+def trace_function(func):
+ """ Decorator, trace entry and exit for a function outside any class. """
+ def helper_func(*args, **kwargs):
+ """
+ Helper function, wrapping real function by trace_method decorator.
+ """
+ logger.log(TRACE_VERBOSE, "Entering %s.%s",
+ func.__module__, func.__name__)
+ try:
+ ret = func(*args, **kwargs)
+ except Exception as exc:
+ if getattr(exc, "tb_printed", False) is False:
+ logger.exception("full traceback")
+ logger.log(TRACE_VERBOSE, "traceback: %s",
+ traceback.format_exc())
+ exc.tb_printed = True
+ logger.log(TRACE_WARNING, "%s.%s threw exception %s",
+ func.__module__, func.__name__, str(exc))
+ raise
+ logger.log(TRACE_VERBOSE, "Exiting %s", func.__name__)
+ return ret
+ helper_func.__name__ = func.__name__
+ helper_func.__doc__ = func.__doc__
+ helper_func.__module__ = func.__module__
+ return helper_func
+
+class LogManager(object):
+ """
+ Class, which takes care of CMPI logging.
+ There should be only one instance of this class and it should be
+ instantiated as soon as possible, even before reading a config.
+ The config file can be provided later by set_config call.
+ """
+ FORMAT_STDERR = '%(levelname)s: %(message)s'
+ FORMAT_CMPI = '%(levelname)s: %(message)s'
+
+ LOGGER_NAME = "openlmi.storage"
+
+ def __init__(self, env):
+ """
+ Initialize logging.
+ """
+ formatter = logging.Formatter(self.FORMAT_CMPI)
+
+ self.cmpi_handler = CMPILogHandler(env.get_logger())
+ self.cmpi_handler.setLevel(logging.DEBUG)
+ self.cmpi_handler.setFormatter(formatter)
+
+ self.logger = logging.getLogger(self.LOGGER_NAME)
+ self.logger.addHandler(self.cmpi_handler)
+ self.logger.setLevel(logging.DEBUG)
+
+ self.stderr_handler = None
+ self.config = None
+
+ global logger # IGNORE:W0603
+ logger = self.logger
+ logger.info("CMPI log started")
+
+ @trace_method
+ def set_config(self, config):
+ """
+ Set a configuration of logging. It applies its setting immediately
+ and also subscribes for configuration changes.
+ """
+ self.config = config
+ config.add_listener(self._config_changed)
+ # apply the config
+ self._config_changed(config)
+
+ @trace_method
+ def _config_changed(self, config):
+ """
+ Apply changed configuration, i.e. start/stop sending to stderr
+ and set appropriate log level.
+ """
+ if config.tracing:
+ self.logger.setLevel(logging.DEBUG)
+ else:
+ self.logger.setLevel(logging.INFO)
+ if config.stderr:
+ # start sending to stderr
+ if not self.stderr_handler:
+ # create stderr handler
+ formatter = logging.Formatter(self.FORMAT_STDERR)
+ self.stderr_handler = logging.StreamHandler()
+ self.stderr_handler.setLevel(logging.DEBUG)
+ self.stderr_handler.setFormatter(formatter)
+ self.logger.addHandler(self.stderr_handler)
+ self.logger.info("Started logging to stderr.")
+ else:
+ # stop sending to stderr
+ if self.stderr_handler:
+ self.logger.info("Stopped logging to stderr.")
+ self.logger.removeHandler(self.stderr_handler)
+ self.stderr_handler = None
+
+ def destroy(self):
+ if self.stderr_handler:
+ self.logger.removeHandler(self.stderr_handler)
+ self.stderr_handler = None
+ self.logger.removeHandler(self.cmpi_handler)
+ self.cmpi_handler = None
+ self.config.remove_listener(self._config_changed)
+
+logger = None
diff --git a/src/software/openlmi/software/util/common.py b/src/software/openlmi/software/util/common.py
deleted file mode 100644
index 685171a..0000000
--- a/src/software/openlmi/software/util/common.py
+++ /dev/null
@@ -1,825 +0,0 @@
-# -*- encoding: utf-8 -*-
-# Software Management Providers
-#
-# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-#
-# Authors: Michal Minar <miminar@redhat.com>
-#
-
-"""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
-from openlmi.software.util 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):
- """
- Context manager for accessing yum/rpm database.
- """
-
- # this is to inform Singleton, that __init__ should be called only once
- ignoreSubsequent = True
-
- def __init__(self, env, *args, **kwargs): #pylint: disable=W0231
- 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 True if rpm database has been modified since last update
- """
- return self._db_mtime < os.stat(RPMDB_PATH).st_mtime
-
- def is_locked(self):
- """
- @return True if rpm database is locked
- """
- return self._yum._lockfile is not None
-
- def update_db(self):
- """
- Call to update database metadata.
- """
- 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():
- """
- @return name of linux distribution
- """
- 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(prefix='Linux'):
- """
- @return object path of CIM_ComputerSystem for this system
- """
- return pywbem.CIMInstanceName(
- classname='%s_ComputerSystem' % prefix,
- keybindings={
- "CreationClassName": "%s_ComputerSystem" % prefix
- , "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):
- """
- @param system is object path referring to CIM_ComputerSystem instance
- passed as argument to some cim function
- @return True if this instance matches our system; otherwise a CIMError
- will be raised
- """
- 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('CIM')
- chandle = env.get_cimom_handle()
- if not chandle.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 chandle.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 attr in ( 'evra', 'nevra', 'name', 'epoch'
- , 'version', 'release', 'arch'):
- value = kwargs.get(attr, None)
- if value and getattr(pkg, attr) != value:
- return False
- return True
-
-class SoftwarePackage:
- """
- Just a namespace for common function related to SoftwarePackage provider.
- TODO: make it a submodule
- """
-
- @staticmethod
- def object_path2pkg(env, objpath, package_list='installed'):
- """
- @param objpath 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(objpath, pywbem.CIMInstanceName):
- raise TypeError("objpath 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)
-
- if ( not objpath['Name'] or not objpath['SoftwareElementID']
- or not objpath['SoftwareElementID'].startswith(objpath['Name'])
- or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
- if not check_target_operating_system(objpath['TargetOperatingSystem']):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong target operating system.")
- if not objpath['Name'] or not objpath['Version']:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- 'Both "Name" and "Version" must be given')
- match = RE_NEVRA.match(objpath['SoftwareElementID'])
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong SotwareElementID. Expected valid nevra"
- " (name-[epoch:]version-release.arch).")
- if objpath['Version'] != match.group('ver'):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Version does not match version part in SoftwareElementID.")
- evra = "{}:{}-{}.{}".format(*(
- (match.group(k) if k != "epoch" or match.group(k) else "0")
- for k in ("epoch", 'ver', 'rel', 'arch')))
- with YumDB.getInstance(env) as ydb:
- pkglist = ydb.doPackageLists(package_list,
- showdups=package_list != 'installed')
- if package_list != 'all':
- pkglist = getattr(pkglist, package_list)
- else:
- # NOTE: available ∩ installed = ∅
- pkglist = itertools.chain(pkglist.available, pkglist.installed)
- exact, _, _ = yum.packages.parsePackages(pkglist, [objpath['Name']])
- for pkg in yum.misc.unique(exact):
- if pkg.evra == evra:
- return pkg
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "No matching package found.")
-
- @staticmethod
- def object_path2pkg_search(env, objpath):
- """
- 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 objpath 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(objpath, pywbem.CIMInstance):
- def _get_key(k):
- """@return value of instance's key"""
- value = objpath.properties.get(k, None)
- if isinstance(value, pywbem.CIMProperty):
- return value.value
- if value is not None:
- return value
- logger.log_error('missing key "{}" in inst.props'.format(k))
- return objpath.path[k] if k in objpath.path else None
- elif isinstance(objpath, pywbem.CIMInstanceName):
- _get_key = lambda k: objpath[k] if k in objpath else None
- else:
- raise TypeError("objpath must be either CIMInstance"
- "or CIMInstanceName")
-
- # parse and check arguments
- match_props = {} # args for match_pkg
- if _get_key('SoftwareElementID'):
- match = RE_NEVRA.match(_get_key('SoftwareElementID'))
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
- "SoftwareElementID could not be parsed.")
- for k in ('name', 'version', 'release', 'arch'):
- mkey = k if k not in ('version', 'release') else k[:3]
- match_props[k] = match.group(mkey)
- if not match.group("epoch"):
- match_props["epoch"] = "0"
- else:
- for k in ('name', 'epoch', 'version', 'release', 'arch'):
- ikey = k if k != 'arch' else "architecture"
- if _get_key(ikey):
- match_props[k] = _get_key(ikey)
-
- 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:
- raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
- "Missing either Name or SoftwareElementID property.")
-
- # get available packages
- pkglist = YumDB.getInstance(env).doPackageLists('all', showdups=True)
- # NOTE: available ∩ installed = ∅
- exact, _, _ = yum.packages.parsePackages(
- itertools.chain(pkglist.available, pkglist.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) ])
- 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(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('LMI_SoftwarePackage',
- namespace='root/cimv2')
- if not keys_only:
- model = pywbem.CIMInstance('LMI_SoftwarePackage', path=model)
- if isinstance(model, pywbem.CIMInstance):
- def _set_key(k, value):
- """Sets the value of key property of cim instance"""
- model[k] = value
- model.path[k] = value #pylint: disable=E1103
- 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',
- pywbem.Uint16(get_target_operating_system()[0]))
- _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
-
-class SoftwareFileCheck:
- """
- Just a namespace for functions related to FileCheck provider.
- TODO: make it a submodule
- """
-
- passed_flags_descriptions = (
- "Existence",
- "File Type",
- "File Size",
- "File Mode",
- "File Checksum",
- "Device major/minor number",
- "Symlink Target",
- "User Ownership", "Group Ownership",
- "Modify Time")
-
- checksumtype_str2num = dict((val, k) for (k, val) 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 YumDB.getInstance(): # ensure, that _yum is inited
- return SoftwareFileCheck.checksumtype_str2pywbem(
- pkg.yumdb_info.checksum_type)
-
- @staticmethod
- def checksumtype_num2hash(csumt):
- """
- @param csumt checksum type as a number obtained from package
- @return hash function object corresponding to csumt
- """
- return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
-
- @staticmethod
- def checksumtype_str2pywbem(alg):
- """
- @param alg is a name of algorithm used for checksum
- @return pywbem number corresponding to given alg
- """
- try:
- res = SoftwareFileCheck.checksumtype_str2num[alg.lower()]
- except KeyError:
- res = 0
- return pywbem.Uint16(res)
-
- @staticmethod
- def filetype_str2pywbem(file_type):
- """
- @param file_type is a name of file type obtained from pkg headers
- @return pywbem number corresponding to thus file type
- """
- try:
- return pywbem.Uint16(
- { 'file' : 1
- , 'directory' : 2
- , 'symlink' : 3
- , 'fifo' : 4
- , 'character device' : 5
- , 'block device' : 6
- }[file_type])
- except KeyError:
- return pywbem.Uint16(0)
-
- @staticmethod
- def filetype_mode2pywbem(mode):
- """
- @param mode is a raw file mode as integer
- @return pywbem numeric value of file's type
- """
- 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
- @return list of integer flags describing file's access permissions
- """
- if mode is None:
- return None
- flags = []
- for i, flag 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 flag & 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 hashfunc in hashers:
- hashfunc.update(buf)
- buf = afile.read(blocksize)
- return [ hashfunc.hexdigest() for hashfunc 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()] #pylint: disable=E1101
- 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 fobj:
- rslts = SoftwareFileCheck.hashfile(fobj, hashers)
- except (OSError, IOError) as exc:
- env.get_logger().log_error("could not open file \"%s\""
- " for reading: %s" % (file_path, exc))
- return None, None
- return (rslts[0], rslts[1] if len(rslts) > 1 else rslts[0]*2)
-
- @staticmethod
- def object_path2yumcheck(env, objpath):
- """
- @return instance of yum.packages._RPMVerifyPackage
- this object holds RPMInstalledPackage under its po attribute
- """
- if not isinstance(objpath, pywbem.CIMInstanceName):
- raise TypeError("objpath must be instance of CIMInstanceName, "
- "not \"%s\"" % objpath.__class__.__name__)
-
- if ( not objpath['Name'] or not objpath['SoftwareElementID']
- or not objpath['CheckID']
- or not objpath['CheckID'].endswith('#'+objpath['Name'])
- or objpath['SoftwareElementID'].find(objpath['Version']) == -1):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
- if objpath['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(objpath['TargetOperatingSystem']):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong target operating system.")
- if not objpath['Name'] or not objpath['Version']:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- 'Both "Name" and "Version" must be given')
- match = RE_NEVRA.match(objpath['SoftwareElementID'])
- if not match:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Wrong SotwareElementID. Expected valid nevra"
- " (name-epoch:version-release.arch).")
- if objpath['Version'] != match.group('ver'):
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "Version does not match version part in SoftwareElementID.")
-
- evra = "{}:{}-{}.{}".format(*(
- (match.group(k) if k != "epoch" or match.group(k) else "0")
- for k in ("epoch", 'ver', 'rel', 'arch')))
- with YumDB.getInstance(env) as ydb:
- pkglist = ydb.doPackageLists('installed')
- exact, _, _ = yum.packages.parsePackages(
- pkglist.installed, [match.group('name')])
- for pkg in yum.misc.unique(exact):
- if pkg.evra != evra:
- continue
- vpkg = yum.packages._RPMVerifyPackage(
- pkg, pkg.hdr.fiFromHeader(),
- SoftwareFileCheck.pkg_checksum_type(pkg), [], True)
- if not objpath['Name'] in vpkg:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "File not found in RPM package.")
- return vpkg
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "No matching package installed.")
-
- # 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, '
- 'file_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,
- "file_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["file_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(file_check):
- """
- @return True if installed file passed all checks.
- """
- if not isinstance(file_check, SoftwareFileCheck.FileCheck):
- raise TypeError("file_check must be an instance of FileCheck")
- passed = file_check.exists
- for k, val in file_check._asdict().items():
- if not passed:
- break
- if not isinstance(val, tuple):
- continue
- if ( k == "last_modification_time"
- and file_check.file_type[0] != \
- SoftwareFileCheck.filetype_str2pywbem("file")):
- continue
- if ( k == "file_mode"
- and file_check.file_type[0] == \
- SoftwareFileCheck.filetype_str2pywbem("symlink")):
- continue
- passed = val[0] == val[1]
- return passed
-
- @staticmethod
- def _filecheck2model_flags(file_check):
- """
- @param file_check is an instance of FileCheck
- @return pywbem value for PassedFlags property
- """
- flags = []
- for k, value in file_check._asdict().items(): #pylint: disable=W0212
- if isinstance(value, tuple):
- if ( k == "last_modification_time"
- and file_check.file_type[0] != \
- SoftwareFileCheck.filetype_str2pywbem('file')):
- # last_modification_time check is valid only for
- # regular files
- flag = file_check.exists
- elif ( k == "file_mode"
- and file_check.file_type[0] == \
- SoftwareFileCheck.filetype_str2pywbem('symlink')):
- # do not check mode of symlinks
- flag = ( file_check.exists
- and ( file_check.file_type[0]
- == file_check.file_type[1]))
- else:
- flag = file_check.exists and value[0] == value[1]
- flags.append(flag)
- elif isinstance(value, bool):
- flags.append(value)
- return flags
-
- @staticmethod
- def _fill_non_key_values(env, model, pkg, vpf, file_check):
- """
- Fills a non key values into instance of SoftwareFileCheck.
- """
- model['FileName'] = os.path.basename(vpf.filename)
- model['FileChecksumType'] = csumt = \
- pywbem.Uint16(SoftwareFileCheck.pkg_checksum_type(pkg))
- if file_check is None:
- file_check = 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'),
- ('FileChecksum', 'file_checksum')):
- exp, rea = getattr(file_check, fattr)
- if exp is not None:
- model['Expected' + mattr] = exp
- if rea is not None:
- model[mattr] = rea
- model['ExpectedFileModeFlags'] = \
- SoftwareFileCheck.mode2pywbem_flags(file_check.file_mode[0])
- if file_check.exists:
- model['FileModeFlags'] = \
- SoftwareFileCheck.mode2pywbem_flags(file_check.file_mode[1])
- model['FileExists'] = file_check.exists
- if file_check.md5_checksum is not None:
- model['MD5Checksum'] = file_check.md5_checksum
- model['PassedFlags'] = SoftwareFileCheck._filecheck2model_flags(
- file_check)
- model['PassedFlagsDescriptions'] = list(
- SoftwareFileCheck.passed_flags_descriptions)
-
- @staticmethod
- def filecheck2model(vpkg, file_name, env, keys_only=True,
- model=None, file_check=None):
- """
- @param vpkg is an instance of yum.packages_RPMVerifyPackage
- @param file_name a absolute file path contained in package
- @param keys_only if True, then only key values will be filed
- @param model if given, then this instance will be modified and
- returned
- @param file_check if not given, it will be computed
- @return instance of LMI_SoftwareFileCheck class with all desired
- values filed
- """
- if not isinstance(vpkg, yum.packages._RPMVerifyPackage):
- raise TypeError(
- "vpkg must be an instance of _RPMVerifyPackage")
- if not file_name in vpkg:
- raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
- "File \"%s\" not found among package files" % file_name)
- if model is None:
- model = pywbem.CIMInstanceName("LMI_SoftwareFileCheck",
- namespace="root/cimv2")
- if not keys_only:
- model = pywbem.CIMInstance("LMI_SoftwareFileCheck", path=model)
- if file_check is not None:
- if not isinstance(file_check, SoftwareFileCheck.FileCheck):
- raise TypeError("file_check must be an instance of FileCheck")
- pkg = vpkg.po
- vpf = vpkg._files[file_name]
- 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:
- SoftwareFileCheck._fill_non_key_values(
- env, model, pkg, vpf, file_check)
- return model
-
diff --git a/src/software/openlmi/software/yumdb/__init__.py b/src/software/openlmi/software/yumdb/__init__.py
new file mode 100644
index 0000000..54eeae0
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/__init__.py
@@ -0,0 +1,307 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Since yum API functions should not be called with different thread_ids
+repeatedly in the same program. It's neccessary, to make these calls
+in single thread. But the provider needs to be able to clean up itself,
+when its not needed. That's why the yum API needs to be accessed from
+separated process, that is created and terminated when needed.
+
+This package contains all the bowels of this separate process together
+with its management and communication facilities.
+
+YumDB is a context manager supposed to be used by any provider as the
+only accessor to yum api.
+"""
+
+import errno
+import os
+import re
+import time
+from multiprocessing import Process, JoinableQueue, Queue
+import Queue as TQueue # T as threaded
+import threading
+import yum
+
+from openlmi.software.yumdb import jobs
+from openlmi.software.yumdb import errors
+from openlmi.software.yumdb.packageinfo import PackageInfo
+from openlmi.software.yumdb.packagecheck import PackageFile
+from openlmi.software.yumdb.packagecheck import PackageCheck
+from openlmi.software.yumdb.process import YumWorker
+from openlmi.software.util import cmpi_logging
+from openlmi.software.util import singletonmixin
+
+# this may be used as an argument to YumWorker
+YUM_WORKER_DEBUG_LOGGING_CONFIG = {
+ "version" : 1,
+ "formatters": {
+ "default": {
+ "format" : "%(asctime)s %(levelname)s:%(module)s:"
+ "%(funcName)s:%(lineno)d - %(message)s"
+ }
+ },
+ "handlers": {
+ "file" : {
+ "class" : "logging.handlers.RotatingFileHandler",
+ "filename" : "/var/tmp/YumWorker.log",
+ "level" : "DEBUG",
+ "formatter": "default",
+ }
+ },
+ "root": {
+ "level": "DEBUG",
+ "handlers" : ["file"]
+ }
+ }
+
+class YumDB(singletonmixin.Singleton):
+ """
+ Context manager for accessing yum/rpm database.
+ All requests are bundled into jobs -- instances of jobs.YumJob and
+ sent to YumWorker for processing.
+
+ YumWorker is a separate process handling all calls to yum api.
+ Communication is done via queues (uplink and downlink).
+ Uplink is used to send jobs to YumWorker and downlink for obtaining
+ results.
+
+ This is implemented in thread safe manner.
+
+ It should be used as a context manager in case, we want to process
+ multiple jobs in single transaction. The example of usage:
+ with YumDB.getInstance() as ydb:
+ pkgs = ydb.filter_packages(...)
+ for pkg in pkgs:
+ ydb.install_package(pkg)
+ ...
+ Yum database stays locked in whole block of code under with statement.
+ """
+
+ # this is to inform Singleton, that __init__ should be called only once
+ ignoreSubsequent = True
+
+ def __init__(self, *args, **kwargs): #pylint: disable=W0231
+ """
+ All arguments are passed to yum.YumBase constructor.
+ """
+ self._process = None
+ self._yum_args = (args, kwargs)
+
+ # used to access _replies dictionary and _expected list
+ self._reply_lock = threading.Lock()
+ # used to wait for job to be processed and received
+ self._reply_cond = threading.Condition(self._reply_lock)
+ # { job_id : reply, ... }
+ self._replies = {}
+ # ids of all expected jobs -- those to be processed by YumWorker
+ self._expected = []
+ cmpi_logging.logger.trace_info('YumDB: initialized')
+
+ # *************************************************************************
+ # Private methods
+ # *************************************************************************
+ @cmpi_logging.trace_method
+ def _wait_for_reply(self, job):
+ """
+ Blocks until job is processed by YumWorker and received.
+
+ Only one thread can block on downlink channel to obtain reply. If
+ it's reply for him, he takes it and leaves, otherwise he adds it to
+ _replies dictionary and notifies other threads. This thread is the
+ one, whose job appears as first in _expected list.
+
+ @return result of job
+ """
+ with self._reply_lock:
+ # until our job is not at the head of
+ self._expected.append(job.jobid)
+ while job.jobid != self._expected[0]:
+ if job.jobid in self._replies:
+ self._expected.remove(job.jobid)
+ return self._replies.pop(job.jobid)
+ else:
+ self._reply_cond.wait()
+ while True:
+ jobid, reply = self._worker.downlink.get()
+ with self._reply_lock:
+ if jobid != job.jobid:
+ self._replies[jobid] = reply
+ self._reply_cond.notifyAll()
+ else:
+ self._expected.remove(job.jobid)
+ if len(self._expected):
+ self._reply_cond.notify()
+ break
+ return reply
+
+ def _do_job(self, job):
+ """
+ Sends the job to YumWorker process and waits for reply.
+ If reply is a tuple, there was an error, while job processing.
+ Incoming exception is in format:
+ (exception_type, exception_value, formated_traceback_as_string)
+ @return reply
+ """
+ cmpi_logging.logger.trace_verbose("YumDB: doing %s(id=%s) job",
+ job.__class__.__name__, job.jobid)
+ self._worker.uplink.put(job)
+ reply = self._wait_for_reply(job)
+ if isinstance(reply, tuple):
+ cmpi_logging.logger.error(
+ "YumDB: job %s(id=%s) failed with error %s: %s",
+ job.__class__.__name__, job.jobid,
+ reply[0].__name__, str(reply[1]))
+ cmpi_logging.logger.trace_warn(
+ "YumDB: job %s(id=%s) exception traceback:\n%s%s: %s",
+ job.__class__.__name__, job.jobid, "".join(reply[2]),
+ reply[0].__name__, str(reply[1]))
+ reply[1].tb_printed = True
+ raise reply[1]
+ cmpi_logging.logger.trace_verbose("YumDB: job %s(id=%s) done",
+ job.__class__.__name__, job.jobid)
+ return reply
+
+ @property
+ def _worker(self):
+ """
+ YumWorker process accessor. It's created upon first need.
+ """
+ if self._process is None:
+ cmpi_logging.logger.trace_info("YumDB: starting YumWorker")
+ uplink = JoinableQueue()
+ downlink = Queue()
+ self._process = YumWorker(uplink, downlink,
+ yum_args=self._yum_args[0], yum_kwargs=self._yum_args[1])
+ #logging_config=YUM_WORKER_DEBUG_LOGGING_CONFIG)
+ self._process.start()
+ return self._process
+
+ # *************************************************************************
+ # Special methods
+ # *************************************************************************
+ def __del__(self):
+ """
+ Ensure, that YumWorker process is correctly shutted down.
+ """
+ self.clean_up()
+ YumDB.__del__(self)
+
+ @cmpi_logging.trace_method
+ def __enter__(self):
+ self._do_job(jobs.YumBeginSession())
+ cmpi_logging.logger.trace_info('YumDB: new session started')
+ return self
+
+ @cmpi_logging.trace_method
+ def __exit__(self, exc_type, exc_value, traceback):
+ self._do_job(jobs.YumEndSession())
+ cmpi_logging.logger.trace_info('YumDB: session ended')
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ @cmpi_logging.trace_method
+ def clean_up(self):
+ """
+ Shut down the YumWorker process.
+ """
+ cmpi_logging.logger.info('YumDB: cleanup called')
+ if self._process:
+ cmpi_logging.logger.info('YumDB: terminating YumWorker')
+ self._process.uplink.put(None) # terminating command
+ self._process.uplink.join()
+ self._process.join()
+ cmpi_logging.logger.info('YumDB: YumWorker terminated')
+ self._process = None
+
+ @cmpi_logging.trace_method
+ def get_package_list(self, kind,
+ allow_duplicates=False,
+ sort=False):
+ """
+ @param kind is one of: {"installed", "available", "all"}
+ @param allow_duplicates says, whether to list all found versions
+ of single package
+ @return [pkg1, pkg2, ...], pkgi is instance of yumdb.PackageInfo
+ """
+ return self._do_job(jobs.YumGetPackageList(
+ kind, allow_duplicates=allow_duplicates, sort=sort))
+
+ @cmpi_logging.trace_method
+ def filter_packages(self, kind,
+ allow_duplicates=False,
+ sort=False,
+ **filters):
+ """
+ Similar to get_package_list(), but applies filter on packages.
+ @see yumdb.jobs.YumFilterPackages job for supported filter keys
+ """
+ return self._do_job(jobs.YumFilterPackages(
+ kind, allow_duplicates=allow_duplicates, sort=sort,
+ **filters))
+
+ @cmpi_logging.trace_method
+ def install_package(self, pkg):
+ """
+ Install package.
+ @param pkg is an instance of PackageInfo obtained with
+ get_package_list() or filter_packages(), which must be not installed
+ """
+ return self._do_job(jobs.YumInstallPackage(pkg))
+
+ @cmpi_logging.trace_method
+ def remove_package(self, pkg):
+ """
+ @param pkg is an instance of PackageInfo obtained with
+ get_package_list() or filter_packages(), which must be installed
+ """
+ return self._do_job(jobs.YumRemovePackage(pkg))
+
+ @cmpi_logging.trace_method
+ def update_to_package(self, desired_pkg):
+ """
+ @param desired_pkg is an instance of PackageInfo,
+ which must be available
+ """
+ return self._do_job(jobs.YumUpdateToPackage(desired_pkg))
+
+ @cmpi_logging.trace_method
+ def update_package(self, pkg,
+ to_epoch=None,
+ to_version=None,
+ to_release=None):
+ """
+ @param pkg is an instance of PackageInfo, which must be installed
+
+ The other parameters filter candidate available packages for update.
+ """
+ return self._do_job(jobs.YumUpdatePackage(
+ pkg, to_epoch, to_version, to_release))
+
+ @cmpi_logging.trace_method
+ def check_package(self, pkg):
+ """
+ @param pkg is an instance of PackageInfo representing installed package
+ @return instance of yumdb.PackageCheck
+ """
+ return self._do_job(jobs.YumCheckPackage(pkg))
+
diff --git a/src/software/openlmi/software/yumdb/errors.py b/src/software/openlmi/software/yumdb/errors.py
new file mode 100644
index 0000000..646048e
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/errors.py
@@ -0,0 +1,43 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Exceptions raisable by YumWorker.
+"""
+
+class DatabaseLockError(Exception):
+ """Raised, when the yum database can not be locked."""
+ pass
+class TransactionError(Exception):
+ """Base exception representing yum transaction processing error."""
+ pass
+class TransactionBuildFailed(TransactionError):
+ """Raised, when transaction building fails."""
+ pass
+class TransactionExecutionFailed(TransactionError):
+ """Raised, when YumBase.doTransaction() method fails."""
+ pass
+class UnknownJob(Exception):
+ """Raised, when no handler is available for given job on worker."""
+ pass
+class PackageNotFound(Exception):
+ """Raised, when requested package could not be found."""
+ pass
diff --git a/src/software/openlmi/software/yumdb/jobs.py b/src/software/openlmi/software/yumdb/jobs.py
new file mode 100644
index 0000000..1fdfbe3
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/jobs.py
@@ -0,0 +1,234 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Define job classes representing kinds of jobs of worker process.
+"""
+
+import threading
+
+from openlmi.software import util
+from openlmi.software.yumdb.packageinfo import PackageInfo
+
+class YumJob(object): #pylint: disable=R0903
+ """
+ Base class for any job, that is processable by YumWorker process.
+ It contains at least a jobid attribute, that must be unique for
+ each job, it's counted from zero a incremented after each creation.
+ """
+ __slots__ = ('jobid', )
+
+ # jobs can be created concurrently from multiple threads, that's
+ # why we need to make its creation thread safe
+ _JOB_ID_LOCK = threading.Lock()
+ _JOB_ID = 0
+
+ @staticmethod
+ def _get_job_id():
+ """
+ Generates new job ids. It should be called only from constructor
+ of YumJob. Ensures, that each job has a unique number.
+ @return number of jobs created since program start -1
+ """
+ with YumJob._JOB_ID_LOCK:
+ val = YumJob._JOB_ID
+ YumJob._JOB_ID += 1
+ return val
+
+ def __init__(self):
+ self.jobid = self._get_job_id()
+
+ @property
+ def job_kwargs(self):
+ """
+ Jobs are in worker represented be methods with arguments.
+ Those can be obtained from job by calling this property.
+ @return dictionary of keyword arguments of job
+ """
+ kwargs = {}
+ cls = self.__class__
+ while not cls in (YumJob, object):
+ for slot in cls.__slots__:
+ if not slot in kwargs:
+ kwargs[slot] = getattr(self, slot)
+ cls = cls.__bases__[0]
+ kwargs.pop('jobid', None)
+ return kwargs
+
+ def __getstate__(self):
+ ret = self.job_kwargs
+ ret.update(jobid=self.jobid)
+ return ret
+
+ def __setstate__(self, state):
+ for k, value in state.items():
+ setattr(self, k, value)
+
+class YumBeginSession(YumJob): #pylint: disable=R0903
+ """
+ Begin session on YumWorker which ensures that yum database is locked
+ during its lifetime. Sessions can be nested, but the number of
+ YumEndSession jobs must be processed to make the database unlocked.
+ """
+ pass
+class YumEndSession(YumJob): #pylint: disable=R0903
+ """
+ End the session started with YumBeginSession. If the last active session
+ is ended, database will be unlocked.
+ """
+ pass
+
+class YumGetPackageList(YumJob): #pylint: disable=R0903
+ """
+ Job requesing a list of packages.
+ Arguments:
+ kind - supported values are in SUPPORTED_KINDS tuple
+ allow_duplicates - whether multiple packages can be present
+ in result for single (name, arch) of package differing
+ in their version
+
+ Worker replies with [pkg1, pkg2, ...].
+ """
+ __slots__ = ('kind', 'allow_duplicates', 'sort')
+
+ SUPPORTED_KINDS = ('installed', 'available', 'all')
+
+ def __init__(self, kind, allow_duplicates, sort=False):
+ YumJob.__init__(self)
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+ if not kind in self.SUPPORTED_KINDS:
+ raise ValueError("kind must be one of {%s}" %
+ ", ".join(self.SUPPORTED_KINDS))
+ self.kind = kind
+ self.allow_duplicates = bool(allow_duplicates)
+ self.sort = bool(sort)
+
+class YumFilterPackages(YumGetPackageList): #pylint: disable=R0903
+ """
+ Job similar to YumGetPackageList, but allowing to specify
+ filter on packages.
+ Arguments (plus those in YumGetPackageList):
+ name, epoch, version, release, arch, nevra, envra, evra
+
+ Some of those are redundant, but filtering is optimized for
+ speed, so supplying all of them won't affect performance.
+
+ Worker replies with [pkg1, pkg2, ...].
+ """
+ __slots__ = (
+ 'name', 'epoch', 'version', 'release', 'arch',
+ 'nevra', 'envra', 'evra')
+
+ def __init__(self, kind, allow_duplicates,
+ sort=False,
+ name=None, epoch=None, version=None,
+ release=None, arch=None,
+ nevra=None, evra=None,
+ envra=None):
+ if nevra is not None and not util.RE_NEVRA.match(nevra):
+ raise ValueError("Invalid nevra: %s" % nevra)
+ if evra is not None and not util.RE_EVRA.match(evra):
+ raise ValueError("Invalid evra: %s" % evra)
+ if envra is not None and not util.RE_ENVRA.match(evra):
+ raise ValueError("Invalid envra: %s" % envra)
+ YumGetPackageList.__init__(self, kind, allow_duplicates, sort)
+ self.name = name
+ self.epoch = None if epoch is None else str(epoch)
+ self.version = version
+ self.release = release
+ self.arch = arch
+ self.nevra = nevra
+ self.evra = evra
+ self.envra = envra
+
+class YumSpecificPackageJob(YumJob): #pylint: disable=R0903
+ """
+ Abstract job taking instance of yumdb.PackageInfo as argument.
+ Arguments:
+ pkg - plays different role depending on job subclass
+ """
+ __slots__ = ('pkg', )
+ def __init__(self, pkg):
+ if not isinstance(pkg, PackageInfo):
+ raise TypeError("pkg must be instance of PackageInfo")
+ YumJob.__init__(self)
+ self.pkg = pkg
+
+class YumInstallPackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Job requesting installation of specific package.
+ pkg argument should be available.
+
+ Worker replies with new instance of package.
+ """
+ pass
+
+class YumRemovePackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Job requesting removal of specific package.
+ pkg argument should be installed.
+ """
+ pass
+
+class YumUpdateToPackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Job requesting update to provided specific package.
+ Package is updated to epoch, version and release of this
+ provided available package.
+
+ Worker replies with new instance of package.
+ """
+ pass
+
+class YumUpdatePackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Job requesting update of package, optionally reducing possible
+ candidate packages to ones with specific evr.
+ Arguments:
+ to_epoch, to_version, to_release
+
+ The arguments more given, the more complete filter of candidates.
+
+ Worker replies with new instance of package.
+ """
+ __slots__ = ('to_epoch', 'to_version', 'to_release')
+
+ def __init__(self, pkg,
+ to_epoch=None, to_version=None, to_release=None):
+ if not isinstance(pkg, PackageInfo):
+ raise TypeError("pkg must be instance of yumdb.PackageInfo")
+ YumSpecificPackageJob.__init__(self, pkg)
+ self.to_epoch = to_epoch
+ self.to_version = to_version
+ self.to_release = to_release
+
+class YumCheckPackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Request verification information for instaled package and its files.
+
+ Worker replies with new instance of yumdb.PackageCheck.
+ """
+ def __init__(self, pkg):
+ YumSpecificPackageJob.__init__(self, pkg)
+ if not pkg.installed:
+ raise ValueError("package must be installed to check it")
+
diff --git a/src/software/openlmi/software/yumdb/packagecheck.py b/src/software/openlmi/software/yumdb/packagecheck.py
new file mode 100644
index 0000000..fbb5b21
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/packagecheck.py
@@ -0,0 +1,188 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module with definition of RPM package check class.
+"""
+
+from collections import OrderedDict
+from datetime import datetime
+import grp
+import os
+import pwd
+import rpm
+import yum
+
+CHECKSUMTYPE_STR2NUM = dict((val.lower(), k) for (k, val) in
+ yum.constants.RPM_CHECKSUM_TYPES.items())
+
+class PackageFile(object):
+ """
+ Metadata related to particular file on filesystem belonging to RPM package.
+ Data contained here are from RPM database.
+ """
+ __slots__ = ("path", "file_type", "uid", "gid", "mode", "device", "mtime",
+ "size", "link_target", "checksum")
+
+ def __init__(self, path, file_type, uid, gid, mode, device, mtime, size,
+ link_target, checksum):
+ for arg in ('uid', 'gid', 'mode', 'mtime', 'size'):
+ if not isinstance(locals()[arg], (int, long)):
+ raise TypeError("%s must be integer" % arg)
+ if not os.path.isabs(path):
+ raise ValueError("path must be an absolute path")
+ self.path = path
+ self.file_type = file_type.lower()
+ self.uid = uid
+ self.gid = gid
+ self.mode = mode
+ self.device = device if file_type.endswith('device') else None
+ self.mtime = mtime
+ self.size = size
+ self.link_target = (link_target
+ if file_type == "symlink" and link_target else None)
+ self.checksum = checksum
+
+ @property
+ def last_modification_datetime(self):
+ """
+ @return instance datetime for last modification time of file
+ """
+ return datetime.fromtimestamp(self.mtime)
+
+ def __getstate__(self):
+ """
+ Used for serialization with pickle.
+ @return container content that will be serialized
+ """
+ return dict((k, getattr(self, k)) for k in self.__slots__)
+
+ def __setstate__(self, state):
+ """
+ Used for deserialization with pickle.
+ Restores the object from serialized form.
+ @param state is an object created by __setstate__() method
+ """
+ for k, value in state.items():
+ setattr(self, k, value)
+
+class PackageCheck(object):
+ """
+ Metadata for package concerning verification.
+ It contains metadata for each file installed in "files" attribute.
+ """
+ __slots__ = ("pkgid", "file_checksum_type", "files")
+
+ def __init__(self, pkgid, file_checksum_type, files=None):
+ """
+ @param pkgid is an in of original yum package object, which is used
+ by server for subsequent operations on this package requested by client
+ """
+ if files is not None and not isinstance(
+ files, (list, tuple, set, dict)):
+ raise TypeError("files must be an iterable container")
+ self.pkgid = pkgid
+ self.file_checksum_type = file_checksum_type
+ if not isinstance(files, dict):
+ self.files = OrderedDict()
+ if files is not None:
+ for file_check in sorted(files, key=lambda f: f.path):
+ self.files[file_check.path] = file_check
+ else:
+ for path in sorted(files):
+ self.files[path] = files[path]
+
+ def __iter__(self):
+ return iter(self.files)
+
+ def __len__(self):
+ return len(self.files)
+
+ def __getitem__(self, filepath):
+ return self.files[filepath]
+
+ def __setitem__(self, filepath, package_file):
+ if not isinstance(package_file, PackageFile):
+ raise TypeError("package_file must be a PackageFile instance")
+ self.files[filepath] = package_file
+
+ def __contains__(self, fileobj):
+ if isinstance(fileobj, basestring):
+ return fileobj in self.files
+ elif isinstance(fileobj, PackageFile):
+ return fileobj.path in self.files
+ else:
+ raise TypeError("expected file path for argument")
+
+ def __getstate__(self):
+ """
+ Used for serialization with pickle.
+ @return container content that will be serialized
+ """
+ return dict((k, getattr(self, k)) for k in self.__slots__)
+
+ def __setstate__(self, state):
+ """
+ Used for deserialization with pickle.
+ Restores the object from serialized form.
+ @param state is an object created by __setstate__() method
+ """
+ for k, value in state.items():
+ setattr(self, k, value)
+
+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]
+ return CHECKSUMTYPE_STR2NUM[pkg.yumdb_info.checksum_type.lower()]
+
+def make_package_check_from_db(vpkg):
+ """
+ Create instance of PackageCheck from instance of
+ yum.packages._RPMVerifyPackage
+ @return instance of PackageCheck
+ """
+ if not isinstance(vpkg, yum.packages._RPMVerifyPackage):
+ raise TypeError("vpkg must be instance of"
+ " yum.packages._RPMVerifyPackage")
+ pkg = vpkg.po
+
+ res = PackageCheck(id(pkg), pkg_checksum_type(pkg))
+ files = res.files
+ for vpf in vpkg:
+ files[vpf.filename] = PackageFile(
+ vpf.filename,
+ vpf.ftype,
+ pwd.getpwnam(vpf.user).pw_uid,
+ grp.getgrnam(vpf.group).gr_gid,
+ vpf.mode,
+ vpf.dev,
+ vpf.mtime,
+ vpf.size,
+ vpf.readlink,
+ vpf.digest[1]
+ )
+ return res
+
diff --git a/src/software/openlmi/software/yumdb/packageinfo.py b/src/software/openlmi/software/yumdb/packageinfo.py
new file mode 100644
index 0000000..b2cd2b8
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/packageinfo.py
@@ -0,0 +1,171 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding an abstraction for RPM package.
+"""
+
+from datetime import datetime
+import yum
+
+from openlmi.software import util
+
+class PackageInfo(object):
+ """
+ Container for package metadata. It represents rpm package in yum
+ database. It's supposed to be passed from YumWorker to YumDB client
+ and vice-versa. Instances of YumAvailablePackage can not be exchanged
+ -- results in segfaults.
+
+ To speed up looking up of original yum package object on server, an
+ atribute "pkgid" is provided.
+ """
+ __slots__ = (
+ "pkgid",
+ "name", "epoch", "version", "release", "architecture",
+ 'summary', 'description', 'license', 'group', 'vendor',
+ 'size',
+ 'installed', # boolean
+ 'install_time' # datetime instance
+ )
+
+ def __init__(self, pkgid, name, epoch, version, release, arch, **kwargs):
+ """
+ @param pkgid is an in of original yum package object, which is used
+ by server for subsequent operations on this package requested by client
+ """
+ self.pkgid = pkgid
+ self.name = name
+ self.epoch = epoch
+ self.version = version
+ self.release = release
+ self.architecture = arch
+ self.summary = kwargs.pop('summary', None)
+ self.description = kwargs.pop('description', None)
+ self.license = kwargs.pop('license', None)
+ self.group = kwargs.pop('group', None)
+ self.vendor = kwargs.pop('vendor', None)
+ self.size = kwargs.pop('size', None)
+ if self.size is not None and not isinstance(self.size, (int, long)):
+ raise TypeError('size must be an integer')
+ self.installed = kwargs.pop('installed', None)
+ if self.installed is not None:
+ self.installed = bool(self.installed)
+ self.install_time = kwargs.pop('install_time', None)
+ if ( self.install_time is not None
+ and not isinstance(self.install_time, datetime)):
+ raise TypeError('install_time must be a datetime')
+
+ # *************************************************************************
+ # Properties
+ # *************************************************************************
+ @property
+ def ver(self):
+ """Shortcut for version property."""
+ return self.version
+
+ @property
+ def rel(self):
+ """Shortcut for release property."""
+ return self.release
+
+ @property
+ def arch(self):
+ """Shortcut for architecture property."""
+ return self.architecture
+
+ @property
+ def nevra(self):
+ """@return nevra of package with epoch always present."""
+ return self.get_nevra(with_epoch="ALWAYS")
+
+ @property
+ def evra(self):
+ """@return evra of package."""
+ return "%s:%s-%s.%s" % (
+ self.epoch if self.epoch and self.epoch != "(none)" else "0",
+ self.version,
+ self.release,
+ self.architecture)
+
+ @property
+ def key_props(self):
+ """
+ @return package properties as dictionary,
+ that uniquelly identify package in database
+ """
+ return dict((k, getattr(self, k)) for k in (
+ 'name', 'epoch', 'version', 'release', 'arch'))
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ def get_nevra(self, 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
+ @return nevra of package
+ """
+ return util.make_nevra(self.name, self.epoch, self.version,
+ self.release, self.arch, with_epoch)
+
+ # *************************************************************************
+ # Special methods
+ # *************************************************************************
+ def __str__(self):
+ return self.nevra
+
+ def __getstate__(self):
+ """
+ Used for serialization with pickle.
+ @return container content that will be serialized
+ """
+ return dict((k, getattr(self, k)) for k in self.__slots__)
+
+ def __setstate__(self, state):
+ """
+ Used for deserialization with pickle.
+ Restores the object from serialized form.
+ @param state is an object created by __setstate__() method
+ """
+ for k, value in state.items():
+ setattr(self, k, value)
+
+def make_package_from_db(pkg):
+ """
+ Create instance of PackageInfo from instance of
+ yum.packages.YumAvailablePackage.
+ @return instance of PackageInfo
+ """
+ metadata = dict((k, getattr(pkg, k)) for k in (
+ 'summary', 'description', 'license', 'group', 'vendor',
+ 'size'))
+ if isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ metadata['installed'] = True
+ metadata['install_time'] = datetime.fromtimestamp(pkg.installtime)
+ else:
+ metadata['installed'] = False
+ res = PackageInfo(id(pkg), pkg.name, pkg.epoch, pkg.version, pkg.release,
+ pkg.arch, **metadata)
+ return res
+
diff --git a/src/software/openlmi/software/yumdb/process.py b/src/software/openlmi/software/yumdb/process.py
new file mode 100644
index 0000000..c247de7
--- /dev/null
+++ b/src/software/openlmi/software/yumdb/process.py
@@ -0,0 +1,605 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding the code of separate process accessing the YUM API.
+"""
+
+import errno
+import inspect
+from itertools import chain
+import logging
+from multiprocessing import Process
+import Queue as TQueue # T as threaded
+import sys
+import time
+import traceback
+import weakref
+import yum
+
+from openlmi.software import util
+from openlmi.software.yumdb import errors
+from openlmi.software.yumdb import jobs
+from openlmi.software.yumdb import packageinfo
+from openlmi.software.yumdb import packagecheck
+
+# *****************************************************************************
+# Constants
+# *****************************************************************************
+# interval in seconds
+FREE_DATABASE_TIMEOUT = 60
+LOCK_WAIT_INTERVAL = 0.5
+RPMDB_PATH = '/var/lib/rpm/Packages'
+
+# *****************************************************************************
+# Utilities
+# ****************************************************************************
+def _logger():
+ """
+ Returns logger for this module, when first needed.
+ @return logger specific for this process
+ """
+ if not hasattr(_logger, "logger"):
+ _logger.logger = logging.getLogger(__name__)
+ return _logger.logger
+
+def _get_package_filter_function(filters):
+ """
+ @param filters is a dictionary, where keys are package property
+ names and values are their desired values.
+ @return a function used to filter list of packages
+ """
+ if not isinstance(filters, dict):
+ raise TypeError("filters must be a dictionary")
+
+ filters = dict((k, value) for k, value in filters.items()
+ if value is not None)
+
+ if "nevra" in filters:
+ def _cmp_nevra(pkg):
+ """@return True if pkg matches nevra filter"""
+ value = '%s-%s:%s-%s.%s' % (
+ pkg.name,
+ "0" if not pkg.epoch or pkg.epoch == "(none)"
+ else pkg.epoch,
+ pkg.version, pkg.release, pkg.arch)
+ return value == filters["nevra"]
+ return _cmp_nevra
+
+ elif "envra" in filters:
+ def _cmp_envra(pkg):
+ """@return True if pkg matches envra filter"""
+ value = '%s:%s-%s-%s.%s' % (
+ "0" if not pkg.epoch or pkg.epoch == "(none)"
+ else pkg.epoch,
+ pkg.name,
+ pkg.version, pkg.release, pkg.arch)
+ return value == filters["envra"]
+ return _cmp_envra
+
+ else:
+ if "evra" in filters:
+ for prop_name in ("epoch", "version", "release", "epoch"):
+ filters.pop(prop_name, None)
+ filter_list = []
+ # properties are sorted by their filtering ability
+ # (the most unprobable property, that can match, comes first)
+ for prop_name in ("evra", "name", "version", "epoch",
+ "release", "arch"):
+ if not prop_name in filters:
+ continue
+ filter_list.append((prop_name, filters.pop(prop_name)))
+ def _cmp_props(pkg):
+ """@return True if pkg matches properies filter"""
+ return all(getattr(pkg, p) == v for p, v in filter_list)
+ return _cmp_props
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def _trace_function(func):
+ """
+ Decorator for logging entries and exits of function or method.
+ """
+ if not inspect.ismethod(func) and not inspect.isfunction(func):
+ raise TypeError("func must be a function")
+
+ def _print_value(val):
+ """
+ Used here for printing function arguments. Shortens the output
+ string, if that would be too long.
+ """
+ if isinstance(val, list):
+ if len(val) < 2:
+ return str(val)
+ else:
+ return "[%s, ...]" % _print_value(val[0])
+ return str(val)
+
+ def _wrapper(self, *args, **kwargs):
+ """
+ Wrapper for function or method, that does the logging.
+ """
+ ftype = "method" if inspect.ismethod(func) else "function"
+ parent = ( func.im_class.__name__ + "."
+ if inspect.ismethod(func) else "")
+
+ _logger().debug("entering %s %s%s with args=(%s)",
+ ftype, parent, func.__name__,
+ ", ".join(chain(
+ (_print_value(a) for a in args),
+ ( "%s=%s"%(k, _print_value(v))
+ for k, v in kwargs.items()))))
+ result = func(self, *args, **kwargs)
+ _logger().debug("exiting %s %s%s", ftype, parent, func.__name__)
+ return result
+
+ return _wrapper
+
+def _needs_database(method):
+ """
+ Decorator for YumWorker job handlers, that need to access the yum database.
+ It ensures, that database is initialized and locks it in case, that
+ no session is active.
+ """
+ def _wrapper(self, *args, **kwargs):
+ """
+ Wrapper for the job handler method.
+ """
+ self._init_database() #pylint: disable=W0212
+ if self._session_level == 0: #pylint: disable=W0212
+ self._lock_database() #pylint: disable=W0212
+ try:
+ _logger().debug("calling job handler %s with args=(%s)",
+ method.__name__,
+ ", ".join(chain(
+ (str(a) for a in args),
+ ("%s=%s"%(k, str(v)) for k, v in kwargs.items()))))
+ result = method(self, *args, **kwargs)
+ _logger().debug("job handler %s finished", method.__name__)
+ return result
+ except:
+ _logger().debug("job handler %s terminated with exception: %s",
+ method.__name__, traceback.format_exc())
+ raise
+ finally:
+ if self._session_level == 0: #pylint: disable=W0212
+ self._unlock_database() #pylint: disable=W0212
+ return _wrapper
+
+# *****************************************************************************
+# Classes
+# *****************************************************************************
+class YumWorker(Process):
+ """
+ The main process, that works with YUM API. It has two queues, one
+ for input jobs and second for results.
+
+ Jobs are dispatched by their class names to particular handler method.
+ """
+
+ def __init__(self,
+ queue_in,
+ queue_out,
+ yum_args=None,
+ yum_kwargs=None,
+ logging_config=None):
+ Process.__init__(self)
+ self._queue_in = queue_in
+ self._queue_out = queue_out
+ self._session_level = 0
+ self._session_ended = False
+
+ if yum_args is None:
+ yum_args = tuple()
+ if yum_kwargs is None:
+ yum_kwargs = {}
+
+ self._yum_args = (yum_args, yum_kwargs)
+ self._yum_base = None
+
+ self._pkg_cache = None
+ self._logging_config = logging_config
+
+ # *************************************************************************
+ # Private methods
+ # *************************************************************************
+ @_trace_function
+ def _init_database(self):
+ """
+ Initializes yum base object, when it does no exists.
+ And updates the cache (when out of date).
+ """
+ if self._yum_base is None:
+ _logger().info("creating YumBase with args=(%s)",
+ ", ".join(chain(
+ (str(a) for a in self._yum_args[0]),
+ ( "%s=%s"%(k, str(v))
+ for k, v in self._yum_args[1].items()))))
+ self._yum_base = yum.YumBase(
+ *self._yum_args[0], **self._yum_args[1])
+
+ @_trace_function
+ def _free_database(self):
+ """
+ Release the yum base object to safe memory.
+ """
+ _logger().info("freing database")
+ self._pkg_cache.clear()
+ self._yum_base = None
+
+ @_trace_function
+ def _lock_database(self):
+ """
+ Only one process is allowed to work with package database at given time.
+ That's why we lock it.
+
+ Try to lock it in loop, until success.
+ """
+ while True:
+ try:
+ _logger().info("trying to lock database - session level %d",
+ self._session_level)
+ self._yum_base.doLock()
+ _logger().info("successfully locked up")
+ break
+ except yum.Errors.LockError as exc:
+ _logger().warn("failed to lock")
+ if exc.errno in (errno.EPERM, errno.EACCES, errno.ENOSPC):
+ _logger().error("can't create lock file")
+ raise errors.DatabaseLockError("Can't create lock file.")
+ _logger().info("trying to lock again after %.1f seconds",
+ LOCK_WAIT_INTERVAL)
+ time.sleep(LOCK_WAIT_INTERVAL)
+
+ @_trace_function
+ def _unlock_database(self):
+ """
+ The opposite to _lock_database() method.
+ """
+ if self._yum_base is not None:
+ _logger().info("unlocking database")
+ self._yum_base.closeRpmDB()
+ self._yum_base.doUnlock()
+
+ @_trace_function
+ def _transform_packages(self, packages,
+ cache_packages=True,
+ flush_cache=True):
+ """
+ Return instances of PackageInfo for each package in packages.
+ Cache all the packages.
+ @param packages list of YumAvailablePackage instances
+ @param cache_packages whether to update cache with packages
+ @param flush_cache whether to clear the cache before adding input
+ packages; makes sense only with cachee_packages=True
+ """
+ if cache_packages is True and flush_cache is True:
+ _logger().debug("flushing package cache")
+ self._pkg_cache.clear()
+ res = []
+ for orig in packages:
+ pkg = packageinfo.make_package_from_db(orig)
+ if cache_packages is True:
+ self._pkg_cache[pkg.pkgid] = orig
+ res.append(pkg)
+ return res
+
+ @_trace_function
+ def _cache_packages(self, packages, flush_cache=True, transform=False):
+ """
+ Store packages in cache and return them.
+ @param flush_cache whether to clear the cache before adding new
+ packages
+ @param transform whether to return packages as PackageInfos
+ @return either list of original packages or PackageInfo instances
+ """
+ if transform is True:
+ return self._transform_packages(packages, flush_cache=flush_cache)
+ if flush_cache is True:
+ _logger().debug("flushing package cache")
+ self._pkg_cache.clear()
+ for pkg in packages:
+ self._pkg_cache[id(pkg)] = pkg
+ return packages
+
+ @_trace_function
+ def _lookup_package(self, pkg):
+ """
+ Lookup the original package in cache.
+ If it was garbage collected already, make new query to find it.
+ @return instance of YumAvailablePackage
+ """
+ if not isinstance(pkg, packageinfo.PackageInfo):
+ raise TypeError("pkg must be instance of PackageInfo")
+ _logger().debug("looking up yum package %s with id=%d",
+ pkg, pkg.pkgid)
+ try:
+ result = self._pkg_cache[pkg.pkgid]
+ _logger().debug("lookup successful")
+ except KeyError:
+ _logger().warn("lookup of package %s with id=%d failed, trying"
+ " to query database", pkg, pkg.pkgid)
+ result = self._handle_filter_packages(
+ 'installed' if pkg.installed else 'available',
+ allow_duplicates=False,
+ sort=False,
+ transform=False,
+ **pkg.key_props)
+ if len(result) < 1:
+ _logger().warn("package %s not found", pkg)
+ raise errors.PackageNotFound(
+ "package %s could not be found" % pkg)
+ result = result[0]
+ return result
+
+ @_trace_function
+ def _do_work(self, job):
+ """
+ Dispatcher of incoming jobs. Job is passed to the right handler
+ depending on its class.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be instance of YumJob")
+ try:
+ handler = {
+ jobs.YumGetPackageList : self._handle_get_package_list,
+ jobs.YumFilterPackages : self._handle_filter_packages,
+ jobs.YumInstallPackage : self._handle_install_package,
+ jobs.YumRemovePackage : self._handle_remove_package,
+ jobs.YumUpdateToPackage: self._handle_update_to_package,
+ jobs.YumUpdatePackage : self._handle_update_package,
+ jobs.YumBeginSession : self._handle_begin_session,
+ jobs.YumEndSession : self._handle_end_session,
+ jobs.YumCheckPackage : self._handle_check_package
+ }[job.__class__]
+ _logger().info("processing job %s(id=%d)",
+ job.__class__.__name__, job.jobid)
+ except KeyError:
+ _logger().info("No handler for job \"%s\"", job.__class__.__name__)
+ raise errors.UnknownJob("No handler for job \"%s\"." %
+ job.__class__.__name__)
+ return handler(**job.job_kwargs)
+
+ @_trace_function
+ def _run_transaction(self, name):
+ """
+ Builds and runs the yum transaction and checks for errors.
+ @param name of transaction used only in error description on failure
+ """
+ _logger().info("building transaction %s", name)
+ (code, msgs) = self._yum_base.buildTransaction()
+ if code == 1:
+ _logger().error("building transaction %s failed: %s",
+ name, "\n".join(msgs))
+ raise errors.TransactionBuildFailed(
+ "Failed to build \"%s\" transaction: %s" % (
+ name, "\n".join(msgs)))
+ _logger().info("processing transaction %s", name)
+ self._yum_base.processTransaction()
+ self._yum_base.closeRpmDB()
+
+ @_trace_function
+ def _handle_begin_session(self):
+ """
+ Handler for session begin job.
+ """
+ self._session_level += 1
+ _logger().info("beginning session level %s", self._session_level)
+ if self._session_level == 1:
+ self._init_database()
+ self._lock_database()
+
+ @_trace_function
+ def _handle_end_session(self):
+ """
+ Handler for session end job.
+ """
+ _logger().info("ending session level %d", self._session_level)
+ self._session_level -= 1
+ if self._session_level == 0:
+ self._unlock_database()
+ self._session_ended = True
+
+ @_needs_database
+ def _handle_get_package_list(self, kind, allow_duplicates, sort,
+ transform=True):
+ """
+ Handler for listing packages job.
+ @param transform says, whether to return just a package abstractions
+ or original ones
+ @return [pkg1, pkg2, ...]
+ """
+ pkglist = self._yum_base.doPackageLists(kind, showdups=allow_duplicates)
+ if kind == 'all':
+ result = pkglist.available + pkglist.installed
+ else:
+ result = getattr(pkglist, kind)
+ if sort is True:
+ result.sort()
+ _logger().debug("returning %s packages", len(result))
+ return self._cache_packages(result, transform=transform)
+
+ @_needs_database
+ def _handle_filter_packages(self, kind, allow_duplicates, sort,
+ transform=True, **filters):
+ """
+ Handler for filtering packages job.
+ @return [pkg1, pkg2, ...]
+ """
+ pkglist = self._handle_get_package_list(kind, allow_duplicates, False,
+ transform=False)
+ matches = _get_package_filter_function(filters)
+ result = [p for p in pkglist if matches(p)]
+ if sort is True:
+ result.sort()
+ _logger().debug("%d packages matching", len(result))
+ if transform is True:
+ # caching has been already done by _handle_get_package_list()
+ result = self._transform_packages(result, cache_packages=False)
+ return result
+
+ @_needs_database
+ def _handle_install_package(self, pkg):
+ """
+ Handler for package installation job.
+ @return installed package instance
+ """
+ pkg_desired = self._lookup_package(pkg)
+ self._yum_base.install(pkg_desired)
+ self._run_transaction("install")
+ installed = self._handle_filter_packages("installed", False, False,
+ nevra=util.pkg2nevra(pkg_desired, with_epoch="ALWAYS"))
+ if len(installed) < 1:
+ raise errors.TransactionExecutionFailed(
+ "Failed to install desired package %s." % pkg)
+ return installed[0]
+
+ @_needs_database
+ def _handle_remove_package(self, pkg):
+ """
+ Handler for package removal job.
+ """
+ pkg = self._lookup_package(pkg)
+ self._yum_base.remove(pkg)
+ self._run_transaction("remove")
+
+ @_needs_database
+ def _handle_update_to_package(self, pkg):
+ """
+ Handler for specific package update job.
+ @return package corresponding to pkg after update
+ """
+ pkg_desired = self._lookup_package(pkg)
+ self._yum_base.update(update_to=True,
+ name=pkg_desired.name,
+ epoch=pkg_desired.epoch,
+ version=pkg_desired.version,
+ release=pkg_desired.release,
+ arch=pkg_desired.arch)
+ self._run_transaction("update")
+ installed = self._handle_filter_packages("installed", False, False,
+ **pkg.key_props)
+ if len(installed) < 1:
+ raise errors.TransactionExecutionFailed(
+ "Failed to update to desired package %s." % pkg)
+ return installed[0]
+
+ @_needs_database
+ def _handle_update_package(self, pkg, to_epoch, to_version, to_release):
+ """
+ Handler for package update job.
+ @return updated package instance
+ """
+ pkg = self._lookup_package(pkg)
+ kwargs = { "name" : pkg.name, "arch" : pkg.arch }
+ if any(v is not None for v in (to_epoch, to_version, to_release)):
+ kwargs["update_to"] = True
+ if to_epoch:
+ kwargs["to_epoch"] = to_epoch
+ if to_version:
+ kwargs["to_version"] = to_version
+ if to_release:
+ kwargs["to_release"] = to_release
+ self._yum_base.update(**kwargs)
+ self._run_transaction("update")
+ kwargs = dict( (k[3:] if k.startswith("to_") else k, v)
+ for k, v in kwargs.items())
+ installed = self._handle_filter_packages(
+ "installed", False, False, **kwargs)
+ if len(installed) < 1:
+ raise errors.TransactionExecutionFailed(
+ "Failed to update package %s." % pkg)
+ return installed[0]
+
+ @_needs_database
+ def _handle_check_package(self, pkg):
+ """
+ @return PackageFile instance for requested package
+ """
+ pkg = self._lookup_package(pkg)
+ vpkg = yum.packages._RPMVerifyPackage(pkg, pkg.hdr.fiFromHeader(),
+ packagecheck.pkg_checksum_type(pkg), [], True)
+ return packagecheck.make_package_check_from_db(vpkg)
+
+ # *************************************************************************
+ # Public properties
+ # *************************************************************************
+ @property
+ def uplink(self):
+ """
+ @return input queue for jobs
+ """
+ return self._queue_in
+
+ @property
+ def downlink(self):
+ """
+ @return output queue for job results
+ """
+ return self._queue_out
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ def run(self):
+ """
+ Main loop of process. It accepts a job from input queue, handles it,
+ sends the result to output queue and marks the job as done.
+
+ It is terminated, when None is received from input queue.
+ """
+ if self._logging_config is not None:
+ try:
+ logging.config.dictConfig(self._logging_config)
+ except Exception: #pylint: disable=W0703
+ # logging is not set up but client expects us to work
+ pass
+ _logger().info("starting %s main loop", self.__class__.__name__)
+ self._pkg_cache = weakref.WeakValueDictionary()
+ while True:
+ if self._session_ended and self._session_level == 0:
+ try:
+ job = self._queue_in.get(True, FREE_DATABASE_TIMEOUT)
+ except TQueue.Empty:
+ self._free_database()
+ self._session_ended = False
+ continue
+ else:
+ job = self._queue_in.get()
+ if job is not None: # not a terminate command
+ try:
+ result = self._do_work(job)
+ except Exception: #pylint: disable=W0703
+ # (type, value, traceback)
+ result = sys.exc_info()
+ # traceback is not pickable - replace it with formatted
+ # text
+ result = ( result[0], result[1]
+ , traceback.format_tb(result[2]))
+ _logger().error("job %s(id=%d) failed: %s",
+ job.__class__.__name__, job.jobid,
+ traceback.format_exc())
+ self._queue_out.put((job.jobid, result))
+ self._queue_in.task_done()
+ if job is None:
+ break
+
diff --git a/src/software/setup.py b/src/software/setup.py
index 25eb001..a044ac0 100644
--- a/src/software/setup.py
+++ b/src/software/setup.py
@@ -5,9 +5,13 @@ setup(
author='Michal Minar',
author_email='miminar@redhat.com',
url='https://fedorahosted.org/openlmi/',
- version='0.5',
+ version='0.6',
namespace_packages=['openlmi'],
- packages=['openlmi.software', 'openlmi.software.util'],
+ packages=[
+ 'openlmi.software',
+ 'openlmi.software.core',
+ 'openlmi.software.util',
+ 'openlmi.software.yumdb'],
install_requires=['openlmi'],
license="LGPLv2+",
classifiers=[
diff --git a/src/software/test/common.py b/src/software/test/common.py
index ef3d157..d91f236 100644
--- a/src/software/test/common.py
+++ b/src/software/test/common.py
@@ -43,11 +43,14 @@ def remove_pkg(pkg, *args):
pkg = pkg.name
call(["rpm", "--quiet"] + list(args) + ["-e", pkg])
-def install_pkg(pkg, newer=True):
+def install_pkg(pkg, newer=True, repolist=[]):
"""
Install a specific package.
@param pkg is either package name or instance of Package
In latter case, a specific version is installed.
+ @param repolist is a list of repositories, that should be
+ used for downloading, if using yum
+ when empty, all enabled repositories are used
"""
if isinstance(pkg, rpmcache.Package):
try:
@@ -57,7 +60,7 @@ def install_pkg(pkg, newer=True):
except rpmcache.MissingRPM:
pass
pkg = pkg.name
- call(["yum", "-q", "-y", "install", pkg])
+ rpmcache.run_yum('-q', '-y', 'install', pkg, repolist=repolist)
def is_installed(pkg, newer=True):
"""
@@ -148,6 +151,11 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
self.objpath = pywbem.CIMInstanceName(
namespace="root/cimv2", classname=self.CLASS_NAME)
+ def install_pkg(self, pkg, newer=True, repolist=None):
+ if repolist is None:
+ repolist = self.test_repos
+ return install_pkg(pkg, newer, repolist)
+
def assertIsSubclass(self, cls, base_cls): #pylint: disable=C0103
"""
Checks, whether cls is subclass of base_cls from CIM perspective.
@@ -161,6 +169,21 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
return self.assertTrue(pywbem.is_subclass(self.conn,
"root/cimv2", base_cls, cls))
+ def assertEqual(self, fst, snd, *args, **kwargs):
+ if ( isinstance(fst, pywbem.CIMInstanceName)
+ and isinstance(snd, pywbem.CIMInstanceName)
+ and fst.classname == "LMI_SoftwarePackage"
+ and fst.classname == snd.classname
+ and fst.namespace == snd.namespace
+ and fst.keys() == snd.keys()
+ and all(fst[k] == snd[k] for k in ("Name", "SoftwareElementID",
+ "SoftwareElementState", "Version"))
+ and isinstance(fst["TargetOperatingSystem"], (int, long))
+ and isinstance(snd["TargetOperatingSystem"], (int, long))):
+ return True
+ return unittest.TestCase.assertEqual(
+ self, fst, snd, *args, **kwargs)
+
@classmethod
def setUpClass(cls):
cls.url = os.environ.get('LMI_CIMOM_URL', 'http://localhost:5988')
@@ -170,6 +193,11 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
cls.conn = pywbem.WBEMConnection(cls.url, (cls.user, cls.password))
cls.run_dangerous = (
os.environ.get('LMI_RUN_DANGEROUS', '0') == '1')
+ cls.test_repos = os.environ.get('LMI_SOFTWARE_TEST_REPOS', '')
+ if not cls.test_repos:
+ cls.test_repos = []
+ else:
+ cls.test_repos = cls.test_repos.split(',')
use_cache = os.environ.get('LMI_SOFTWARE_USE_CACHE', '0') == '1'
cls.cache_dir = None
if use_cache:
@@ -185,7 +213,7 @@ class SoftwareBaseTestCase(unittest.TestCase): #pylint: disable=R0904
cls.pkgdb = rpmcache.get_pkg_database(use_cache=use_cache)
for pkg in cls.pkgdb:
if not is_installed(pkg.name):
- install_pkg(pkg)
+ install_pkg(pkg, repolist=cls.test_repos)
cls.pkg_files = dict((pkg.name, get_pkg_files(pkg))
for pkg in cls.pkgdb)
diff --git a/src/software/test/rpmcache.py b/src/software/test/rpmcache.py
index f68f128..9a0e98a 100644
--- a/src/software/test/rpmcache.py
+++ b/src/software/test/rpmcache.py
@@ -61,7 +61,7 @@ RE_REPO = re.compile(
r'(?:^\*?)(?P<name>[^\s/]+\b)(?!\s+id)', re.MULTILINE | re.IGNORECASE)
# maximum number of packages, that will be selected for testing
-MAX_PKG_DB_SIZE = 5
+MAX_PKG_DB_SIZE = 10
# step used to iterate over package names used to check for thery dependencies
# it's a number of packages, that will be passed to yum command at once
PKG_DEPS_ITER_STEP = 50
@@ -95,6 +95,22 @@ def make_nevra(name, epoch, ver, rel, arch, with_epoch='NOT_ZERO'):
estr += ":"
return "%s-%s%s-%s.%s" % (name, estr, ver, rel, arch)
+def run_yum(*params, **kwargs):
+ """
+ Runs yum with params and returns its output
+ It's here especially to allow pass a repolist argument, that
+ specifies list of repositories, to run the command on.
+ """
+ cmd = ['yum'] + list(params)
+ repolist = kwargs.get('repolist', [])
+ if repolist:
+ cmd += ['--disablerepo=*']
+ cmd += ['--enablerepo='+r for r in repolist]
+ try:
+ return check_output(cmd)
+ except Exception:
+ import pdb;pdb.set_trace()
+
class Package(object): #pylint: disable=R0902
"""
Element of test package database. It's a container for package
@@ -244,20 +260,21 @@ def _check_single_pkg_deps(
def _check_pkg_dependencies(
installed,
dup_list,
- number_of_packages=MAX_PKG_DB_SIZE):
+ number_of_packages=MAX_PKG_DB_SIZE,
+ repolist=[]):
"""
Finds packages from dup_list with satisfied (installed) dependencies.
@param installed is a set of installed package names
@return filtered dup_list with at least number_of_packages elements.
"""
- cmd = ['yum', 'deplist']
+ yum_params = ['deplist']
dups_no_deps = []
for i in range(0, len(dup_list), PKG_DEPS_ITER_STEP):
dups_part = dup_list[i:i+PKG_DEPS_ITER_STEP]
- cmd = cmd[:2]
+ yum_params = yum_params[:1]
for dups in dups_part:
- cmd.extend([d.get_nevra(newer=False) for d in dups])
- deplist_str = check_output(cmd)
+ yum_params.extend([d.get_nevra(newer=False) for d in dups])
+ deplist_str = run_yum(*yum_params, repolist=repolist)
matches = RE_PKG_DEPS.finditer(deplist_str)
prev_match = None
for pkgs in dups_part:
@@ -283,14 +300,14 @@ def _check_pkg_dependencies(
break
return dups_no_deps
-def _sorted_db_by_size(pkgdb):
+def _sorted_db_by_size(pkgdb, repolist=[]):
"""
@param pkgdb is a list of lists of packages with common name
@return sorted instances of Package according to their size
"""
- cmd = ['yum', 'info', '--showduplicates']
- cmd.extend([ps[0].name for ps in pkgdb])
- info_str = check_output(cmd)
+ yum_params = ['info', '--showduplicates']
+ yum_params.extend([ps[0].name for ps in pkgdb])
+ info_str = run_yum(*yum_params, repolist=repolist)
pkg_sizes = {}
# to get correct ordering from "yum info" command
# { pkg_name : [(epoch, version, release), ... ] }
@@ -376,7 +393,8 @@ def is_installed(pkg, newer=True):
return call(["rpm", "--quiet", "-q", pkg]) == 0
else:
try:
- cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}", pkg.name ]
+ cmd = [ "rpm", "-q", "--qf", "%{EPOCH}:%{NVRA}", pkg.get_nevra(
+ newer, with_epoch='NEVER') ]
out = check_output(cmd)
epoch, nvra = out.split(':') #pylint: disable=E1103
if not epoch or epoch == "(none)":
@@ -430,7 +448,9 @@ def load_pkgdb(cache_dir=''):
#print "Loaded package database from: %s" % date_time
return pkgdb
-def get_pkg_database(force_update=False, use_cache=True, cache_dir=''):
+def get_pkg_database(force_update=False, use_cache=True,
+ cache_dir='',
+ repolist=[]):
"""
Checks yum database for available packages, that have at least two
different versions in repositories. Only not installed ones with
@@ -454,16 +474,18 @@ def get_pkg_database(force_update=False, use_cache=True, cache_dir=''):
installed = set(check_output( #pylint: disable=E1103
['rpm', '-qa', '--qf=%{NAME}\n']).splitlines())
#print "Getting all available packages"
- avail_str = check_output(['yum', 'list', 'available', '--showduplicates'])
+ avail_str = run_yum('list', 'available', '--showduplicates',
+ repolist=repolist)
# list of lists of packages with the same name, longer than 2
#print "Finding duplicates"
dups_list = _filter_duplicates(installed, avail_str)
#print "Selecting only those (from %d) with installed dependencies" % \
#len(dups_list)
selected = _check_pkg_dependencies(installed, dups_list,
- number_of_packages=MAX_PKG_DB_SIZE*5)
+ number_of_packages=MAX_PKG_DB_SIZE*5,
+ repolist=repolist)
#print "Selecting the smallest ones"
- pkgdb = _sorted_db_by_size(selected)
+ pkgdb = _sorted_db_by_size(selected, repolist=repolist)
if use_cache:
repolist = _get_repo_list()
_download_pkgdb(repolist, pkgdb, cache_dir)
diff --git a/src/software/test/run.py b/src/software/test/run.py
index aba362f..950b0c9 100755
--- a/src/software/test/run.py
+++ b/src/software/test/run.py
@@ -95,8 +95,17 @@ def parse_cmd_line():
parser.add_argument('--cache-dir',
default=os.environ.get('LMI_SOFTWARE_CACHE_DIR', ''),
help="Use particular directory, instead of temporary one, to store"
- " rpm packages for testing purposes. Overrides nevironment"
+ " rpm packages for testing purposes. Overrides environment"
" variable LMI_SOFTWARE_CACHE_DIR.")
+ parser.add_argument('--test-repos',
+ default=os.environ.get('LMI_SOFTWARE_REPOSITORIES', ''),
+ help="Use this option to specify list of repositories, that"
+ " alone should be used for testing. Overrides environment"
+ " variable LMI_SOFTWARE_REPOSITORIES.")
+ parser.add_argument('--test-packages',
+ default=os.environ.get('LMI_SOFTWARE_PACKAGES', ''),
+ help="Specify packages for dangerous tests. If empty"
+ " and cache is enabled, some will be picked up by algorithm")
parser.add_argument('--force-update', action="store_true",
help="Force update of package database. Otherwise an old"
" one will be used (if any exists).")
@@ -154,6 +163,8 @@ def prepare_environment(args):
os.environ['LMI_SOFTWARE_USE_CACHE'] = '1' if args.use_cache else '0'
if args.use_cache:
os.environ['LMI_SOFTWARE_CACHE_DIR'] = CACHE_DIR
+ os.environ['LMI_SOFTWARE_REPOSITORIES'] = args.test_repos
+ os.environ['LMI_SOFTWARE_PACKAGES'] = args.test_packages
def load_tests(loader, standard_tests, pattern):
"""
@@ -252,8 +263,16 @@ def main():
CACHE_DIR = tempfile.mkdtemp(suffix="software_database")
elif args.use_cache:
CACHE_DIR = args.cache_dir
+ if not os.path.exists(args.cache_dir):
+ os.makedirs(args.cache_dir)
try_connection(args)
- rpmcache.get_pkg_database(args.force_update, args.use_cache, CACHE_DIR)
+ if args.test_repos:
+ repolist = args.test_repos.split(',')
+ else:
+ repolist = []
+ rpmcache.get_pkg_database(args.force_update, args.use_cache,
+ CACHE_DIR, repolist=repolist)
+ #rpmcache.make_pkg_database(packages
prepare_environment(args)
test_program = unittest.main(argv=ut_args,
testLoader=LMITestLoader(), exit=False)
diff --git a/src/software/test/test_software_file_check.py b/src/software/test/test_software_file_check.py
index 4228c18..7b97ee9 100755
--- a/src/software/test/test_software_file_check.py
+++ b/src/software/test/test_software_file_check.py
@@ -217,9 +217,10 @@ class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
"File %s:%s should exist"%(pkg.name, filepath))
self.assertTrue(cur_pflags.type,
"File type should match for symlink %s:%s"%(pkg.name, filepath))
- self.assertFalse(cur_pflags.size,
- "File size should not match for symlink %s:%s"%(
- pkg.name, filepath))
+ # file size not checked for symlinks
+ #self.assertFalse(cur_pflags.size,
+ #"File size should not match for symlink %s:%s"%(
+ #pkg.name, filepath))
self.assertTrue(cur_pflags.mode,
"File mode should match for symlink %s:%s"%(pkg.name, filepath))
self.assertTrue(cur_pflags.checksum,
@@ -432,7 +433,7 @@ class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
and not common.verify_pkg(pkg.name)):
common.remove_pkg(pkg.name)
if not common.is_installed(pkg.name):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
self.assertTrue(common.is_installed(pkg),
"Package %s must be installed"%pkg)
@@ -449,12 +450,14 @@ class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
for key in self.KEYS:
if key.lower() == "softwareelementid":
self.assertEqualSEID(inst[key], objpath[key],
- "OP keys should match for %s:%s"%(
- pkg.name, filepath))
+ "OP key %s values should match for %s:%s"%(
+ key, pkg.name, filepath))
+ elif key.lower() == "targetoperatingsystem":
+ self.assertIsInstance(objpath[key], (int, long))
else:
self.assertEqual(objpath[key], inst[key],
- "OP keys should match for %s:%s"%(
- pkg.name, filepath))
+ "OP key %s values should match for %s:%s"%(
+ key, pkg.name, filepath))
self.assertTrue(inst["FileExists"],
"File %s:%s must exist"%(pkg.name, filepath))
@@ -467,6 +470,10 @@ class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
for prop in ( "FileType", "FileUserID", "FileGroupID"
, "FileMode", "FileSize", "LinkTarget"
, "FileChecksum", "FileModeFlags"):
+ if ( ( os.path.islink(filepath)
+ or (not os.path.isfile(filepath)))
+ and prop == "FileSize"):
+ continue
self.assertEqual(inst["Expected"+prop], inst[prop],
"%s should match for %s:%s"%(prop, pkg.name, filepath))
if os.path.islink(filepath):
@@ -486,7 +493,7 @@ class TestSoftwareFileCheck(common.SoftwareBaseTestCase):
if common.is_installed(pkg) and not common.verify_pkg(pkg.name):
common.remove_pkg(pkg.name)
if not common.is_installed(pkg.name):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
self.assertTrue(common.is_installed(pkg),
"Package %s must be installed"%pkg)
for filepath in files:
diff --git a/src/software/test/test_software_installed_package.py b/src/software/test/test_software_installed_package.py
index 9de627a..93669f9 100755
--- a/src/software/test/test_software_installed_package.py
+++ b/src/software/test/test_software_installed_package.py
@@ -24,13 +24,15 @@ Unit tests for LMI_SoftwareInstalledPackage provider.
import os
import pywbem
-import shutil
import socket
import stat
import unittest
import common
-import rpmcache
+
+def make_path_tuple(objpath):
+ return tuple(objpath[a] for a in ("SoftwareElementID", "Name", "Version",
+ "SoftwareElementState"))
class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
"""
@@ -71,7 +73,7 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
"""
for pkg in self.pkgdb:
if not common.is_installed(pkg):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
objpath = self.make_op(pkg)
inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
self.assertIsSubclass(inst.path.classname, self.CLASS_NAME)
@@ -93,32 +95,46 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
"Package %s should not be installed"%pkg)
@common.mark_dangerous
- def test_enum_instances(self):
+ def test_enum_instance_names(self):
"""
Tests EnumInstances call.
TODO: test this in non-dangerous way
"""
- pkg = self.pkgdb[0]
- if common.is_installed(pkg.name):
+ pkg = self.pkgdb[0] if len(self.pkgdb) > 0 else None
+ if pkg and common.is_installed(pkg.name):
common.remove_pkg(pkg.name)
insts1 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
- common.install_pkg(pkg)
- insts2 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
- self.assertEqual(len(insts1) + 1, len(insts2))
+ if pkg:
+ self.install_pkg(pkg)
+ insts2 = self.conn.EnumerateInstanceNames(ClassName=self.CLASS_NAME)
+ self.assertEqual(len(insts1) + 1, len(insts2))
- objpath = self.make_op(pkg)
- self.assertIn(objpath['Software'],
- (inst['Software'] for inst in insts2))
+ if pkg:
+ objpath = self.make_op(pkg)
+ self.assertIn(make_path_tuple(objpath['Software']),
+ set(make_path_tuple(inst['Software']) for inst in insts2))
self.assertTrue(all(isinstance(inst, pywbem.CIMInstanceName)
for inst in insts1))
+ @common.mark_dangerous
+ def test_enum_instances(self):
+ """
+ Tests EnumInstances call.
+ """
+ pkg = self.pkgdb[0] if len(self.pkgdb) > 0 else None
+ if pkg and not common.is_installed(pkg.name):
+ self.install_pkg(pkg.name)
insts1 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
- common.remove_pkg(pkg.name)
- insts2 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ if pkg:
+ common.remove_pkg(pkg.name)
+ insts2 = self.conn.EnumerateInstances(ClassName=self.CLASS_NAME)
+ objpath = self.make_op(pkg)
+ self.assertEqual(len(insts2) + 1, len(insts1))
+ path_values = set(make_path_tuple(p["Software"]) for p in insts1)
+ self.assertIn(make_path_tuple(objpath['Software']), path_values)
+ path_values = set(make_path_tuple(p["Software"]) for p in insts2)
+ self.assertNotIn(make_path_tuple(objpath['Software']), path_values)
- self.assertEqual(len(insts2) + 1, len(insts1))
- self.assertIn(objpath['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']
@@ -164,7 +180,7 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
"""
for pkg in self.pkgdb:
if not common.is_installed(pkg.name):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
self.assertTrue(common.is_installed(pkg.name),
"Package %s must be installed"%pkg)
objpath = self.make_op(pkg)
@@ -173,7 +189,8 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
"Package %s must be uninstalled"%pkg)
with self.assertRaises(pywbem.CIMError) as cmngr:
self.conn.DeleteInstance(objpath)
- self.assertEqual(pywbem.CIM_ERR_NOT_FOUND, cmngr.exception.args[0],
+ self.assertIn(cmngr.exception.args[0],
+ [pywbem.CIM_ERR_FAILED, pywbem.CIM_ERR_NOT_FOUND],
"Package %s can not be uninstalled again"%pkg)
@common.mark_dangerous
@@ -190,7 +207,7 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
and not common.is_installed(pkg))) :
common.remove_pkg(pkg.name)
if not common.is_installed(pkg.name):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
self.assertTrue(common.is_installed(pkg),
"Package %s must be installed"%pkg)
@@ -244,8 +261,8 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
self.assertEqual(1, len(oparms))
self.assertIn("Failed", oparms)
self.assertEqual(len(oparms["Failed"]), cnt_bad,
- "Number of errors should match number of elements in"
- " Failed for %s:%s"%(pkg.name, file_path))
+ "Number of errors not correct. Failed for %s:%s" % (
+ pkg.name, file_path))
self.assertIn(file_path, (p["Name"] for p in oparms["Failed"]),
"File %s:%s should also be in failed"%(pkg.name, file_path))
@@ -259,7 +276,7 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
and not common.is_installed(pkg, False)):
common.remove_pkg(pkg.name)
if not common.is_installed(pkg.name):
- common.install_pkg(pkg, False)
+ self.install_pkg(pkg, False)
self.assertTrue(common.is_installed(pkg, False),
"Package %s must be installed"%pkg.get_nevra(False))
@@ -288,17 +305,19 @@ class TestSoftwareInstalledPackage(common.SoftwareBaseTestCase):
self.assertTrue(common.is_installed(pkg.name),
"Package %s must be installed"%pkg)
self.assertFalse(common.is_installed(pkg, False),
- "Older package %s must not be installed"%pkg.get_nevra(False))
+ "Older package %s must not be installed" %
+ pkg.get_nevra(False))
with self.assertRaises(pywbem.CIMError) as cmngr:
self.conn.InvokeMethod(
MethodName="Update",
ObjectName=objpath)
self.assertEqual(pywbem.CIM_ERR_NOT_FOUND, cmngr.exception.args[0],
- "Older package %s should not be installed"%pkg.get_nevra(False))
+ "Older package %s should not be installed" %
+ pkg.get_nevra(False))
common.remove_pkg(pkg.name)
- common.install_pkg(pkg, False)
+ self.install_pkg(pkg, False)
self.assertTrue(common.is_installed(pkg, False))
(rval, oparms) = self.conn.InvokeMethod(
diff --git a/src/software/test/test_software_package.py b/src/software/test/test_software_package.py
index 3411678..4cd5da9 100755
--- a/src/software/test/test_software_package.py
+++ b/src/software/test/test_software_package.py
@@ -66,11 +66,14 @@ class TestSoftwarePackage(common.SoftwareBaseTestCase): #pylint: disable=R0904
for key in self.KEYS:
self.assertTrue(inst.properties.has_key(key),
"OP is missing \"%s\" key for package %s"%(key, pkg))
- self.assertEqual(inst.path[key], inst[key],
- "Object paths of instance should match for %s"%pkg)
+ if key == "TargetOperatingSystem":
+ self.assertIsInstance(inst.path[key], (int, long))
+ else:
+ self.assertEqual(inst.path[key], inst[key],
+ "Object paths of instance should match for %s"%pkg)
self.assertEqual(pkg.up_rel, inst['Release'],
"Release property should match for %s"%pkg)
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
objpath['SoftwareElementState'] = pywbem.Uint16(2)
inst = self.conn.GetInstance(InstanceName=objpath, LocalOnly=False)
self.assertEqual(inst.path, objpath,
@@ -123,7 +126,7 @@ class TestSoftwarePackage(common.SoftwareBaseTestCase): #pylint: disable=R0904
"""
for pkg in self.pkgdb:
if not common.is_installed(pkg.name):
- common.install_pkg(pkg)
+ self.install_pkg(pkg)
objpath = self.make_op(pkg)
(rval, oparms) = self.conn.InvokeMethod(
MethodName="Remove",