summaryrefslogtreecommitdiffstats
path: root/src/yum/providers
diff options
context:
space:
mode:
authorMichal Minar <miminar@redhat.com>2012-09-27 13:08:16 +0200
committerMichal Minar <miminar@redhat.com>2012-09-27 13:08:16 +0200
commit0490bf7f3e9ffdaacc07397e5a42cfc06a017c11 (patch)
tree65ba288835dea2be96326fa95528f826828aa5d5 /src/yum/providers
parent0299616427b96d8822292dc166f505fb0c919381 (diff)
downloadopenlmi-providers-0490bf7f3e9ffdaacc07397e5a42cfc06a017c11.tar.gz
openlmi-providers-0490bf7f3e9ffdaacc07397e5a42cfc06a017c11.tar.xz
openlmi-providers-0490bf7f3e9ffdaacc07397e5a42cfc06a017c11.zip
initial import of yum providers
Diffstat (limited to 'src/yum/providers')
-rw-r--r--src/yum/providers/LMI_YumFileCheck.py406
-rw-r--r--src/yum/providers/LMI_YumInstalledPackage.py278
-rw-r--r--src/yum/providers/LMI_YumPackage.py636
-rw-r--r--src/yum/providers/__init__.py16
-rw-r--r--src/yum/providers/util/__init__.py17
-rw-r--r--src/yum/providers/util/common.py944
-rw-r--r--src/yum/providers/util/singletonmixin.py508
7 files changed, 2805 insertions, 0 deletions
diff --git a/src/yum/providers/LMI_YumFileCheck.py b/src/yum/providers/LMI_YumFileCheck.py
new file mode 100644
index 0000000..c5e29bc
--- /dev/null
+++ b/src/yum/providers/LMI_YumFileCheck.py
@@ -0,0 +1,406 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_YumFileCheck
+
+Instruments the CIM class LMI_YumFileCheck
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from util.common import *
+
+filecheck2model = YumFileCheck.filecheck_wrapper(
+ 'root/cimv2', 'LMI_YumFileCheck')
+
+class LMI_YumFileCheck(CIMProvider2):
+ """Instrument the CIM class LMI_YumFileCheck
+
+ Identifies a file contained by RPM package. It's located in directory
+ identified in FileName. The Invoke methods check for file existence
+ and whether its attributes match those given by RPM package.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ vpkg = YumFileCheck.object_path2yumcheck(env, model.path)
+ pkg = vpkg.po
+ fi = pkg.hdr.fiFromHeader()
+ return filecheck2model(vpkg, model['Name'], env, False)
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+ # TODO create or modify the instance
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+ return instance
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ # TODO delete the resource
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def cim_method_invoke(self, env, object_name):
+ """Implements LMI_YumFileCheck.Invoke()
+
+ The Invoke method evaluates this Check. The details of the
+ evaluation are described by the specific subclasses of CIM_Check.
+ When the SoftwareElement being checked is already installed, the
+ CIM_InstalledSoftwareElement association identifies the
+ CIM_ComputerSystem in whose context the Invoke is executed. If
+ this association is not in place, then the InvokeOnSystem method
+ should be used - since it identifies the TargetSystem as an input
+ parameter of the method. \nThe results of the Invoke method are
+ based on the return value. A zero is returned if the condition is
+ satisfied. A one is returned if the method is not supported. Any
+ other value indicates the condition is not satisfied.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Invoke()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_invoke()' \
+ % self.__class__.__name__)
+
+ vpkg = YumFileCheck.object_path2yumcheck(env, object_name)
+ fc = YumFileCheck.test_file(env,
+ YumFileCheck.checksumtype_str2pywbem(
+ vpkg.po.yumdb_info.checksum_type),
+ vpkg._files[object_name["Name"]])
+ out_params = []
+ ret = 0 if YumFileCheck.filecheck_passed(fc) else 2
+ return (pywbem.Uint32(ret), out_params)
+
+ def cim_method_invokeonsystem(self, env, object_name,
+ param_targetsystem=None):
+ """Implements LMI_YumFileCheck.InvokeOnSystem()
+
+ The InvokeOnSystem method evaluates this Check. The details of the
+ evaluation are described by the specific subclasses of CIM_Check.
+ The method\'s TargetSystem input parameter specifies the
+ ComputerSystem in whose context the method is invoked. \nThe
+ results of the InvokeOnSystem method are based on the return
+ value. A zero is returned if the condition is satisfied. A one is
+ returned if the method is not supported. Any other value indicates
+ the condition is not satisfied.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InvokeOnSystem()
+ should be invoked.
+ param_targetsystem -- The input parameter TargetSystem (type REF (pywbem.CIMInstanceName(classname='CIM_ComputerSystem', ...))
+ Reference to ComputerSystem in whose context the method is to
+ be invoked.
+
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters: none
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_invokeonsystem()' \
+ % self.__class__.__name__)
+
+ # TODO do something
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented
+ out_params = []
+ #rval = # TODO (type pywbem.Uint32)
+ return (rval, out_params)
+
+ class Values(object):
+ class TargetOperatingSystem(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ MACOS = pywbem.Uint16(2)
+ ATTUNIX = pywbem.Uint16(3)
+ DGUX = pywbem.Uint16(4)
+ DECNT = pywbem.Uint16(5)
+ Tru64_UNIX = pywbem.Uint16(6)
+ OpenVMS = pywbem.Uint16(7)
+ HPUX = pywbem.Uint16(8)
+ AIX = pywbem.Uint16(9)
+ MVS = pywbem.Uint16(10)
+ OS400 = pywbem.Uint16(11)
+ OS_2 = pywbem.Uint16(12)
+ JavaVM = pywbem.Uint16(13)
+ MSDOS = pywbem.Uint16(14)
+ WIN3x = pywbem.Uint16(15)
+ WIN95 = pywbem.Uint16(16)
+ WIN98 = pywbem.Uint16(17)
+ WINNT = pywbem.Uint16(18)
+ WINCE = pywbem.Uint16(19)
+ NCR3000 = pywbem.Uint16(20)
+ NetWare = pywbem.Uint16(21)
+ OSF = pywbem.Uint16(22)
+ DC_OS = pywbem.Uint16(23)
+ Reliant_UNIX = pywbem.Uint16(24)
+ SCO_UnixWare = pywbem.Uint16(25)
+ SCO_OpenServer = pywbem.Uint16(26)
+ Sequent = pywbem.Uint16(27)
+ IRIX = pywbem.Uint16(28)
+ Solaris = pywbem.Uint16(29)
+ SunOS = pywbem.Uint16(30)
+ U6000 = pywbem.Uint16(31)
+ ASERIES = pywbem.Uint16(32)
+ HP_NonStop_OS = pywbem.Uint16(33)
+ HP_NonStop_OSS = pywbem.Uint16(34)
+ BS2000 = pywbem.Uint16(35)
+ LINUX = pywbem.Uint16(36)
+ Lynx = pywbem.Uint16(37)
+ XENIX = pywbem.Uint16(38)
+ VM = pywbem.Uint16(39)
+ Interactive_UNIX = pywbem.Uint16(40)
+ BSDUNIX = pywbem.Uint16(41)
+ FreeBSD = pywbem.Uint16(42)
+ NetBSD = pywbem.Uint16(43)
+ GNU_Hurd = pywbem.Uint16(44)
+ OS9 = pywbem.Uint16(45)
+ MACH_Kernel = pywbem.Uint16(46)
+ Inferno = pywbem.Uint16(47)
+ QNX = pywbem.Uint16(48)
+ EPOC = pywbem.Uint16(49)
+ IxWorks = pywbem.Uint16(50)
+ VxWorks = pywbem.Uint16(51)
+ MiNT = pywbem.Uint16(52)
+ BeOS = pywbem.Uint16(53)
+ HP_MPE = pywbem.Uint16(54)
+ NextStep = pywbem.Uint16(55)
+ PalmPilot = pywbem.Uint16(56)
+ Rhapsody = pywbem.Uint16(57)
+ Windows_2000 = pywbem.Uint16(58)
+ Dedicated = pywbem.Uint16(59)
+ OS_390 = pywbem.Uint16(60)
+ VSE = pywbem.Uint16(61)
+ TPF = pywbem.Uint16(62)
+ Windows__R__Me = pywbem.Uint16(63)
+ Caldera_Open_UNIX = pywbem.Uint16(64)
+ OpenBSD = pywbem.Uint16(65)
+ Not_Applicable = pywbem.Uint16(66)
+ Windows_XP = pywbem.Uint16(67)
+ z_OS = pywbem.Uint16(68)
+ Microsoft_Windows_Server_2003 = pywbem.Uint16(69)
+ Microsoft_Windows_Server_2003_64_Bit = pywbem.Uint16(70)
+ Windows_XP_64_Bit = pywbem.Uint16(71)
+ Windows_XP_Embedded = pywbem.Uint16(72)
+ Windows_Vista = pywbem.Uint16(73)
+ Windows_Vista_64_Bit = pywbem.Uint16(74)
+ Windows_Embedded_for_Point_of_Service = pywbem.Uint16(75)
+ Microsoft_Windows_Server_2008 = pywbem.Uint16(76)
+ Microsoft_Windows_Server_2008_64_Bit = pywbem.Uint16(77)
+ FreeBSD_64_Bit = pywbem.Uint16(78)
+ RedHat_Enterprise_Linux = pywbem.Uint16(79)
+ RedHat_Enterprise_Linux_64_Bit = pywbem.Uint16(80)
+ Solaris_64_Bit = pywbem.Uint16(81)
+ SUSE = pywbem.Uint16(82)
+ SUSE_64_Bit = pywbem.Uint16(83)
+ SLES = pywbem.Uint16(84)
+ SLES_64_Bit = pywbem.Uint16(85)
+ Novell_OES = pywbem.Uint16(86)
+ Novell_Linux_Desktop = pywbem.Uint16(87)
+ Sun_Java_Desktop_System = pywbem.Uint16(88)
+ Mandriva = pywbem.Uint16(89)
+ Mandriva_64_Bit = pywbem.Uint16(90)
+ TurboLinux = pywbem.Uint16(91)
+ TurboLinux_64_Bit = pywbem.Uint16(92)
+ Ubuntu = pywbem.Uint16(93)
+ Ubuntu_64_Bit = pywbem.Uint16(94)
+ Debian = pywbem.Uint16(95)
+ Debian_64_Bit = pywbem.Uint16(96)
+ Linux_2_4_x = pywbem.Uint16(97)
+ Linux_2_4_x_64_Bit = pywbem.Uint16(98)
+ Linux_2_6_x = pywbem.Uint16(99)
+ Linux_2_6_x_64_Bit = pywbem.Uint16(100)
+ Linux_64_Bit = pywbem.Uint16(101)
+ Other_64_Bit = pywbem.Uint16(102)
+ Microsoft_Windows_Server_2008_R2 = pywbem.Uint16(103)
+ VMware_ESXi = pywbem.Uint16(104)
+ Microsoft_Windows_7 = pywbem.Uint16(105)
+ CentOS_32_bit = pywbem.Uint16(106)
+ CentOS_64_bit = pywbem.Uint16(107)
+ Oracle_Enterprise_Linux_32_bit = pywbem.Uint16(108)
+ Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109)
+ eComStation_32_bitx = pywbem.Uint16(110)
+
+ class SoftwareElementState(object):
+ Deployable = pywbem.Uint16(0)
+ Installable = pywbem.Uint16(1)
+ Executable = pywbem.Uint16(2)
+ Running = pywbem.Uint16(3)
+
+ class FileType(object):
+ Unknown = pywbem.Uint16(0)
+ File = pywbem.Uint16(1)
+ Directory = pywbem.Uint16(2)
+ Symlink = pywbem.Uint16(3)
+ FIFO = pywbem.Uint16(4)
+ Character_Device = pywbem.Uint16(5)
+ Block_Device = pywbem.Uint16(6)
+
+## end of class LMI_YumFileCheckProvider
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_yumfilecheck_prov = LMI_YumFileCheck(env)
+ return {'LMI_YumFileCheck': lmi_yumfilecheck_prov}
diff --git a/src/yum/providers/LMI_YumInstalledPackage.py b/src/yum/providers/LMI_YumInstalledPackage.py
new file mode 100644
index 0000000..cb432d2
--- /dev/null
+++ b/src/yum/providers/LMI_YumInstalledPackage.py
@@ -0,0 +1,278 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_YumInstalledPackage
+
+Instruments the CIM class LMI_YumInstalledPackage
+
+"""
+
+import pywbem
+import socket
+from pywbem.cim_provider2 import CIMProvider2
+from LMI_YumPackage import LMI_YumPackage
+
+class LMI_YumInstalledPackage(CIMProvider2):
+ """Instrument the CIM class LMI_YumInstalledPackage
+
+ The InstalledSoftwareElement association allows the identification of
+ the ComputerSystem on which a particular SoftwareElement is installed.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+
+ # TODO fetch system resource matching the following keys:
+ # model['System']
+ # model['Software']
+
+ return model
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'System': None, 'Software': None})
+
+ yum_package = LMI_YumPackage(env)
+ yum_package_path = pywbem.CIMInstanceName("LMI_YumPackage",
+ namespace=model.path.namespace,
+ host=model.path.host)
+ yum_package_model = pywbem.CIMInstance(classname="LMI_YumPackage",
+ path=yum_package_path)
+ for iname in yum_package.enum_instances(
+ env, yum_package_model, True):
+ model['System'] = pywbem.CIMInstanceName(
+ classname='Linux_ComputerSystem',
+ keybindings={
+ "CreationClassName": "Linux_ComputerSystem"
+ , "Name" : socket.gethostname() },
+ host=model.path.host,
+ namespace=model.path.namespace)
+ model['Software'] = iname.path
+ if keys_only:
+ yield model
+ else:
+ try:
+ yield self.get_instance(env, model)
+ except pywbem.CIMError, (num, msg):
+ if num not in (pywbem.CIM_ERR_NOT_FOUND,
+ pywbem.CIM_ERR_ACCESS_DENIED):
+ raise
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+ # TODO create or modify the instance
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+ return instance
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ # TODO delete the resource
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement
+
+ def references(self, env, object_name, model, result_class_name, role,
+ result_role, keys_only):
+ """Instrument Associations.
+
+ All four association-related operations (Associators, AssociatorNames,
+ References, ReferenceNames) are mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName that defines the source
+ CIM Object whose associated Objects are to be returned.
+ model -- A template pywbem.CIMInstance to serve as a model
+ of the objects to be returned. Only properties present on this
+ model need to be set.
+ result_class_name -- If not empty, this string acts as a filter on
+ the returned set of Instances by mandating that each returned
+ Instances MUST represent an association between object_name
+ and an Instance of a Class whose name matches this parameter
+ or a subclass.
+ role -- If not empty, MUST be a valid Property name. It acts as a
+ filter on the returned set of Instances by mandating that each
+ returned Instance MUST refer to object_name via a Property
+ whose name matches the value of this parameter.
+ result_role -- If not empty, MUST be a valid Property name. It acts
+ as a filter on the returned set of Instances by mandating that
+ each returned Instance MUST represent associations of
+ object_name to other Instances, where the other Instances play
+ the specified result_role in the association (i.e. the
+ name of the Property in the Association Class that refers to
+ the Object related to object_name MUST match the value of this
+ parameter).
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ The following diagram may be helpful in understanding the role,
+ result_role, and result_class_name parameters.
+ +------------------------+ +-------------------+
+ | object_name.classname | | result_class_name |
+ | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ |
+ +------------------------+ +-------------------+
+ | +-----------------------------------+ |
+ | | [Association] model.classname | |
+ | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
+ +--------------+ object_name.classname REF role | |
+ (CIMInstanceName) | result_class_name REF result_role +------+
+ | |(CIMInstanceName)
+ +-----------------------------------+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.references()' \
+ % self.__class__.__name__)
+ ch = env.get_cimom_handle()
+
+ # If you want to get references for free, implemented in terms
+ # of enum_instances, just leave the code below unaltered.
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_ComputerSystem') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_YumPackage'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
+
+## end of class LMI_YumInstalledPackageProvider
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_yuminstalledpackage_prov = LMI_YumInstalledPackage(env)
+ return {'LMI_YumInstalledPackage': lmi_yuminstalledpackage_prov}
diff --git a/src/yum/providers/LMI_YumPackage.py b/src/yum/providers/LMI_YumPackage.py
new file mode 100644
index 0000000..f1441b0
--- /dev/null
+++ b/src/yum/providers/LMI_YumPackage.py
@@ -0,0 +1,636 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Python Provider for LMI_YumPackage
+
+Instruments the CIM class LMI_YumPackage
+
+"""
+
+import itertools
+import datetime
+#import yum
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+from util.common import *
+
+pkg2model = YumPackage.pkg2model_wrapper('root/cimv2', "LMI_YumPackage")
+
+class LMI_YumPackage(CIMProvider2):
+ """Instrument the CIM class LMI_YumPackage
+
+ RPM package installed on particular computer system with YUM (The
+ Yellowdog Updated, Modified) package manager.
+
+ """
+
+ def __init__ (self, env):
+ logger = env.get_logger()
+ logger.log_debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+
+ def get_instance(self, env, model):
+ """Return an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstance to be returned. The
+ key properties are set on this instance to correspond to the
+ instanceName that was requested. The properties of the model
+ are already filtered according to the PropertyList from the
+ request. Only properties present in the model need to be
+ given values. If you prefer, you can set all of the
+ values, and the instance will be filtered for you.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.get_instance()' \
+ % self.__class__.__name__)
+
+ pkg = YumPackage.object_path2pkg(env, model.path)
+ return pkg2model(pkg, env, keys_only=False, model=model)
+
+ def enum_instances(self, env, model, keys_only):
+ """Enumerate instances.
+
+ The WBEM operations EnumerateInstances and EnumerateInstanceNames
+ are both mapped to this method.
+ This method is a python generator
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ model -- A template of the pywbem.CIMInstances to be generated.
+ The properties of the model are already filtered according to
+ the PropertyList from the request. Only properties present in
+ the model need to be given values. If you prefer, you can
+ always set all of the values, and the instance will be filtered
+ for you.
+ keys_only -- A boolean. True if only the key properties should be
+ set on the generated instances.
+
+ Possible Errors:
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.enum_instances()' \
+ % self.__class__.__name__)
+
+ # Prime model.path with knowledge of the keys, so key values on
+ # the CIMInstanceName (model.path) will automatically be set when
+ # we set property values on the model.
+ model.path.update({'TargetOperatingSystem': None, 'Version': None,
+ 'SoftwareElementState': None, 'Name': None,
+ 'SoftwareElementID': None})
+
+ with YumDB.getInstance(env) as yb:
+ for pkg in yb.rpmdb:
+ yield pkg2model(pkg, env, keys_only, model)
+
+ def set_instance(self, env, instance, modify_existing):
+ """Return a newly created or modified instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance -- The new pywbem.CIMInstance. If modifying an existing
+ instance, the properties on this instance have been filtered by
+ the PropertyList from the request.
+ modify_existing -- True if ModifyInstance, False if CreateInstance
+
+ Return the new instance. The keys must be set on the new instance.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only
+ valid if modify_existing is False, indicating that the operation
+ was CreateInstance)
+ CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid
+ if modify_existing is True, indicating that the operation
+ was ModifyInstance)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.set_instance()' \
+ % self.__class__.__name__)
+
+ _has_key = lambda k: k in instance.properties or (
+ instance.path is not None and k in instance.path)
+ def _get_key(k):
+ v = instance.properties.get(k, None)
+ if isinstance(v, pywbem.CIMProperty): return v.value
+ if v is not None: return v
+ logger.log_error('missing key "{}" in inst.props'.format(k))
+ return instance.path[k] if k in instance.path else None
+
+ # parse and check arguments
+ if modify_existing is True:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "MofifyInstance is not supported")
+ match_props = {} # args for match_pkg
+ installed_kwargs = {} # args for rpmdb.installed
+ if instance['SoftwareElementID']:
+ m = re_nevra.match(instance['SoftwareElementID'])
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "SoftwareElementID could not be parsed.")
+ match_props['nevra'] = instance['SoftwareElementID']
+ match_props['name'] = m.group('name')
+ for a in ('name', 'epoch', 'ver', 'rel', 'arch'):
+ installed_kwargs[a] = m.group(a)
+ else:
+ for matchattr, instattr in (
+ ('name', 'name'), ('epoch', 'epoch'), ('version', 'ver'),
+ ('release', 'rel'), ('arch', 'arch')):
+ if _has_key(matchattr) and _get_key(matchattr):
+ match_props[matchattr] = installed_kwargs[instattr] = \
+ _get_key(matchattr)
+
+ if not match_props:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Too few key values given (give at least a Name).")
+ if not 'name' in match_props and not 'nevra' in match_props:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ "Missing either Name or SoftwareElementID property.")
+
+ with YumDB.getInstance(env) as yb:
+ # check already installed
+ if yb.rpmdb.installed(**installed_kwargs):
+ raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
+ "Package is already installed.")
+
+ # get available packages
+ pl = yb.doPackageLists('available', showdups=True)
+ exact, matched, unmatched = yum.packages.parsePackages(
+ pl.available, [match_props['name']])
+ exact_orig = exact
+ exact = sorted( [ p for p in exact if match_pkg(p, **match_props) ]
+ , key=lambda a: a.evra)
+ if len(exact) == 0:
+ logger.log_error('could not find any package for query: {}'
+ ' in list: {}'
+ .format(match_props, [p.nevra for p in exact_orig]))
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package found.")
+ if len(exact) > 1:
+ logger.log_info('found multiple matching packages'
+ ' for query: {}'.format(match_props))
+ pkg = exact[-1] # select highest version
+ else:
+ logger.log_debug('exact match found for query: {}'
+ .format(match_props))
+ pkg = exact[0]
+
+ logger.log_info('installing package {}'.format(pkg.nevra))
+ # install
+ yb.install(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package installed'.format(pkg.nevra))
+
+ # return instance
+ instance = pkg2model(pkg, env, True, instance)
+ return self.get_instance(env, instance)
+
+ def delete_instance(self, env, instance_name):
+ """Delete an instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ instance_name -- A pywbem.CIMInstanceName specifying the instance
+ to delete.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_NOT_SUPPORTED
+ CIM_ERR_INVALID_NAMESPACE
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized
+ or otherwise incorrect parameters)
+ CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified
+ namespace)
+ CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM
+ Instance does not exist in the specified namespace)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.delete_instance()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env) as yb:
+ pkg = YumPackage.object_path2pkg(env, instance_name)
+ logger.log_info('removing package "%s"' % pkg.nevra)
+ yb.remove(pkg)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package "%s" removed' % pkg.nevra)
+
+ def cim_method_checkintegrity(self, env, object_name):
+ """Implements LMI_YumPackage.CheckIntegrity()
+
+ Verify existence and attributes of files installed from RPM
+ package. If all files installed exist and their attributes
+ matches, method returns "Pass". "Not passed" is returned when
+ arbitrary file differs in its attributes. And "Error" if
+ verification could not be done.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method CheckIntegrity()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.CheckIntegrity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Failed -- (type REF (pywbem.CIMInstanceName(classname='LMI_YumFileCheck', ...))
+ Array of references to LMI_YumFileCheck, that did not pass
+ verification.
+
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_checkintegrity()' \
+ % self.__class__.__name__)
+
+ # TODO do something
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented
+ out_params = []
+ #out_params+= [pywbem.CIMParameter('failed', type='reference',
+ # value=[pywbem.CIMInstanceName(classname='LMI_YumFileCheck', ...),])] # TODO
+ #rval = # TODO (type pywbem.Uint32 self.Values.CheckIntegrity)
+ return (rval, out_params)
+
+ def cim_method_update(self, env, object_name,
+ param_epoch=None,
+ param_release=None,
+ param_version=None):
+ """Implements LMI_YumPackage.Update()
+
+ Updates the package to latest version. When any of "version" or
+ "release" argument is given, install only matching available
+ package. Otherwise try to update to newest package available.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method Update()
+ should be invoked.
+ param_release -- The input parameter release (type unicode)
+ Specify particular release of package to update to. Update to
+ newest, when empty
+
+ param_version -- The input parameter version (type unicode)
+ Specify particular version of package to update to. Update to
+ newest, when empty
+
+ Returns a two-tuple containing the return value (type pywbem.Uint16 self.Values.Update)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Installed -- (type REF (pywbem.CIMInstanceName(classname='LMI_YumPackage', ...))
+ The reference to newly installed package, if installation was
+ successful. Otherwise reference to current package.
+
+ Possible Errors:
+ CIM_ERR_ACCESS_DENIED
+ CIM_ERR_INVALID_PARAMETER (including missing, duplicate,
+ unrecognized or otherwise incorrect parameters)
+ CIM_ERR_NOT_FOUND (the target CIM Class or instance does not
+ exist in the specified namespace)
+ CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor
+ the invocation request)
+ CIM_ERR_FAILED (some other unspecified error occurred)
+
+ """
+
+ logger = env.get_logger()
+ logger.log_debug('Entering %s.cim_method_update()' \
+ % self.__class__.__name__)
+
+ with YumDB.getInstance(env) as yb:
+ orig = YumPackage.object_path2pkg(env, object_name)
+
+ 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["Name"]))
+
+ pl = yb.doPackageLists('all', showdups=True)
+ exact, matched, unmatched = yum.packages.parsePackages(
+ itertools.chain(pl.available, pl.installed),
+ [orig.name])
+ exact = sorted(exact, key=lambda a:a.evra)
+
+ try:
+ pkg = [p for p in exact
+ if (not param_epoch or param_epoch == p.epoch)
+ and (not param_version or param_version == p.ver)
+ and (not param_release or param_release == p.rel)] [-1]
+ except IndexError:
+ logger.log_error(
+ 'could not find any matching available package')
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND)
+ out_params = [pywbem.CIMParameter('Installed', type='reference',
+ value=pkg2model(pkg, env, True))]
+ if orig.evra == pkg.evra:
+ logger.log_info('already up to date')
+ return (self.Values.Update.Already_newest, out_params)
+
+ yb.update(update_to=True,
+ name=pkg.name,
+ epoch=pkg.epoch,
+ version=pkg.version,
+ release=pkg.release)
+ yb.buildTransaction()
+ yb.processTransaction()
+ logger.log_info('package {} updated to: {}'.format(
+ orig.name, pkg.evra))
+
+ return (self.Values.Update.Successful_installation, out_params)
+
+ 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 Update(object):
+ Already_newest = pywbem.Uint16(0)
+ Successful_installation = pywbem.Uint16(1)
+ Failed = pywbem.Uint16(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 CheckIntegrity(object):
+ Pass = pywbem.Uint32(0)
+ Not_passed = pywbem.Uint32(1)
+ Error = pywbem.Uint32(2)
+
+## end of class LMI_YumPackageProvider
+
+## get_providers() for associating CIM Class Name to python provider class name
+
+def get_providers(env):
+ lmi_yumpackage_prov = LMI_YumPackage(env)
+ return {'LMI_YumPackage': lmi_yumpackage_prov}
diff --git a/src/yum/providers/__init__.py b/src/yum/providers/__init__.py
new file mode 100644
index 0000000..452bf25
--- /dev/null
+++ b/src/yum/providers/__init__.py
@@ -0,0 +1,16 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/src/yum/providers/util/__init__.py b/src/yum/providers/util/__init__.py
new file mode 100644
index 0000000..fddeea6
--- /dev/null
+++ b/src/yum/providers/util/__init__.py
@@ -0,0 +1,17 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
diff --git a/src/yum/providers/util/common.py b/src/yum/providers/util/common.py
new file mode 100644
index 0000000..1519a9a
--- /dev/null
+++ b/src/yum/providers/util/common.py
@@ -0,0 +1,944 @@
+# Software Management Providers
+#
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Common utilities for LMI_Yum* providers
+"""
+
+import collections
+from datetime import datetime
+import grp
+import hashlib
+import os
+import platform
+import pwd
+import re
+import socket
+import stat
+import pywbem
+import yum
+import cmpi_pywbem_bindings as pycimmb
+import singletonmixin
+
+re_evra = re.compile(r'^(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+)$')
+re_nevra = re.compile(r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<ver>[^-]+)'
+ r'-(?P<rel>.+)\.(?P<arch>[^.]+))$')
+
+rpmdb_path = '/var/lib/rpm/Packages'
+
+class YumDB(singletonmixin.Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, env, *args, **kwargs):
+ if not isinstance(env, pycimmb.ProviderEnvironment):
+ raise TypeError("env must be instance of"
+ " pycimmb.ProviderEnvironment")
+ self._yum = yum.YumBase(*args, **kwargs)
+ self._db_mtime = 0
+ self._lock_cnt = 0
+ self.env = env
+ env.get_logger().log_info('init called')
+
+ def is_dirty(self):
+ return self._db_mtime < os.stat(rpmdb_path).st_mtime
+
+ def is_locked(self):
+ return self._yum._lockfile is not None
+
+ def update_db(self):
+ self.env.get_logger().log_info('updating rpmdb')
+ self._db_mtime = os.stat(rpmdb_path).st_mtime
+ self._yum.doConfigSetup()
+ self._yum.doTsSetup()
+ self._yum.doRpmDBSetup()
+
+ def __enter__(self):
+ self._lock_cnt += 1
+ if self._lock_cnt < 2:
+ 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 > 0:
+ self._yum.closeRpmDB()
+ self._yum.doUnlock()
+ self._lock_cnt -= 1
+
+ def __getattr__(self, name):
+ if not self.is_locked() and self.is_dirty():
+ self.update_db()
+ return getattr(self._yum, name)
+
+def _get_distname():
+ if hasattr(platform, 'linux_distribution'):
+ return platform.linux_distribution(
+ full_distribution_name=False)[0].lower()
+ else:
+ return platform.dist()[0].lower()
+
+
+def get_target_operating_system():
+ """
+ @return (val, text).
+ Where val is a number from ValueMap of TargetOperatingSystem property
+ of CIM_SoftwareElement class and text is its testual representation.
+ """
+
+ system = platform.system()
+ if system.lower() == 'linux':
+ try:
+ val, dist = \
+ { 'redhat' : (79, 'RedHat Enterprise Linux')
+ , 'suse' : (81, 'SUSE')
+ , 'mandriva' : (88, 'Mandriva')
+ , 'ubuntu' : (93, 'Ubuntu')
+ , 'debian' : (95, 'Debian')
+ }[_get_distname()]
+ except KeyError:
+ linrel = platform.uname()[2]
+ if linrel.startswith('2.4'):
+ val, dist = (97, 'Linux 2.4.x')
+ elif linrel.startswith('2.6'):
+ val, dist = (99, 'Linux 2.6.x')
+ else:
+ return (36, 'LINUX') # no check for x86_64
+ if platform.machine() == 'x86_64':
+ val += 1
+ dist += ' 64-Bit'
+ return (val, dist)
+ elif system.lower() in ('macosx', 'darwin'):
+ return (2, 'MACOS')
+ # elif system.lower() == 'windows': # no general value
+ else:
+ return (0, 'Unknown')
+
+def 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 match_pkg(pkg, **kwargs):
+ """
+ all not Null and not empty arguments will be matched against pkg
+ attributes; if all of them match, return True
+
+ possible arguments:
+ name, epoch, version, release, arch
+ evra, nevra
+ """
+ for a in ('evra', 'nevra', 'name', 'epoch', 'version', 'release', 'arch'):
+ v = kwargs.get(a, None)
+ if v and getattr(pkg, a) != v:
+ return False
+ return True
+
+class YumPackage:
+ """
+ Just a namespace for common function related to YumPackage provider.
+ """
+
+ @staticmethod
+ def parse_arch(arch, env):
+ try:
+ res = \
+ { "noarch" : 0
+ , "i386" : 4
+ , "i586" : 5
+ , "i686" : 6
+ , "x86_64" : 7
+ , "athlon" : 8
+ , "ia32e" : 10
+ , "ia64" : 11
+ , "powerpc" : 12
+ , "ppc" : 12
+ , "ppc64" : 13
+ , "sparc" : 15
+ , "sparcv9" : 16
+ , "sparc64" : 17
+ , "arm" : 20
+ , "arm6" : 21
+ , "arm7" : 22
+ , "arm8" : 23
+ , "arm9" : 24
+ , "strongarm" : 30
+ , "xscale" : 31
+ , "parisc" : 35
+ , "parisc11" : 36
+ , "mips" : 40
+ , "mips32" : 41
+ , "mips64" : 42
+ , "mipsnecvr4xxx" : 45
+ , "mipsrm7000" : 46
+ , "mipstx39" : 50
+ , "mipstx49" : 51
+ , "v850" : 55
+ , "alpha" : 60
+ , "alphaev6" : 61
+ , "alphaev7" : 62
+ , "alphaev8" : 63
+ , "alphaev9" : 64
+ , "sh" : 70
+ , "sh2" : 71
+ , "sh3" : 72
+ , "sh4" : 73
+ , "sh5" : 74
+ }[arch.lower()]
+ except KeyError:
+ logger = env.get_logger()
+ logger.log_error("failed to parse architecture '{}'".format(arch))
+ res = 2
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def parse_license(lic, env):
+ try:
+ res = \
+ { "freely redistributable without restriction" : 2
+ , "glide" : 5
+ , "afl" : 6
+ , "adobe" : 7
+ , "mit" : 10
+ , "mit with advertising" : 11
+ , "agpl" : 15
+ , "agplv1" : 16
+ , "agplv3" : 17
+ , "agplv3+" : 18
+ , "agplv3 with exceptions" : 19
+ , "adsl" : 25
+ , "amdplpa" : 26
+ , "asl" : 30
+ , "asl 1.0" : 31
+ , "asl 1.1" : 32
+ , "asl 2.0" : 33
+ , "artistic clarified" : 35
+ , "artistic 2.0" : 36
+ , "arl" : 40
+ , "aal" : 41
+ , "bahyph" : 42
+ , "barr" : 43
+ , "beerware" : 44
+ , "beopen" : 45
+ , "bibtex" : 46
+ , "boost" : 47
+ , "borceux" : 48
+ , "bsd" : 50
+ , "bsd with advertising" : 51
+ , "bsd with attribution" : 52
+ , "bsd protection" : 53
+ , "ampas bsd" : 55
+ , "lbnl bsd" : 56
+ , "ricebsd" : 57
+ , "catosl" : 65
+ , "cecill" : 66
+ , "cecill-b" : 67
+ , "cecill-c" : 68
+ , "netscape" : 70
+ , "cnri" : 71
+ , "cddl" : 72
+ , "cpl" : 73
+ , "condor" : 75
+ , "copyright only" : 76
+ , "cpal" : 77
+ , "gpl" : 80
+ , "gpl+" : 81
+ , "gpl+ with exceptions" : 82
+ , "gplv1" : 83
+ , "gplv2" : 84
+ , "gplv2 with exceptions" : 85
+ , "gplv2+" : 86
+ , "gplv2+ with exceptions" : 87
+ , "gplv3" : 88
+ , "gplv3 with exceptions" : 89
+ , "gplv3+" : 90
+ , "gplv3+ with exceptions" : 91
+ , "gpl+ or artistic" : 100
+ , "gplv2 or artistic" : 101
+ , "gplv2+ or artistic" : 102
+ , "lgpl" : 110
+ , "lgplv2" : 111
+ , "lgplv2 with exceptions" : 112
+ , "lgplv2+" : 113
+ , "lgplv3" : 115
+ , "lgplv3 with exceptions" : 116
+ , "lgplv3+" : 117
+ , "cc0" : 125
+ , "crossword" : 126
+ , "crystal stacker" : 127
+ , "mpl" : 130
+ , "mplv1.0" : 131
+ , "mplv1.1" : 132
+ , "mplv2.0" : 133
+ , "diffmark" : 135
+ , "wtfpl" : 136
+ , "doc" : 137
+ , "dotseqn" : 138
+ , "dsdp" : 139
+ , "dvipdfm" : 140
+ , "epl" : 141
+ , "ecos" : 142
+ , "ecl 1.0" : 145
+ , "ecl 2.0" : 146
+ , "egenix" : 150
+ , "efl 2.0" : 151
+ , "entessa" : 152
+ , "erpl" : 153
+ , "eu datagrid" : 154
+ , "eupl 1.1" : 155
+ , "eurosym" : 156
+ , "fair" : 157
+ , "ftl" : 158
+ , "gitware" : 159
+ , "gl2ps" : 160
+ , "glulxe" : 161
+ , "gnuplot" : 162
+ , "haskellreport" : 163
+ , "ibm" : 164
+ , "imatrix" : 165
+ , "imagemagick" : 166
+ , "imlib2" : 167
+ , "ijg" : 168
+ , "intel acpi" : 169
+ , "interbase" : 170
+ , "isc" : 171
+ , "jabber" : 172
+ , "jasper" : 173
+ , "jpython" : 174
+ , "julius" : 175
+ , "knuth" : 176
+ , "lppl" : 177
+ , "latex2e" : 178
+ , "leptonica" : 179
+ , "lhcyr" : 180
+ , "libtiff" : 181
+ , "logica" : 182
+ , "makeindex" : 183
+ , "mecab-ipadic" : 184
+ , "ms-pl" : 185
+ , "ms-rl" : 186
+ , "midnight" : 187
+ , "miros" : 188
+ , "mod_macro" : 189
+ , "motosoto" : 190
+ , "naumen" : 191
+ , "ncsa" : 192
+ , "netcdf" : 193
+ , "ngpl" : 194
+ , "nosl" : 195
+ , "newmat" : 196
+ , "newsletr" : 197
+ , "nokia" : 198
+ , "nlpl" : 200
+ , "noweb" : 201
+ , "openldap" : 202
+ , "oml" : 203
+ , "openpbs" : 204
+ , "osl 1.0" : 205
+ , "osl 1.1" : 206
+ , "osl 2.0" : 207
+ , "osl 2.1" : 208
+ , "osl 3.0" : 209
+ , "openssl" : 215
+ , "oreilly" : 216
+ , "par" : 217
+ , "phorum" : 220
+ , "php" : 221
+ , "plaintex" : 222
+ , "plexus" : 223
+ , "postgresql" : 224
+ , "psfrag" : 225
+ , "psutils" : 226
+ , "public domain" : 227
+ , "python" : 228
+ , "qhull" : 229
+ , "qpl" : 230
+ , "rdisc" : 231
+ , "rpsl" : 232
+ , "romio" : 233
+ , "rsfs" : 235
+ , "ruby" : 236
+ , "saxpath" : 237
+ , "scea" : 238
+ , "scrip" : 239
+ , "sendmail" : 240
+ , "sleepycat" : 241
+ , "slib" : 242
+ , "snia" : 243
+ , "sissl" : 244
+ , "spl" : 245
+ , "tcl" : 246
+ , "teeworlds" : 247
+ , "tpl" : 248
+ , "threeparttable" : 249
+ , "tmate" : 250
+ , "torquev1.1" : 251
+ , "tosl" : 252
+ , "ucd" : 253
+ , "vim" : 254
+ , "vnlsl" : 255
+ , "vostrom" : 256
+ , "vsl" : 257
+ , "w3c" : 258
+ , "webmin" : 259
+ , "wsuipa" : 260
+ , "wxwidgets" : 261
+ , "xinetd" : 262
+ , "xerox" : 263
+ , "xpp" : 264
+ , "xskat" : 265
+ , "yplv1.1" : 266
+ , "zed" : 270
+ , "zend" : 271
+ , "zplv1.0" : 275
+ , "zplv2.0" : 276
+ , "zplv2.1" : 277
+ , "zlib" : 280
+ , "zlib with acknowledgement" : 281
+
+ , "cdl" : 400
+ , "cc-by" : 401
+ , "cc-by-sa" : 402
+ , "cc-by-nd" : 403
+ , "dsl" : 405
+ , "dmtf" : 406
+ , "oal" : 407
+ , "fbsddl" : 408
+ , "gfdl" : 409
+ , "ieee" : 410
+ , "ldpl" : 411
+ , "ofsfdl" : 412
+ , "open publication" : 413
+ , "public use" : 414
+ , "efml" : 415
+ , "free art" : 416
+ , "geotratis" : 417
+ , "green openmusic" : 418
+ , "ofl" : 419
+ , "utopia" : 420
+ , "ams" : 421
+ , "arphic" : 422
+ , "baekmuk" : 423
+ , "bitstream vera" : 424
+ , "charter" : 425
+ , "doublestroke" : 426
+ , "ec" : 427
+ , "elvish" : 428
+ , "lppl" : 429
+ , "hershey" : 430
+ , "ipa" : 431
+ , "liberation" : 432
+ , "lucida" : 433
+ , "mgopen" : 434
+ , "mplus" : 435
+ , "ptfl" : 436
+ , "punknova" : 437
+ , "stix" : 438
+ , "wadalab" : 439
+ , "xano" : 440
+ }[lic.lower()]
+ except KeyError:
+ logger = env.get_logger()
+ logger.log_error("failed to parse license '{}'".format(lic))
+ res = 1
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def parse_group(group, env):
+ try:
+ res = \
+ { "amusements/games" : 3
+ , "amusements/graphics" : 4
+ , "applications/archiving" : 5
+ , "applications/communications" : 6
+ , "applications/databases" : 7
+ , "applications/editors" : 8
+ , "applications/emulators" : 9
+ , "applications/engineering" : 10
+ , "applications/file" : 11
+ , "applications/internet" : 12
+ , "applications/multimedia" : 13
+ , "applications/productivity" : 14
+ , "applications/publishing" : 15
+ , "applications/system" : 16
+ , "applications/text" : 17
+ , "development/build tools" : 18
+ , "development/debug" : 19
+ , "development/debuggers" : 20
+ , "development/documentation" : 21
+ , "development/java" : 22
+ , "development/languages" : 23
+ , "development/libraries" : 24
+ , "development/libraries/java" : 25
+ , "development/system" : 26
+ , "development/tools" : 27
+ , "documentation" : 28
+ , "internet/www/dynamic content" : 29
+ , "system/libraries" : 30
+ , "system environment/base" : 31
+ , "system environment/daemons" : 32
+ , "system environment/kernel" : 33
+ , "system environment/libraries" : 34
+ , "system environment/shells" : 35
+ , "text processing/markup/xml" : 36
+ , "user interface/desktops" : 37
+ , "user interface/x" : 38
+ , "user interface/x hardware support" : 39
+ , "utilities" : 40
+ }[group.lower()]
+ except KeyError:
+ logger = env.get_logger()
+ if not group or group.lower() == "unspecified":
+ logger.log_info("unspecified group '{}'".format(group))
+ res = 2
+ else:
+ logger.log_error("failed to parse group '{}'".format(group))
+ res = 0
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def object_path2pkg(env, op):
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be an instance of CIMInstanceName")
+ tos = get_target_operating_system()[0]
+ if ( not op['Name'] or not op['SoftwareElementID']
+ or not op['SoftwareElementID'].startswith(op['Name'])
+ or op['SoftwareElementID'].find(op['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+ if op['SoftwareElementState'] not in ("2", 2):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Only \"Executable\" software element state supported")
+ if not check_target_operating_system(op['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not op['Name'] or not op['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ m = re_nevra.match(op['SoftwareElementID'])
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-epoch:version-release.arch).")
+ if op['Version'] != m.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+ kwargs = dict((k, m.group(k)) for k in
+ ('name', 'epoch', 'ver', 'rel', 'arch'))
+ with YumDB.getInstance(env) as yb:
+ if not yb.rpmdb.installed(**kwargs):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Package not installed")
+
+ pl = yb.doPackageLists('installed')
+ exact, matched, unmatched = yum.packages.parsePackages(
+ pl.installed, [op['Name']])
+ exact = yum.misc.unique(exact)
+ for pkg in exact:
+ if pkg.evra == m.group('evra'): break
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package installed")
+ return pkg
+
+ @staticmethod
+ def pkg2model_wrapper(namespace, classname):
+ """
+ @return a function that transforms YumAvailablePackage object
+ to CIMInstanceName object
+ """
+
+ tos = pywbem.Uint16(get_target_operating_system()[0])
+
+ def pkg2model(pkg, env, keys_only=True, model=None):
+ """
+ @param model if None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ #if not isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ #if not isinstance(pkg, yum.packages.YumHeaderPackage):
+ if not isinstance(pkg, yum.packages.YumAvailablePackage):
+ raise TypeError(
+ "pkg must be an instance of YumAvailablePackage")
+ if model is None:
+ model = pywbem.CIMInstanceName(classname, namespace=namespace)
+ if not keys_only:
+ model = pywbem.CIMInstance(classname, path=model)
+ model['Name'] = pkg.name
+ model['SoftwareElementID'] = pkg.nevra
+ model['SoftwareElementState'] = pywbem.Uint16(2)
+ model['TargetOperatingSystem'] = tos
+ model['Version'] = pkg.version
+ if not keys_only:
+ model['Caption'] = pkg.summary
+ model['Description'] = pkg.description
+ 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"] = YumPackage.parse_arch(pkg.arch, env)
+ model['License'] = YumPackage.parse_license(pkg.license, env)
+ model['LicenseString']= pkg.license
+ model['Group'] = YumPackage.parse_group(pkg.group, env)
+ model['GroupString'] = pkg.group
+ model['Size'] = pywbem.Uint64(pkg.size)
+ return model
+
+ return pkg2model
+
+class YumFileCheck:
+ """
+ Just a namespace for functions related to FileCheck provider.
+ """
+
+ passed_flags_descriptions = (
+ "Existence",
+ "File Type",
+ "File Size",
+ "File Mode",
+ "Checksum",
+ "Device major/minor number",
+ "Symlink Target",
+ "User Ownership", "Group Ownership",
+ "Modify Time")
+
+ checksumtype_str2num = dict((v, k) for (k, v) in
+ yum.constants.RPM_CHECKSUM_TYPES.items())
+
+ @staticmethod
+ def checksumtype_num2hash(csumt):
+ return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
+
+ @staticmethod
+ def checksumtype_str2pywbem(alg):
+ try:
+ res = YumFileCheck.checksumtype_str2num[alg.lower()]
+ except KeyError: res = 0
+ return pywbem.Uint16(res)
+
+ @staticmethod
+ def filetype_str2pywbem(ft):
+ try:
+ return pywbem.Uint16(
+ { 'file' : 1
+ , 'directory' : 2
+ , 'symlink' : 3
+ , 'fifo' : 4
+ , 'character device' : 5
+ , 'block device' : 6
+ }[ft])
+ except KeyError:
+ return pywbem.Uint16(0)
+
+ @staticmethod
+ def filetype_mode2pywbem(mode):
+ for i, name in enumerate(
+ ('REG', 'DIR', 'LNK', 'FIFO', 'CHR', 'BLK'), 1):
+ if getattr(stat, 'S_IS' + name)(mode):
+ return pywbem.Uint16(i)
+ return pywbem.Uint16(0)
+
+ @staticmethod
+ def mode2pywbem_flags(mode):
+ """
+ @param mode if None, file does not exist
+ """
+ if mode is None: return None
+ flags = []
+ for i, c in enumerate((
+ stat.S_IXOTH,
+ stat.S_IWOTH,
+ stat.S_IROTH,
+ stat.S_IXGRP,
+ stat.S_IWGRP,
+ stat.S_IRGRP,
+ stat.S_IXUSR,
+ stat.S_IWUSR,
+ stat.S_IRUSR,
+ stat.S_ISVTX,
+ stat.S_ISGID,
+ stat.S_ISUID)):
+ if c & mode:
+ flags.append(pywbem.Uint8(i))
+ return flags
+
+ @staticmethod
+ def hashfile(afile, hashers, blocksize=65536):
+ """
+ @param hashers is a list of hash objects
+ @return list of digest strings (in hex format) for each hash object
+ given in the same order
+ """
+ if not isinstance(hashers, (tuple, list, set, frozenset)):
+ hashers = (hashers, )
+ buf = afile.read(blocksize)
+ while len(buf) > 0:
+ for h in hashers: h.update(buf)
+ buf = afile.read(blocksize)
+ return [ h.hexdigest() for h in hashers ]
+
+ @staticmethod
+ def compute_checksums(env, checksum_type, file_type, file_path):
+ """
+ @param file_type is not a file, then zeroes are returned
+ @param checksum_type selected hash algorithm to compute second
+ checksum
+ @return (md5sum, checksum)
+ both checksums are computed from file_path's content
+ first one is always md5, the second one depends on checksum_type
+ if file does not exists, (None, None) is returned
+ """
+ hashers = [hashlib.md5()]
+ if checksum_type != YumFileCheck.checksumtype_str2num["md5"]:
+ hashers.append(YumFileCheck.checksumtype_num2hash(checksum_type)())
+ if file_type != YumFileCheck.filetype_str2pywbem('file'):
+ rslts = ['0'*len(h.hexdigest()) for h in hashers]
+ else:
+ try:
+ with open(file_path, 'rb') as f:
+ rslts = YumFileCheck.hashfile(f, hashers)
+ except (OSError, IOError) as e:
+ env.get_logger().log_error("could not open file \"%s\""
+ " for reading: %s" % (file_path, e))
+ return None, None
+ return (rslts[0], rslts[1] if len(rslts) > 1 else rslts[0]*2)
+
+ @staticmethod
+ def object_path2yumcheck(env, op):
+ """
+ @return instance of yum.packages._RPMVerifyPackage
+ this object holds YumInstalledPackage under its po attribute
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be instance of CIMInstanceName, "
+ "not \"%s\"" % op.__class__.__name__)
+ log = env.get_logger()
+
+ tos = get_target_operating_system()[0]
+ if ( not op['Name'] or not op['SoftwareElementID']
+ or not op['CheckID']
+ or not op['CheckID'].endswith('#'+op['Name'])
+ or op['SoftwareElementID'].find(op['Version']) == -1):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.")
+ if op['SoftwareElementState'] not in ("2", 2):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Only \"Executable\" software element state supported")
+ if not check_target_operating_system(op['TargetOperatingSystem']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong target operating system.")
+ if not op['Name'] or not op['Version']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Both "Name" and "Version" must be given')
+ m = re_nevra.match(op['SoftwareElementID'])
+ if not m:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Wrong SotwareElementID. Expected valid nevra"
+ " (name-epoch:version-release.arch).")
+ if op['Version'] != m.group('ver'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Version does not match version part in SoftwareElementID.")
+
+ kwargs = dict((k, m.group(k)) for k in
+ ('name', 'epoch', 'ver', 'rel', 'arch'))
+ with YumDB.getInstance(env) as yb:
+ if not yb.rpmdb.installed(**kwargs):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Package not installed")
+
+ pl = yb.doPackageLists('installed')
+ exact, matched, unmatched = yum.packages.parsePackages(
+ pl.installed, [kwargs['name']])
+ exact = yum.misc.unique(exact)
+ for pkg in exact:
+ if pkg.evra == m.group('evra'): break
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No matching package installed")
+
+ fi = pkg.hdr.fiFromHeader()
+ vpkg = yum.packages._RPMVerifyPackage(
+ pkg, fi, pkg.yumdb_info.checksum_type, [], True)
+ if not op['Name'] in vpkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "File not found in RPM package")
+ return vpkg
+
+ """
+ Named tuple to store results of rpm file check as pywbem values,
+ all results are in form:
+ (expected, reality)
+ where
+ expected is value from rpm package
+ reality is value obtained from installed file
+ None means, that value could not be obtained
+ except for "exists" and "md5_checksum" attributes, where "exists"
+ is boolean and md5_checksum is a string
+ for example:
+ file_check.file_type == (4, 3)
+ """
+ FileCheck = collections.namedtuple('FileCheck',
+ 'exists, md5_checksum, file_type, file_size, file_mode, '
+ 'checksum, device, link_target, user_id, group_id, '
+ 'last_modification_time')
+
+ @staticmethod
+ def test_file(env, checksum_type, vpf):
+ """
+ @param checksum type is a pywbem value for ChecksumType property
+ @return instance of FileCheck
+ """
+ if not isinstance(vpf, yum.packages._RPMVerifyPackageFile):
+ raise TypeError("vpf must be an instance of _RPMVerifyPackage,"
+ " not \"%s\"" % vpf.__class__.__name__)
+ exists = os.path.lexists(vpf.filename)
+ md5_checksum = None
+ expected = {
+ "file_type" : YumFileCheck.filetype_str2pywbem(vpf.ftype),
+ "user_id" : pywbem.Uint32(pwd.getpwnam(vpf.user).pw_uid),
+ "group_id" : pywbem.Uint32(grp.getgrnam(vpf.group).gr_gid),
+ "file_mode" : pywbem.Uint32(vpf.mode),
+ "file_size" : pywbem.Uint64(vpf.size),
+ "link_target" : vpf.readlink if vpf.readlink else None,
+ "checksum" : vpf.digest[1],
+ "device" : (pywbem.Uint64(vpf.dev)
+ if vpf.ftype.endswith('device') else None),
+ "last_modification_time" : pywbem.Uint64(vpf.mtime)
+ }
+ if not exists:
+ reality = collections.defaultdict(lambda: None)
+ else:
+ fstat = os.lstat(vpf.filename)
+ reality = {
+ "file_type" : YumFileCheck.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'] == YumFileCheck.filetype_str2pywbem(
+ "device") else None)
+ reality["link_target"] = (os.readlink(vpf.filename)
+ if os.path.islink(vpf.filename) else None)
+ md5_checksum, checksum = YumFileCheck.compute_checksums(
+ env, checksum_type, reality["file_type"], vpf.filename)
+ reality["checksum"] = checksum
+ kwargs = dict(exists=exists, md5_checksum=md5_checksum,
+ **dict((k, (expected[k], reality[k])) for k in expected))
+ return YumFileCheck.FileCheck(**kwargs)
+
+ @staticmethod
+ def filecheck_passed(fc):
+ if not isinstance(fc, YumFileCheck.FileCheck):
+ raise TypeError("fc must be an instance of FileCheck")
+ return fc.exists and all(v[0] == v[1]
+ for v in fc if isinstance(v, tuple))
+
+ @staticmethod
+ def filecheck_wrapper(namespace, classname):
+
+ def _filecheck2model_flags(fc):
+ flags = []
+ for v in fc:
+ if isinstance(v, tuple):
+ flags.append(fc.exists and v[0] == v[1])
+ elif isinstance(v, bool):
+ flags.append(v)
+ return flags
+
+ def filecheck2model(vpkg, fn, env, keys_only=True, model=None):
+ if not isinstance(vpkg, yum.packages._RPMVerifyPackage):
+ raise TypeError(
+ "vpkg must be an instance of _RPMVerifyPackage")
+ if not fn in vpkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "File \"%s\" not found among package files" % fn)
+ if model is None:
+ model = pywbem.CIMInstanceName(classname, namespace=namespace)
+ if not keys_only:
+ model = pywbem.CIMInstance(classname, path=model)
+ pkg = vpkg.po
+ vpf = vpkg._files[fn]
+ model['Name'] = vpf.filename
+ model['SoftwareElementID'] = pkg.nevra
+ model['SoftwareElementState'] = pywbem.Uint16(2)
+ model['TargetOperatingSystem'] = pywbem.Uint16(
+ get_target_operating_system()[0])
+ model['Version'] = pkg.version
+ model['CheckID'] = '%s#%s' % (pkg.name, vpf.filename)
+ if not keys_only:
+ #model['Caption'] = '' # TODO
+ #model['CheckMode'] = bool() # TODO
+ #model['CRC1'] = pywbem.Uint32() # TODO
+ #model['CRC2'] = pywbem.Uint32() # TODO
+ #model['CreateTimeStamp'] = pywbem.CIMDateTime() # TODO
+ #model['Description'] = '' # TODO
+ #model['ElementName'] = '' # TODO
+ #model['InstanceID'] = '' # TODO
+ model['FileName'] = os.path.basename(vpf.filename)
+ model['ChecksumType'] = csumt = \
+ YumFileCheck.checksumtype_str2pywbem(
+ pkg.yumdb_info.checksum_type)
+ fc = YumFileCheck.test_file(env, csumt, vpf)
+ for mattr, fattr in (
+ ('FileType', 'file_type'),
+ ('FileUserID', 'user_id'),
+ ('FileGroupID', 'group_id'),
+ ('FileMode', 'file_mode'),
+ ('LastModificationTime', 'last_modification_time'),
+ ('FileSize', 'file_size'),
+ ('LinkTarget', 'link_target'),
+ ('Checksum', 'checksum')):
+ exp, rea = getattr(fc, fattr)
+ if exp is not None:
+ model['Expected' + mattr] = exp
+ if rea is not None:
+ model[mattr] = rea
+ model['ExpectedFileModeFlags'] = \
+ YumFileCheck.mode2pywbem_flags(fc.file_mode[0])
+ if fc.exists:
+ model['FileModeFlags'] = YumFileCheck.mode2pywbem_flags(
+ fc.file_mode[1])
+ model['FileExists'] = fc.exists
+ if fc.md5_checksum is not None:
+ model['MD5Checksum'] = fc.md5_checksum
+ model['PassedFlags'] = _filecheck2model_flags(fc)
+ model['PassedFlagsDescriptions'] = list(
+ YumFileCheck.passed_flags_descriptions)
+ return model
+
+ return filecheck2model
+
diff --git a/src/yum/providers/util/singletonmixin.py b/src/yum/providers/util/singletonmixin.py
new file mode 100644
index 0000000..8051695
--- /dev/null
+++ b/src/yum/providers/util/singletonmixin.py
@@ -0,0 +1,508 @@
+"""
+A Python Singleton mixin class that makes use of some of the ideas
+found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
+from it and you have a singleton. No code is required in
+subclasses to create singleton behavior -- inheritance from
+Singleton is all that is needed.
+
+Singleton creation is threadsafe.
+
+USAGE:
+
+Just inherit from Singleton. If you need a constructor, include
+an __init__() method in your class as you usually would. However,
+if your class is S, you instantiate the singleton using S.getInstance()
+instead of S(). Repeated calls to S.getInstance() return the
+originally-created instance.
+
+For example:
+
+class S(Singleton):
+
+ def __init__(self, a, b=1):
+ pass
+
+S1 = S.getInstance(1, b=3)
+
+
+Most of the time, that's all you need to know. However, there are some
+other useful behaviors. Read on for a full description:
+
+1) Getting the singleton:
+
+ S.getInstance()
+
+returns the instance of S. If none exists, it is created.
+
+2) The usual idiom to construct an instance by calling the class, i.e.
+
+ S()
+
+is disabled for the sake of clarity.
+
+For one thing, the S() syntax means instantiation, but getInstance()
+usually does not cause instantiation. So the S() syntax would
+be misleading.
+
+Because of that, if S() were allowed, a programmer who didn't
+happen to notice the inheritance from Singleton (or who
+wasn't fully aware of what a Singleton pattern
+does) might think he was creating a new instance,
+which could lead to very unexpected behavior.
+
+So, overall, it is felt that it is better to make things clearer
+by requiring the call of a class method that is defined in
+Singleton. An attempt to instantiate via S() will result
+in a SingletonException being raised.
+
+3) Use __S.__init__() for instantiation processing,
+since S.getInstance() runs S.__init__(), passing it the args it has received.
+
+If no data needs to be passed in at instantiation time, you don't need S.__init__().
+
+4) If S.__init__(.) requires parameters, include them ONLY in the
+first call to S.getInstance(). If subsequent calls have arguments,
+a SingletonException is raised by default.
+
+If you find it more convenient for subsequent calls to be allowed to
+have arguments, but for those argumentsto be ignored, just include
+'ignoreSubsequent = True' in your class definition, i.e.:
+
+ class S(Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, a, b=1):
+ pass
+
+5) For testing, it is sometimes convenient for all existing singleton
+instances to be forgotten, so that new instantiations can occur. For that
+reason, a forgetAllSingletons() function is included. Just call
+
+ forgetAllSingletons()
+
+and it is as if no earlier instantiations have occurred.
+
+6) As an implementation detail, classes that inherit
+from Singleton may not have their own __new__
+methods. To make sure this requirement is followed,
+an exception is raised if a Singleton subclass includ
+es __new__. This happens at subclass instantiation
+time (by means of the MetaSingleton metaclass.
+
+
+By Gary Robinson, grobinson@flyfi.com. No rights reserved --
+placed in the public domain -- which is only reasonable considering
+how much it owes to other people's code and ideas which are in the
+public domain. The idea of using a metaclass came from
+a comment on Gary's blog (see
+http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
+Other improvements came from comments and email from other
+people who saw it online. (See the blog post and comments
+for further credits.)
+
+Not guaranteed to be fit for any particular purpose. Use at your
+own risk.
+"""
+
+import threading
+
+class SingletonException(Exception):
+ pass
+
+_stSingletons = set()
+_lockForSingletons = threading.RLock()
+_lockForSingletonCreation = threading.RLock() # Ensure only one instance of each Singleton
+ # class is created. This is not bound to the
+ # individual Singleton class since we need to
+ # ensure that there is only one mutex for each
+ # Singleton class, which would require having
+ # a lock when setting up the Singleton class,
+ # which is what this is anyway. So, when any
+ # Singleton is created, we lock this lock and
+ # then we don't need to lock it again for that
+ # class.
+
+def _createSingletonInstance(cls, lstArgs, dctKwArgs):
+ _lockForSingletonCreation.acquire()
+ try:
+ if cls._isInstantiated(): # some other thread got here first
+ return
+
+ instance = cls.__new__(cls)
+ try:
+ instance.__init__(*lstArgs, **dctKwArgs)
+ except TypeError, e:
+ if e.message.find('__init__() takes') != -1:
+ raise SingletonException, 'If the singleton requires __init__ args, supply them on first call to getInstance().'
+ else:
+ raise
+ cls.cInstance = instance
+ _addSingleton(cls)
+ finally:
+ _lockForSingletonCreation.release()
+
+def _addSingleton(cls):
+ _lockForSingletons.acquire()
+ try:
+ assert cls not in _stSingletons
+ _stSingletons.add(cls)
+ finally:
+ _lockForSingletons.release()
+
+def _removeSingleton(cls):
+ _lockForSingletons.acquire()
+ try:
+ if cls in _stSingletons:
+ _stSingletons.remove(cls)
+ finally:
+ _lockForSingletons.release()
+
+def forgetAllSingletons():
+ '''This is useful in tests, since it is hard to know which singletons need to be cleared to make a test work.'''
+ _lockForSingletons.acquire()
+ try:
+ for cls in _stSingletons.copy():
+ cls._forgetClassInstanceReferenceForTesting()
+
+ # Might have created some Singletons in the process of tearing down.
+ # Try one more time - there should be a limit to this.
+ iNumSingletons = len(_stSingletons)
+ if len(_stSingletons) > 0:
+ for cls in _stSingletons.copy():
+ cls._forgetClassInstanceReferenceForTesting()
+ iNumSingletons -= 1
+ assert iNumSingletons == len(_stSingletons), 'Added a singleton while destroying ' + str(cls)
+ assert len(_stSingletons) == 0, _stSingletons
+ finally:
+ _lockForSingletons.release()
+
+class MetaSingleton(type):
+ def __new__(metaclass, strName, tupBases, dct):
+ if dct.has_key('__new__'):
+ raise SingletonException, 'Can not override __new__ in a Singleton'
+ return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
+
+ def __call__(cls, *lstArgs, **dictArgs):
+ raise SingletonException, 'Singletons may only be instantiated through getInstance()'
+
+class Singleton(object):
+ __metaclass__ = MetaSingleton
+
+ def getInstance(cls, *lstArgs, **dctKwArgs):
+ """
+ Call this to instantiate an instance or retrieve the existing instance.
+ If the singleton requires args to be instantiated, include them the first
+ time you call getInstance.
+ """
+ if cls._isInstantiated():
+ if (lstArgs or dctKwArgs) and not hasattr(cls, 'ignoreSubsequent'):
+ raise SingletonException, 'Singleton already instantiated, but getInstance() called with args.'
+ else:
+ _createSingletonInstance(cls, lstArgs, dctKwArgs)
+
+ return cls.cInstance
+ getInstance = classmethod(getInstance)
+
+ def _isInstantiated(cls):
+ # Don't use hasattr(cls, 'cInstance'), because that screws things up if there is a singleton that
+ # extends another singleton. hasattr looks in the base class if it doesn't find in subclass.
+ return 'cInstance' in cls.__dict__
+ _isInstantiated = classmethod(_isInstantiated)
+
+ # This can be handy for public use also
+ isInstantiated = _isInstantiated
+
+ def _forgetClassInstanceReferenceForTesting(cls):
+ """
+ This is designed for convenience in testing -- sometimes you
+ want to get rid of a singleton during test code to see what
+ happens when you call getInstance() under a new situation.
+
+ To really delete the object, all external references to it
+ also need to be deleted.
+ """
+ try:
+ if hasattr(cls.cInstance, '_prepareToForgetSingleton'):
+ # tell instance to release anything it might be holding onto.
+ cls.cInstance._prepareToForgetSingleton()
+ del cls.cInstance
+ _removeSingleton(cls)
+ except AttributeError:
+ # run up the chain of base classes until we find the one that has the instance
+ # and then delete it there
+ for baseClass in cls.__bases__:
+ if issubclass(baseClass, Singleton):
+ baseClass._forgetClassInstanceReferenceForTesting()
+ _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
+
+
+if __name__ == '__main__':
+
+ import unittest
+ import time
+
+ class singletonmixin_Public_TestCase(unittest.TestCase):
+ def testReturnsSameObject(self):
+ """
+ Demonstrates normal use -- just call getInstance and it returns a singleton instance
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ a1 = A.getInstance()
+ a2 = A.getInstance()
+ self.assertEquals(id(a1), id(a2))
+
+ def testInstantiateWithMultiArgConstructor(self):
+ """
+ If the singleton needs args to construct, include them in the first
+ call to get instances.
+ """
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ b1 = B.getInstance('arg1 value', 'arg2 value')
+ b2 = B.getInstance()
+ self.assertEquals(b1.arg1, 'arg1 value')
+ self.assertEquals(b1.arg2, 'arg2 value')
+ self.assertEquals(id(b1), id(b2))
+
+ def testInstantiateWithKeywordArg(self):
+
+ class B(Singleton):
+
+ def __init__(self, arg1=5):
+ super(B, self).__init__()
+ self.arg1 = arg1
+
+ b1 = B.getInstance('arg1 value')
+ b2 = B.getInstance()
+ self.assertEquals(b1.arg1, 'arg1 value')
+ self.assertEquals(id(b1), id(b2))
+
+ def testTryToInstantiateWithoutNeededArgs(self):
+
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ self.assertRaises(SingletonException, B.getInstance)
+
+ def testPassTypeErrorIfAllArgsThere(self):
+ """
+ Make sure the test for capturing missing args doesn't interfere with a normal TypeError.
+ """
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+ raise TypeError, 'some type error'
+
+ self.assertRaises(TypeError, B.getInstance, 1, 2)
+
+ def testTryToInstantiateWithoutGetInstance(self):
+ """
+ Demonstrates that singletons can ONLY be instantiated through
+ getInstance, as long as they call Singleton.__init__ during construction.
+
+ If this check is not required, you don't need to call Singleton.__init__().
+ """
+
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ self.assertRaises(SingletonException, A)
+
+ def testDontAllowNew(self):
+
+ def instantiatedAnIllegalClass():
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+
+ def __new__(metaclass, strName, tupBases, dct):
+ return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
+
+ self.assertRaises(SingletonException, instantiatedAnIllegalClass)
+
+
+ def testDontAllowArgsAfterConstruction(self):
+ class B(Singleton):
+
+ def __init__(self, arg1, arg2):
+ super(B, self).__init__()
+ self.arg1 = arg1
+ self.arg2 = arg2
+
+ B.getInstance('arg1 value', 'arg2 value')
+ self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
+
+ def test_forgetClassInstanceReferenceForTesting(self):
+ class A(Singleton):
+ def __init__(self):
+ super(A, self).__init__()
+ class B(A):
+ def __init__(self):
+ super(B, self).__init__()
+
+ # check that changing the class after forgetting the instance produces
+ # an instance of the new class
+ a = A.getInstance()
+ assert a.__class__.__name__ == 'A'
+ A._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ # check that invoking the 'forget' on a subclass still deletes the instance
+ B._forgetClassInstanceReferenceForTesting()
+ a = A.getInstance()
+ B._forgetClassInstanceReferenceForTesting()
+ b = B.getInstance()
+ assert b.__class__.__name__ == 'B'
+
+ def test_forgetAllSingletons(self):
+ # Should work if there are no singletons
+ forgetAllSingletons()
+
+ class A(Singleton):
+ ciInitCount = 0
+ def __init__(self):
+ super(A, self).__init__()
+ A.ciInitCount += 1
+
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 1)
+
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 1)
+
+ forgetAllSingletons()
+ A.getInstance()
+ self.assertEqual(A.ciInitCount, 2)
+
+ def test_threadedCreation(self):
+ # Check that only one Singleton is created even if multiple
+ # threads try at the same time. If fails, would see assert in _addSingleton
+ class Test_Singleton(Singleton):
+ def __init__(self):
+ super(Test_Singleton, self).__init__()
+
+ class Test_SingletonThread(threading.Thread):
+ def __init__(self, fTargetTime):
+ super(Test_SingletonThread, self).__init__()
+ self._fTargetTime = fTargetTime
+ self._eException = None
+
+ def run(self):
+ try:
+ fSleepTime = self._fTargetTime - time.time()
+ if fSleepTime > 0:
+ time.sleep(fSleepTime)
+ Test_Singleton.getInstance()
+ except Exception, e:
+ self._eException = e
+
+ fTargetTime = time.time() + 0.1
+ lstThreads = []
+ for _ in xrange(100):
+ t = Test_SingletonThread(fTargetTime)
+ t.start()
+ lstThreads.append(t)
+ eException = None
+ for t in lstThreads:
+ t.join()
+ if t._eException and not eException:
+ eException = t._eException
+ if eException:
+ raise eException
+
+ def testNoInit(self):
+ """
+ Demonstrates use with a class not defining __init__
+ """
+
+ class A(Singleton):
+ pass
+
+ #INTENTIONALLY UNDEFINED:
+ #def __init__(self):
+ # super(A, self).__init__()
+
+ A.getInstance() #Make sure no exception is raised
+
+ def testMultipleGetInstancesWithArgs(self):
+
+ class A(Singleton):
+
+ ignoreSubsequent = True
+
+ def __init__(self, a, b=1):
+ pass
+
+ a1 = A.getInstance(1)
+ a2 = A.getInstance(2) # ignores the second call because of ignoreSubsequent
+
+ class B(Singleton):
+
+ def __init__(self, a, b=1):
+ pass
+
+ b1 = B.getInstance(1)
+ self.assertRaises(SingletonException, B.getInstance, 2) # No ignoreSubsequent included
+
+ class C(Singleton):
+
+ def __init__(self, a=1):
+ pass
+
+ c1 = C.getInstance(a=1)
+ self.assertRaises(SingletonException, C.getInstance, a=2) # No ignoreSubsequent included
+
+ def testInheritance(self):
+ """
+ It's sometimes said that you can't subclass a singleton (see, for instance,
+ http://steve.yegge.googlepages.com/singleton-considered-stupid point e). This
+ test shows that at least rudimentary subclassing works fine for us.
+ """
+
+ class A(Singleton):
+
+ def setX(self, x):
+ self.x = x
+
+ def setZ(self, z):
+ raise NotImplementedError
+
+ class B(A):
+
+ def setX(self, x):
+ self.x = -x
+
+ def setY(self, y):
+ self.y = y
+
+ a = A.getInstance()
+ a.setX(5)
+ b = B.getInstance()
+ b.setX(5)
+ b.setY(50)
+ self.assertEqual((a.x, b.x, b.y), (5, -5, 50))
+ self.assertRaises(AttributeError, eval, 'a.setY', {}, locals())
+ self.assertRaises(NotImplementedError, b.setZ, 500)
+
+ unittest.main()
+