summaryrefslogtreecommitdiffstats
path: root/src/software/lmi
diff options
context:
space:
mode:
Diffstat (limited to 'src/software/lmi')
-rw-r--r--src/software/lmi/__init__.py21
-rw-r--r--src/software/lmi/software/LMI_AffectedSoftwareJobElement.py250
-rw-r--r--src/software/lmi/software/LMI_AssociatedSoftwareInstallationServiceCapabilities.py231
-rw-r--r--src/software/lmi/software/LMI_AssociatedSoftwareJobMethodResult.py248
-rw-r--r--src/software/lmi/software/LMI_HostedSoftwareCollection.py225
-rw-r--r--src/software/lmi/software/LMI_HostedSoftwareIdentityResource.py280
-rw-r--r--src/software/lmi/software/LMI_HostedSoftwareInstallationService.py233
-rw-r--r--src/software/lmi/software/LMI_InstalledSoftwareIdentity.py305
-rw-r--r--src/software/lmi/software/LMI_MemberOfSoftwareCollection.py280
-rw-r--r--src/software/lmi/software/LMI_OwningSoftwareJobElement.py223
-rw-r--r--src/software/lmi/software/LMI_ResourceForSoftwareIdentity.py317
-rw-r--r--src/software/lmi/software/LMI_SoftwareIdentity.py188
-rw-r--r--src/software/lmi/software/LMI_SoftwareIdentityChecks.py256
-rw-r--r--src/software/lmi/software/LMI_SoftwareIdentityFileCheck.py244
-rw-r--r--src/software/lmi/software/LMI_SoftwareIdentityResource.py270
-rw-r--r--src/software/lmi/software/LMI_SoftwareInstallationService.py862
-rw-r--r--src/software/lmi/software/LMI_SoftwareInstallationServiceAffectsElement.py299
-rw-r--r--src/software/lmi/software/LMI_SoftwareInstallationServiceCapabilities.py292
-rw-r--r--src/software/lmi/software/LMI_SoftwareJob.py385
-rw-r--r--src/software/lmi/software/LMI_SoftwareMethodResult.py163
-rw-r--r--src/software/lmi/software/LMI_SystemSoftwareCollection.py173
-rw-r--r--src/software/lmi/software/__init__.py25
-rw-r--r--src/software/lmi/software/cimom_entry.py212
-rw-r--r--src/software/lmi/software/core/AffectedSoftwareJobElement.py264
-rw-r--r--src/software/lmi/software/core/AssociatedInstallationServiceCapabilities.py35
-rw-r--r--src/software/lmi/software/core/ComputerSystem.py87
-rw-r--r--src/software/lmi/software/core/Error.py457
-rw-r--r--src/software/lmi/software/core/Identity.py270
-rw-r--r--src/software/lmi/software/core/IdentityFileCheck.py965
-rw-r--r--src/software/lmi/software/core/IdentityResource.py650
-rw-r--r--src/software/lmi/software/core/InstMethodCall.py144
-rw-r--r--src/software/lmi/software/core/InstallationService.py826
-rw-r--r--src/software/lmi/software/core/InstallationServiceAffectsElement.py87
-rw-r--r--src/software/lmi/software/core/InstallationServiceCapabilities.py226
-rw-r--r--src/software/lmi/software/core/Job.py754
-rw-r--r--src/software/lmi/software/core/MethodResult.py88
-rw-r--r--src/software/lmi/software/core/SystemCollection.py73
-rw-r--r--src/software/lmi/software/core/__init__.py84
-rw-r--r--src/software/lmi/software/util/__init__.py170
-rw-r--r--src/software/lmi/software/yumdb/__init__.py677
-rw-r--r--src/software/lmi/software/yumdb/errors.py111
-rw-r--r--src/software/lmi/software/yumdb/jobmanager.py574
-rw-r--r--src/software/lmi/software/yumdb/jobs.py668
-rw-r--r--src/software/lmi/software/yumdb/packagecheck.py235
-rw-r--r--src/software/lmi/software/yumdb/packageinfo.py181
-rw-r--r--src/software/lmi/software/yumdb/process.py914
-rw-r--r--src/software/lmi/software/yumdb/repository.py202
-rw-r--r--src/software/lmi/software/yumdb/util.py163
48 files changed, 14887 insertions, 0 deletions
diff --git a/src/software/lmi/__init__.py b/src/software/lmi/__init__.py
new file mode 100644
index 0000000..aa4170a
--- /dev/null
+++ b/src/software/lmi/__init__.py
@@ -0,0 +1,21 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/src/software/lmi/software/LMI_AffectedSoftwareJobElement.py b/src/software/lmi/software/LMI_AffectedSoftwareJobElement.py
new file mode 100644
index 0000000..12e6a35
--- /dev/null
+++ b/src/software/lmi/software/LMI_AffectedSoftwareJobElement.py
@@ -0,0 +1,250 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_AffectedSoftwareJobElement
+
+Instruments the CIM class LMI_AffectedSoftwareJobElement
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import AffectedSoftwareJobElement
+from lmi.software.core import Job
+from lmi.software.yumdb import YumDB
+
+class LMI_AffectedSoftwareJobElement(CIMProvider2):
+ """Instrument the CIM class LMI_AffectedSoftwareJobElement
+
+ AffectedJobElement represents an association between a Job and the
+ ManagedElement(s) that may be affected by its execution. It may not be
+ feasible for the Job to describe all of the affected elements. The
+ main purpose of this association is to provide information when a Job
+ requires exclusive use of the 'affected' ManagedElment(s) or when
+ describing that side effects may result.
+ """
+
+ def __init__(self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = AffectedSoftwareJobElement.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ (job, affected) = AffectedSoftwareJobElement.check_path(env, model.path)
+ model['AffectingElement'] = Job.job2model(job)
+
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(affected.namespace,
+ sub=affected.classname, super='LMI_SoftwareIdentity'):
+ _, effects, descriptions = AffectedSoftwareJobElement. \
+ job2affected_software_identity(job)
+ model["AffectedElement"] = affected
+ model["ElementEffects"] = effects
+ model["OtherElementEffectsDescriptions"] = descriptions
+
+ elif ch.is_subclass(affected.namespace,
+ sub=affected.classname, super='Linux_ComputerSystem'):
+ AffectedSoftwareJobElement.fill_model_computer_system(
+ model, job, keys_only=False)
+
+ elif ch.is_subclass(affected.namespace,
+ sub=affected.classname, super='LMI_SystemSoftwareCollection'):
+ AffectedSoftwareJobElement.fill_model_system_collection(
+ model, keys_only=False)
+
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SoftwareIdentityFileCheck'):
+ AffectedSoftwareJobElement.fill_model_failed_check(
+ model, affected)
+
+ else:
+ cmpi_logging.logger.error("Unhandled classname: %s",
+ affected.classname)
+
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'AffectedElement': None, 'AffectingElement': None})
+ for job in YumDB.get_instance().get_job_list():
+ for mdl in AffectedSoftwareJobElement.generate_models_from_job(
+ job, keys_only=keys_only, model=model):
+ yield mdl
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_ManagedElement') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_Job'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/lmi/software/LMI_AssociatedSoftwareInstallationServiceCapabilities.py b/src/software/lmi/software/LMI_AssociatedSoftwareInstallationServiceCapabilities.py
new file mode 100644
index 0000000..15b8df9
--- /dev/null
+++ b/src/software/lmi/software/LMI_AssociatedSoftwareInstallationServiceCapabilities.py
@@ -0,0 +1,231 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_AssociatedSoftwareInstallationServiceCapabilities
+
+Instruments the CIM class LMI_AssociatedSoftwareInstallationServiceCapabilities
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import InstallationService
+from lmi.software.core import InstallationServiceCapabilities
+from lmi.software.core import AssociatedInstallationServiceCapabilities
+
+class LMI_AssociatedSoftwareInstallationServiceCapabilities(CIMProvider2):
+ """Instrument the CIM class \
+ LMI_AssociatedSoftwareInstallationServiceCapabilities
+
+ ElementCapabilities represents the association between ManagedElements
+ and their Capabilities. Note that the cardinality of the
+ ManagedElement reference is Min(1). This cardinality mandates the
+ instantiation of the ElementCapabilities association for the
+ referenced instance of Capabilities. ElementCapabilities describes the
+ existence requirements and context for the referenced instance of
+ ManagedElement. Specifically, the ManagedElement MUST exist and
+ provides the context for the Capabilities.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = AssociatedInstallationServiceCapabilities.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ InstallationService.check_path_property(env, model, "ManagedElement")
+ InstallationServiceCapabilities.check_path_property(
+ env, model, "Capabilities")
+ model['Characteristics'] = [
+ self.values.Characteristics.Default,
+ self.values.Characteristics.Current]
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'Capabilities': None, 'ManagedElement': None})
+
+ model['Capabilities'] = InstallationServiceCapabilities.get_path()
+ model['ManagedElement'] = InstallationService.get_path()
+ if not keys_only:
+ model['Characteristics'] = [
+ self.values.Characteristics.Default,
+ self.values.Characteristics.Current]
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationServiceCapabilities') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationService'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/lmi/software/LMI_AssociatedSoftwareJobMethodResult.py b/src/software/lmi/software/LMI_AssociatedSoftwareJobMethodResult.py
new file mode 100644
index 0000000..fa2bad7
--- /dev/null
+++ b/src/software/lmi/software/LMI_AssociatedSoftwareJobMethodResult.py
@@ -0,0 +1,248 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_AssociatedSoftwareJobMethodResult
+
+Instruments the CIM class LMI_AssociatedSoftwareJobMethodResult
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import Job
+from lmi.software.core import MethodResult
+from lmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_job_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ job = Job.object_path2job(object_name)
+
+ model["Job"] = Job.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_result_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ job = MethodResult.object_path2job(object_name)
+
+ model["Job"] = Job.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ yield model
+
+class LMI_AssociatedSoftwareJobMethodResult(CIMProvider2):
+ """Instrument the CIM class LMI_AssociatedSoftwareJobMethodResult
+
+ AssociatedJobMethodResult represents an association between a
+ ConcreteJob and the MethodResult expressing the parameters for the Job
+ when the job was created by side-effect of the execution of an
+ extrinsic method.
+
+ """
+
+ 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.
+
+ 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)
+ """
+ job = Job.object_path2job(model['Job'])
+ jobid = MethodResult.object_path2jobid(model['JobParameters'])
+ if job.jobid != jobid:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Job id of Job(%d) does not match JobParameters(%d)." %
+ (job.jobid, jobid))
+ model["Job"] = Job.job2model(job)
+ model["JobParameters"] = MethodResult.job2model(job)
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'Job': None, 'JobParameters': None})
+
+ for job in YumDB.get_instance().get_job_list():
+ model['Job'] = Job.job2model(job)
+ model['JobParameters'] = MethodResult.job2model(job)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ handlers = [
+ ("Job", "LMI_SoftwareJob", generate_job_referents),
+ ("JobParameters", "LMI_SoftwareMethodResult",
+ generate_result_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_HostedSoftwareCollection.py b/src/software/lmi/software/LMI_HostedSoftwareCollection.py
new file mode 100644
index 0000000..7249bd6
--- /dev/null
+++ b/src/software/lmi/software/LMI_HostedSoftwareCollection.py
@@ -0,0 +1,225 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_HostedSoftwareCollection
+
+Instruments the CIM class LMI_HostedSoftwareCollection
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem, SystemCollection
+
+class LMI_HostedSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_HostedSoftwareCollection
+
+ HostedSoftwareCollection defines a SystemSpecificCollection in the context of a
+ scoping System. It represents a Collection that has meaning only in
+ the context of a System, a Collection whose elements are restricted by
+ the definition of the System, or both of these types of Collections.
+
+ """
+
+ 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.
+
+ 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)
+
+ """
+ ComputerSystem.check_path_property(env, model, "Antecedent")
+ SystemCollection.check_path_property(env, model, "Dependent")
+
+ model.path.update({"Antecedent":None, "Dependent":None})
+
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = SystemCollection.get_path()
+
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'Dependent': None, 'Antecedent': None})
+
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = SystemCollection.get_path()
+
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_SystemSpecificCollection') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='CIM_System'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
+
diff --git a/src/software/lmi/software/LMI_HostedSoftwareIdentityResource.py b/src/software/lmi/software/LMI_HostedSoftwareIdentityResource.py
new file mode 100644
index 0000000..767523a
--- /dev/null
+++ b/src/software/lmi/software/LMI_HostedSoftwareIdentityResource.py
@@ -0,0 +1,280 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_HostedSoftwareIdentityResource
+
+Instruments the CIM class LMI_HostedSoftwareIdentityResource
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import ComputerSystem
+from lmi.software.core import IdentityResource
+from lmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_system_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ ComputerSystem.check_path(env, object_name, "Antecedent")
+ repomodel = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityResource',
+ namespace='root/cimv2',
+ host=model.path.host)
+ model["Antecedent"] = ComputerSystem.get_path()
+ for repo in YumDB.get_instance().get_repository_list('all'):
+ model["Dependent"] = IdentityResource.repo2model(repo, model=repomodel)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_repository_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ repo = IdentityResource.object_path2repo(
+ env, object_name, kind='all')
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = IdentityResource.repo2model(repo)
+ yield model
+
+class LMI_HostedSoftwareIdentityResource(CIMProvider2):
+ """Instrument the CIM class LMI_HostedSoftwareIdentityResource
+
+ CIM_HostedAccessPoint is an association between a Service AccessPoint
+ and the System on which it is provided. The cardinality of this
+ association is one-to-many and is weak with respect to the System.
+ Each System can host many ServiceAccessPoints. Heuristic: If the
+ implementation of the ServiceAccessPoint is modeled, it must be
+ implemented by a Device or SoftwareFeature that is part of the System
+ that is hosting the ServiceAccessPoint.
+
+ """
+
+ 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.
+
+ 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)
+
+ """
+ ComputerSystem.check_path_property(env, model, "Antecedent")
+ if not "Name" in model['Dependent']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing key property "Name" in Dependent!')
+
+ with YumDB.get_instance() as ydb:
+ repoid = model["Dependent"]["Name"]
+ repos = ydb.filter_repositories('all', repoid=repoid)
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Unknown repository "%s".' % repoid)
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = IdentityResource.repo2model(repos[0])
+
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+
+ # 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({'Dependent': None, 'Antecedent': None})
+
+ repolist = YumDB.get_instance().get_repository_list('all')
+ model["Antecedent"] = ComputerSystem.get_path()
+ repomodel = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityResource',
+ namespace='root/cimv2')
+ for repo in repolist:
+ model["Dependent"] = IdentityResource.repo2model(
+ repo, model=repomodel)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+
+ """
+
+ handlers = [
+ ("Antecedent", "CIM_ComputerSystem" , generate_system_referents),
+ ("Dependent", "LMI_SoftwareIdentityResource",
+ generate_repository_referents)
+ ]
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_HostedSoftwareInstallationService.py b/src/software/lmi/software/LMI_HostedSoftwareInstallationService.py
new file mode 100644
index 0000000..5c650cb
--- /dev/null
+++ b/src/software/lmi/software/LMI_HostedSoftwareInstallationService.py
@@ -0,0 +1,233 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_HostedSoftwareInstallationService
+
+Instruments the CIM class LMI_HostedSoftwareInstallationService
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem, InstallationService
+
+class LMI_HostedSoftwareInstallationService(CIMProvider2):
+ """Instrument the CIM class LMI_HostedSoftwareInstallationService
+
+ CIM_HostedService is an association between a Service and the System on
+ which the functionality is located. The cardinality of this
+ association is one-to-many. A System can host many Services. Services
+ are weak with respect to their hosting System. Heuristic: A Service is
+ hosted on the System where the LogicalDevices or SoftwareFeatures that
+ implement the Service are located. The model does not represent
+ Services hosted across multiple systems. The model is as an
+ ApplicationSystem that acts as an aggregation point for Services that
+ are each located on a single host.
+
+ """
+
+ 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.
+
+ 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)
+ """
+ InstallationService.check_path_property(env, model, 'Dependent')
+ ComputerSystem.check_path_property(env, model, 'Antecedent')
+
+ model.path.update({"Antecedent":None, "Dependent":None})
+
+ model["Dependent"] = InstallationService.get_path()
+ model["Antecedent"] = ComputerSystem.get_path()
+
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'Dependent': None, 'Antecedent': None})
+
+ model["Antecedent"] = ComputerSystem.get_path()
+ model["Dependent"] = InstallationService.get_path()
+
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationService') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='Linux_ComputerSystem'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/lmi/software/LMI_InstalledSoftwareIdentity.py b/src/software/lmi/software/LMI_InstalledSoftwareIdentity.py
new file mode 100644
index 0000000..429a7a2
--- /dev/null
+++ b/src/software/lmi/software/LMI_InstalledSoftwareIdentity.py
@@ -0,0 +1,305 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_InstalledSoftwareIdentity
+
+Instruments the CIM class LMI_InstalledSoftwareIdentity
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import ComputerSystem
+from lmi.software.core import Identity
+from lmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_system_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ ComputerSystem.check_path(env, object_name, "object_name")
+
+ pkg_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentity',
+ namespace="root/cimv2",
+ host=model.path.host)
+ model["System"] = ComputerSystem.get_path()
+ with YumDB.get_instance() as ydb:
+ for pkg_info in ydb.get_package_list('installed', sort=True):
+ model["InstalledSoftware"] = Identity.pkg2model(
+ pkg_info, model=pkg_model)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_package_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ pkg_info = Identity.object_path2pkg(object_name, kind="installed")
+ model['InstalledSoftware'] = Identity.pkg2model(pkg_info)
+ model["System"] = ComputerSystem.get_path()
+ yield model
+
+class LMI_InstalledSoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_InstalledSoftwareIdentity
+
+ The InstalledSoftwareIdentity association identifies the System on
+ which a SoftwareIdentity is installed. This class is a corollary to
+ InstalledSoftwareElement, but deals with the asset aspects of software
+ (as indicated by SoftwareIdentity), versus the deployment aspects (as
+ indicated by SoftwareElement).
+
+ """
+
+ 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.
+
+ 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)
+
+ """
+ ComputerSystem.check_path_property(env, model, 'System')
+
+ if not isinstance(model['InstalledSoftware'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for InstalledSoftware!")
+
+ model["System"] = model.path["System"] = ComputerSystem.get_path()
+ pkg_info = Identity.object_path2pkg(
+ model['InstalledSoftware'], kind='installed')
+ model['InstalledSoftware'] = Identity.pkg2model(pkg_info)
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'InstalledSoftware': None, 'System': None})
+
+ model['System'] = ComputerSystem.get_path()
+ inst_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ with YumDB.get_instance() as yb:
+ pl = yb.get_package_list('installed', sort=True)
+ for pkg in pl:
+ model['InstalledSoftware'] = Identity.pkg2model(
+ pkg, model=inst_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+
+ if modify_existing:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
+ "Installed package can not be modified.")
+
+ if not "InstalledSoftware" in instance:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing InstalledSoftware property.")
+ if not "System" in instance:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing System property.")
+
+ ComputerSystem.check_path_property(env, instance, 'System')
+ with YumDB.get_instance() as ydb:
+ pkg_info = Identity.object_path2pkg(
+ instance["InstalledSoftware"], kind="all")
+ if pkg_info.installed:
+ raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS,
+ 'Package "%s" is already installed.' % pkg_info)
+ cmpi_logging.logger.info('installing package "%s"' % pkg_info)
+ installed = ydb.install_package(pkg_info)
+ cmpi_logging.logger.info('package "%s" installed' % pkg_info)
+ instance["InstalledSoftware"] = Identity.pkg2model(installed)
+ return instance
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ if not "InstalledSoftware" in instance_name:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing InstalledSoftware property.")
+ if not "System" in instance_name:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing System property.")
+ ComputerSystem.check_path_property(env, instance_name, 'System')
+
+ with YumDB.get_instance() as ydb:
+ pkg_info = Identity.object_path2pkg(
+ instance_name["InstalledSoftware"], 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.
+
+ 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)
+
+ """
+ handlers = [
+ ("System", "CIM_ComputerSystem", generate_system_referents),
+ ("InstalledSoftware", "LMI_SoftwareIdentity",
+ generate_package_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_MemberOfSoftwareCollection.py b/src/software/lmi/software/LMI_MemberOfSoftwareCollection.py
new file mode 100644
index 0000000..f66f788
--- /dev/null
+++ b/src/software/lmi/software/LMI_MemberOfSoftwareCollection.py
@@ -0,0 +1,280 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_MemberOfSoftwareCollection
+
+Instruments the CIM class LMI_MemberOfSoftwareCollection
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import SystemCollection
+from lmi.software.core import Identity
+from lmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_collection_referents(env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ SystemCollection.check_path(env, object_name, "collection")
+ pkg_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentity',
+ namespace="root/cimv2",
+ host=model.path.host)
+ model["Collection"] = SystemCollection.get_path()
+ with YumDB.get_instance() as ydb:
+ for pkg_info in ydb.get_package_list('available',
+ allow_duplicates=True, sort=True):
+ model["Member"] = Identity.pkg2model(pkg_info, model=pkg_model)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_member_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request.
+ """
+ try:
+ pkg_info = Identity.object_path2pkg(object_name, kind="available")
+ model['Member'] = Identity.pkg2model(pkg_info)
+ model["Collection"] = SystemCollection.get_path()
+ except pywbem.CIMError as exc:
+ if exc.args[0] == pywbem.CIM_ERR_NOT_FOUND:
+ msg = "Could not find requested package%s."
+ if "InstanceID" in object_name:
+ msg = msg % (' with InstanceID="%s"'%object_name["InstanceID"])
+ else:
+ msg = msg % ""
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, msg)
+ else:
+ raise
+ yield model
+
+class LMI_MemberOfSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_MemberOfSoftwareCollection
+
+ LMI_MemberOfSoftwareCollection is an aggregation used to establish
+ membership of ManagedElements in a Collection.
+ """
+
+ 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.
+
+ 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)
+
+ """
+ SystemCollection.check_path_property(env, model, "Collection")
+
+ if not 'Member' in model:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing "Member" key property!')
+ if not isinstance(model['Member'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for Member!")
+
+ model['Collection'] = SystemCollection.get_path()
+ with YumDB.get_instance():
+ pkg_info = Identity.object_path2pkg(model['Member'], 'available')
+ model['Member'] = Identity.pkg2model(pkg_info)
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ # 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({'Member': None, 'Collection': None})
+
+ model['Collection'] = SystemCollection.get_path()
+ member_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ with YumDB.get_instance() as yb:
+ pl = yb.get_package_list('available',
+ allow_duplicates=True,
+ sort=True)
+ for pkg in pl:
+ model['Member'] = Identity.pkg2model(pkg, model=member_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+
+ """
+ handlers = [
+ ("Collection", "LMI_SystemSoftwareCollection",
+ generate_collection_referents),
+ ("Member", "LMI_SoftwareIdentity", generate_member_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_OwningSoftwareJobElement.py b/src/software/lmi/software/LMI_OwningSoftwareJobElement.py
new file mode 100644
index 0000000..5f8b3a8
--- /dev/null
+++ b/src/software/lmi/software/LMI_OwningSoftwareJobElement.py
@@ -0,0 +1,223 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_OwningSoftwareJobElement
+
+Instruments the CIM class LMI_OwningSoftwareJobElement
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import InstallationService
+from lmi.software.core import Job
+from lmi.software.yumdb import YumDB
+
+class LMI_OwningSoftwareJobElement(CIMProvider2):
+ """Instrument the CIM class LMI_OwningSoftwareJobElement
+
+ OwningJobElement represents an association between a Job and the
+ ManagedElement responsible for the creation of the Job. This
+ association may not be possible, given that the execution of jobs can
+ move between systems and that the lifecycle of the creating entity may
+ not persist for the total duration of the job. However, this can be
+ very useful information when available. This association defines a
+ more specific 'owner' than is provided by the CIM_Job.Owner string.
+ """
+
+ 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.
+
+ 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)
+ """
+ InstallationService.check_path_property(env, model, "OwningElement")
+ model['OwningElement'] = InstallationService.get_path()
+ job = Job.object_path2job(model['OwnedElement'])
+ model['OwnedElement'] = Job.job2model(job)
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'OwningElement': None, 'OwnedElement': None})
+ model['OwningElement'] = InstallationService.get_path()
+ for job in YumDB.get_instance().get_job_list():
+ model['OwnedElement'] = Job.job2model(job)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+
+ if ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareInstallationService') or \
+ ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super='LMI_SoftwareJob'):
+ return self.simple_refs(env, object_name, model,
+ result_class_name, role, result_role, keys_only)
diff --git a/src/software/lmi/software/LMI_ResourceForSoftwareIdentity.py b/src/software/lmi/software/LMI_ResourceForSoftwareIdentity.py
new file mode 100644
index 0000000..ca22337
--- /dev/null
+++ b/src/software/lmi/software/LMI_ResourceForSoftwareIdentity.py
@@ -0,0 +1,317 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_ResourceForSoftwareIdentity
+
+Instruments the CIM class LMI_ResourceForSoftwareIdentity
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import Identity
+from lmi.software.core import IdentityResource
+from lmi.software.yumdb import YumDB
+
+@cmpi_logging.trace_function
+def generate_package_referents(_env, object_name, model, _keys_only):
+ """
+ Generates models of repositories holding package represented
+ by object_name.
+ """
+ with YumDB.get_instance() as ydb:
+ pkg_infos = Identity.object_path2pkg(
+ object_name, 'available', return_all=True)
+ for pkg_info in pkg_infos:
+ repos = ydb.filter_repositories('enabled', repoid=pkg_info.repoid)
+ model['ManagedElement'] = Identity.pkg2model(pkg_info)
+ for repo in repos:
+ model["AvailableSAP"] = \
+ IdentityResource.repo2model(repo)
+ yield model
+
+@cmpi_logging.trace_function
+def generate_repository_referents(env, object_name, model, _keys_only):
+ """
+ Generates models of software identities contained in repository
+ represented by object_name.
+ """
+ with YumDB.get_instance() as ydb:
+ repo = IdentityResource.object_path2repo(
+ env, object_name, 'all')
+ pkglist = ydb.get_package_list('available',
+ allow_duplicates=True, sort=True,
+ include_repos=repo.repoid,
+ exclude_repos='*')
+ model['AvailableSAP'] = IdentityResource.repo2model(repo)
+ pkg_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ for pkg_info in pkglist:
+ model["ManagedElement"] = Identity.pkg2model(
+ pkg_info, model=pkg_model)
+ yield model
+
+class LMI_ResourceForSoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_ResourceForSoftwareIdentity
+
+ CIM_SAPAvailableForElement conveys the semantics of a Service Access
+ Point that is available for a ManagedElement. When
+ CIM_SAPAvailableForElement is not instantiated, then the SAP is
+ assumed to be generally available. If instantiated, the SAP is
+ available only for the associated ManagedElements. For example, a
+ device might provide management access through a URL. This association
+ allows the URL to be advertised for the device.
+
+ """
+
+ 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.
+
+ 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)
+ """
+ if not isinstance(model['ManagedElement'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for ManagedElement!")
+ if not isinstance(model['AvailableSAP'], pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected object path for AvailableSAP!")
+ if not "Name" in model['AvailableSAP']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Missing key property "Name" in AvailableSAP!')
+
+ with YumDB.get_instance() as ydb:
+ repoid = model["AvailableSAP"]["Name"]
+ pkg_info = Identity.object_path2pkg(
+ model["ManagedElement"],
+ kind='available',
+ include_repos=repoid,
+ exclude_repos='*',
+ repoid=repoid)
+ repos = ydb.filter_repositories('all', repoid=repoid)
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Unknown repository "%s".' % repoid)
+ repo = repos[0]
+ repo = IdentityResource.object_path2repo(
+ env, model["AvailableSAP"], 'all')
+
+ model["AvailableSAP"] = IdentityResource.repo2model(repo)
+ model["ManagedElement"] = Identity.pkg2model(pkg_info)
+
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # 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({'ManagedElement': None, 'AvailableSAP': None})
+
+ elem_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ sap_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentityResource",
+ namespace="root/cimv2")
+ # maps repoid to instance name
+ with YumDB.get_instance() as ydb:
+ pl = ydb.get_package_list('available',
+ allow_duplicates=True,
+ sort=True)
+ repos = dict( (r.repoid, r)
+ for r in ydb.get_repository_list('enabled'))
+ for pkg in pl:
+ try:
+ repo = repos[pkg.repoid]
+ except KeyError:
+ cmpi_logging.logger.error('unknown or disabled repository'
+ ' "%s" for package "%s"', pkg, pkg.repoid)
+ model["AvailableSAP"] = IdentityResource.repo2model(
+ repo, model=sap_model)
+ model["ManagedElement"] = Identity.pkg2model(
+ pkg, model=elem_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+
+ """
+ handlers = [
+ ("AvailableSAP", "LMI_SoftwareIdentityResource",
+ generate_repository_referents),
+ ("ManagedElement", "LMI_SoftwareIdentity",
+ generate_package_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_SoftwareIdentity.py b/src/software/lmi/software/LMI_SoftwareIdentity.py
new file mode 100644
index 0000000..722f86c
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareIdentity.py
@@ -0,0 +1,188 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareIdentity
+
+Instruments the CIM class LMI_SoftwareIdentity
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import Identity
+from lmi.software.yumdb import YumDB
+
+class LMI_SoftwareIdentity(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentity
+
+ SoftwareIdentity provides descriptive information about a software
+ component for asset tracking and/or installation dependency
+ management. When the IsEntity property has the value TRUE, the
+ instance of SoftwareIdentity represents an individually identifiable
+ entity similar to Physical Element. SoftwareIdentity does NOT indicate
+ whether the software is installed, executing, etc. This extra
+ information may be provided through specialized associations to
+ Software Identity. For instance, both InstalledSoftwareIdentity and
+ ElementSoftwareIdentity may be used to indicate that the software
+ identified by this class is installed. SoftwareIdentity is used when
+ managing the software components of a ManagedElement that is the
+ management focus. Since software may be acquired, SoftwareIdentity can
+ be associated with a Product using the ProductSoftwareComponent
+ relationship. The Application Model manages the deployment and
+ installation of software via the classes, SoftwareFeatures and
+ SoftwareElements. SoftwareFeature and SoftwareElement are used when
+ the software component is the management focus. The
+ deployment/installation concepts are related to the asset/identity
+ one. In fact, a SoftwareIdentity may correspond to a Product, or to
+ one or more SoftwareFeatures or SoftwareElements - depending on the
+ granularity of these classes and the deployment model. The
+ correspondence of Software Identity to Product, SoftwareFeature or
+ SoftwareElement is indicated using the ConcreteIdentity association.
+ Note that there may not be sufficient detail or instrumentation to
+ instantiate ConcreteIdentity. And, if the association is instantiated,
+ some duplication of information may result. For example, the Vendor
+ described in the instances of Product and SoftwareIdentity MAY be the
+ same. However, this is not necessarily true, and it is why vendor and
+ similar information are duplicated in this class. Note that
+ ConcreteIdentity can also be used to describe the relationship of the
+ software to any LogicalFiles that result from installing it. As above,
+ there may not be sufficient detail or instrumentation to instantiate
+ this association.
+
+ """
+
+ 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.
+
+ 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)
+
+ """
+ with YumDB.get_instance():
+ pkg_info = Identity.object_path2pkg(model.path, 'all')
+ return Identity.pkg2model(pkg_info, keys_only=False, model=model)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # 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({'InstanceID': None})
+
+ with YumDB.get_instance() as ydb:
+ pkglist = ydb.get_package_list(
+ 'all', allow_duplicates=True, sort=True)
+ for pkg_info in pkglist:
+ yield Identity.pkg2model(pkg_info, 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.
+
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
diff --git a/src/software/lmi/software/LMI_SoftwareIdentityChecks.py b/src/software/lmi/software/LMI_SoftwareIdentityChecks.py
new file mode 100644
index 0000000..47ae3f4
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareIdentityChecks.py
@@ -0,0 +1,256 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_SoftwareIdentityChecks
+
+Instruments the CIM class LMI_SoftwareIdentityChecks
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import generate_references
+from lmi.software.core import Identity, IdentityFileCheck
+from lmi.software.yumdb import YumDB
+
+def generate_identity_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request enumerating file checks
+ associated to software identity.
+ """
+ filecheck_model = pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityFileCheck',
+ namespace="root/cimv2",
+ host=model.path.host)
+ pkg_info = Identity.object_path2pkg(
+ object_name, kind="installed")
+ model['Element'] = Identity.pkg2model(pkg_info)
+ pkg_info, pkg_check = YumDB.get_instance().check_package(pkg_info)
+ for file_name in pkg_check:
+ model['Check'] = IdentityFileCheck.file_check2model(
+ IdentityFileCheck.FileCheck(pkg_info, pkg_check[file_name],
+ pkg_check.file_checksum_type),
+ model=filecheck_model)
+ yield model
+
+def generate_check_referents(_env, object_name, model, _keys_only):
+ """
+ Handler for referents enumeration request enumerating software
+ identities associated to file check.
+ """
+ file_check = IdentityFileCheck.object_path2file_check(object_name)
+ model['Check'] = IdentityFileCheck.file_check2model(file_check)
+ model['Element'] = Identity.pkg2model(file_check.pkg_info)
+ yield model
+
+class LMI_SoftwareIdentityChecks(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentityChecks
+
+ This association ties a SoftwareElement to a specific Check to validate
+ its state or its movement to the next state. Note that
+ SoftwareElements in a running state cannot transition to another
+ state. Therefore, the value of the Phase property is restricted to 0
+ ("In-State") for SoftwareElements in the running state.
+
+ """
+
+ def __init__ (self, _env):
+ 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.
+
+ 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)
+
+ """
+ file_check = IdentityFileCheck.object_path2file_check(model['Check'])
+ model['Check'] = IdentityFileCheck.file_check2model(file_check)
+ model['Element'] = Identity.pkg2model(file_check.pkg_info)
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ 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.
+
+ 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)
+
+ """
+ handlers = [
+ ("Element", "LMI_SoftwareIdentity", generate_identity_referents),
+ ("Check", "LMI_SoftwareIdentityFileCheck",
+ generate_check_referents)
+ ]
+
+ for ref in generate_references(env, object_name, model,
+ result_class_name, role, result_role, keys_only, handlers):
+ yield ref
+
diff --git a/src/software/lmi/software/LMI_SoftwareIdentityFileCheck.py b/src/software/lmi/software/LMI_SoftwareIdentityFileCheck.py
new file mode 100644
index 0000000..fe6f101
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareIdentityFileCheck.py
@@ -0,0 +1,244 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_SoftwareIdentityFileCheck
+
+Instruments the CIM class LMI_SoftwareIdentityFileCheck
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import IdentityFileCheck
+from lmi.software.core import ComputerSystem
+
+class LMI_SoftwareIdentityFileCheck(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentityFileCheck
+
+ 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):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = IdentityFileCheck.Values
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ file_check = IdentityFileCheck.object_path2file_check(model.path)
+ return IdentityFileCheck.file_check2model(file_check, keys_only=False,
+ model=model)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # 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.
+
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def cim_method_invoke(self, env, object_name):
+ """Implements LMI_SoftwareIdentityFileCheck.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)
+
+ """
+ file_check = IdentityFileCheck.object_path2file_check(object_name)
+ out_params = []
+ return ( self.values.Invoke.Satisfied
+ if IdentityFileCheck.file_check_passed(file_check)
+ else self.values.Invoke.Not_Satisfied,
+ out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_invokeonsystem(self, env, object_name,
+ param_targetsystem=None):
+ """Implements LMI_SoftwareIdentityFileCheck.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)
+ """
+ if param_targetsystem is not None:
+ ComputerSystem.check_path(env, param_targetsystem, "TargetSystem")
+ return self.cim_method_invoke(env, object_name)
+
diff --git a/src/software/lmi/software/LMI_SoftwareIdentityResource.py b/src/software/lmi/software/LMI_SoftwareIdentityResource.py
new file mode 100644
index 0000000..ff359b7
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareIdentityResource.py
@@ -0,0 +1,270 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Python Provider for LMI_SoftwareIdentityResource
+
+Instruments the CIM class LMI_SoftwareIdentityResource
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import IdentityResource
+from lmi.software.yumdb import YumDB, errors
+
+class LMI_SoftwareIdentityResource(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareIdentityResource
+
+ SoftwareIdentityResource describes the URL of a file or other resource
+ that contains all or part of of a SoftwareIdentity for use by the
+ SoftwareInstallationService. For example, a CIM_SoftwareIdentity might
+ consist of a meta data file, a binary executable file, and a
+ installability checker file for some software on a system. This class
+ allows a management client to selectively access the constituents of
+ the install package to perform a check function, or retrieve some meta
+ data information for the install package represented by the
+ SoftwareIdentity class without downloading the entire package.
+ SoftwareIdentityResources will be related to the SoftwareIdentity
+ using the SAPAvailableForElement association.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = IdentityResource.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ repo = IdentityResource.object_path2repo(env, model.path, kind='all')
+ return IdentityResource.repo2model(repo, keys_only=False, model=model)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # 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({'CreationClassName': None, 'SystemName': None,
+ 'Name': None, 'SystemCreationClassName': None})
+
+ repolist = YumDB.get_instance().get_repository_list('all')
+ for repo in repolist:
+ yield IdentityResource.repo2model(
+ repo, 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.
+
+ 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)
+
+ """
+ # TODO creation and modification should be supported
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # TODO removal should also be supported
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareIdentityResource.RequestStateChange()
+
+ Requests that the state of the element be changed to the value
+ specified in the RequestedState parameter. When the requested
+ state change takes place, the EnabledState and RequestedState of
+ the element will be the same. Invoking the RequestStateChange
+ method multiple times could result in earlier requests being
+ overwritten or lost. A return code of 0 shall indicate the state
+ change was successfully initiated. A return code of 3 shall
+ indicate that the state transition cannot complete within the
+ interval specified by the TimeoutPeriod parameter. A return code
+ of 4096 (0x1000) shall indicate the state change was successfully
+ initiated, a ConcreteJob has been created, and its reference
+ returned in the output parameter Job. Any other return code
+ indicates an error condition.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method RequestStateChange()
+ should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16 Values.RequestStateChange.RequestedState)
+ The state requested for the element. This information will be
+ placed into the RequestedState property of the instance if the
+ return code of the RequestStateChange method is 0 ('Completed
+ with No Error'), or 4096 (0x1000) ('Job Started'). Refer to
+ the description of the EnabledState and RequestedState
+ properties for the detailed explanations of the RequestedState
+ values.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' shall be returned.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 Values.RequestStateChange)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ May contain a reference to the ConcreteJob created to track the
+ state transition initiated by the method invocation.
+
+ 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)
+ """
+ out_params = []
+ if param_timeoutperiod is not None:
+ return ( self.values.RequestStateChange. \
+ Use_of_Timeout_Parameter_Not_Supported
+ , out_params)
+ if param_requestedstate not in {
+ self.values.RequestStateChange.RequestedState.Enabled,
+ self.values.RequestStateChange.RequestedState.Disabled }:
+ return ( self.values.RequestStateChange.Invalid_State_Transition
+ , out_params)
+
+ with YumDB.get_instance() as ydb:
+ repo = IdentityResource.object_path2repo(env,
+ object_name, 'all')
+ enable = param_requestedstate == \
+ self.values.RequestStateChange.RequestedState.Enabled
+ cmpi_logging.logger.info("%s repository %s" % ("enabling"
+ if enable else "disabling", repo))
+ try:
+ prev = ydb.set_repository_enabled(
+ repo, enable)
+ except errors.RepositoryChangeError as exc:
+ msg = "failed to modify repository %s: %s" % (repo, str(exc))
+ cmpi_logging.logger.error(msg)
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, msg)
+ msg = ( "repository %s already %s"
+ if prev == enable else "repository %s %s")
+ cmpi_logging.logger.info(msg % (repo,
+ "enabled" if enable else "disabled"))
+
+ rval = self.values.RequestStateChange.Completed_with_No_Error
+ return (rval, out_params)
+
diff --git a/src/software/lmi/software/LMI_SoftwareInstallationService.py b/src/software/lmi/software/LMI_SoftwareInstallationService.py
new file mode 100644
index 0000000..7b2b73e
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareInstallationService.py
@@ -0,0 +1,862 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareInstallationService
+
+Instruments the CIM class LMI_SoftwareInstallationService
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import Job
+from lmi.software.core import InstallationService
+
+class LMI_SoftwareInstallationService(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstallationService
+
+ A subclass of service which provides methods to install (or update)
+ Software Identities in ManagedElements.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = InstallationService.Values
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ InstallationService.check_path(env, model.path, "path")
+
+ objpath = InstallationService.get_path()
+
+ for key, value in objpath.keybindings.items():
+ model[key] = value
+
+ model['Caption'] = 'Software installation service for this system.'
+ model['CommunicationStatus'] = self.values. \
+ CommunicationStatus.Not_Available
+ model['Description'] = \
+ 'Software installation service using YUM packake manager.'
+ model['DetailedStatus'] = self.values.DetailedStatus.Not_Available
+ model['EnabledDefault'] = self.values.EnabledDefault.Not_Applicable
+ model['EnabledState'] = self.values.EnabledState.Not_Applicable
+ model['HealthState'] = self.values.HealthState.OK
+ model['InstanceID'] = 'LMI:LMI_InstallationService'
+ model['OperatingStatus'] = self.values.OperatingStatus.Servicing
+ model['OperationalStatus'] = [self.values.OperationalStatus.OK]
+ model['PrimaryStatus'] = self.values.PrimaryStatus.OK
+ model['RequestedState'] = self.values.RequestedState.Not_Applicable
+ model['Started'] = True
+ model['TransitioningToState'] = self.values. \
+ TransitioningToState.Not_Applicable
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'CreationClassName': None, 'SystemName': None,
+ 'Name': None, 'SystemCreationClassName': None})
+
+ objpath = InstallationService.get_path()
+ for key, value in objpath.keybindings.items():
+ model[key] = value
+ if not keys_only:
+ model = self.get_instance(env, model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareInstallationService.RequestStateChange()
+
+ Requests that the state of the element be changed to the value
+ specified in the RequestedState parameter. When the requested
+ state change takes place, the EnabledState and RequestedState of
+ the element will be the same. Invoking the RequestStateChange
+ method multiple times could result in earlier requests being
+ overwritten or lost. A return code of 0 shall indicate the state
+ change was successfully initiated. A return code of 3 shall
+ indicate that the state transition cannot complete within the
+ interval specified by the TimeoutPeriod parameter. A return code
+ of 4096 (0x1000) shall indicate the state change was successfully
+ initiated, a ConcreteJob has been created, and its reference
+ returned in the output parameter Job. Any other return code
+ indicates an error condition.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method RequestStateChange()
+ should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16 self.Values.RequestStateChange.RequestedState)
+ The state requested for the element. This information will be
+ placed into the RequestedState property of the instance if the
+ return code of the RequestStateChange method is 0 ('Completed
+ with No Error'), or 4096 (0x1000) ('Job Started'). Refer to
+ the description of the EnabledState and RequestedState
+ properties for the detailed explanations of the RequestedState
+ values.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' shall be returned.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.RequestStateChange)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ May contain a reference to the ConcreteJob created to track the
+ state transition initiated by the method invocation.
+
+
+ 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)
+ """
+ out_params = []
+ out_params += [pywbem.CIMParameter('Job', type='reference', value=None)]
+ return ( self.values.RequestStateChange.Not_Supported
+ , out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_stopservice(self, env, object_name):
+ """Implements LMI_SoftwareInstallationService.StopService()
+
+ The StopService method places the Service in the stopped state.
+ Note that the function of this method overlaps with the
+ RequestedState property. RequestedState was added to the model to
+ maintain a record (such as a persisted value) of the last state
+ request. Invoking the StopService method should set the
+ RequestedState property appropriately. The method returns an
+ integer value of 0 if the Service was successfully stopped, 1 if
+ the request is not supported, and any other number to indicate an
+ error. In a subclass, the set of possible return codes could be
+ specified using a ValueMap qualifier on the method. The strings to
+ which the ValueMap contents are translated can also be specified
+ in the subclass as a Values array qualifier. Note: The semantics
+ of this method overlap with the RequestStateChange method that is
+ inherited from EnabledLogicalElement. This method is maintained
+ because it has been widely implemented, and its simple "stop"
+ semantics are convenient to use.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method StopService()
+ 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)
+
+ """
+ rval = pywbem.Uint32(1) # Not Supported
+ return (rval, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_installfromuri(self, env, object_name,
+ param_installoptionsvalues=None,
+ param_uri=None,
+ param_installoptions=None,
+ param_target=None):
+ """Implements LMI_SoftwareInstallationService.InstallFromURI()
+
+ Start a job to install software from a specific URI in a
+ ManagedElement. Note that this method is provided to support
+ existing, alternative download mechanisms (such as used for
+ firmware download). The 'normal' mechanism will be to use the
+ InstallFromSoftwareIdentity method. If 0 is returned, the function
+ completed successfully and no ConcreteJob instance was required.
+ If 4096/0x1000 is returned, a ConcreteJob will be started to to
+ perform the install. The Job's reference will be returned in the
+ output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InstallFromURI()
+ should be invoked.
+ param_installoptionsvalues -- The input parameter \
+ InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additionalinformation to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. For
+ further information on the use of InstallOptionsValues
+ parameter, see the description of the InstallOptionsValues
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity
+ method.
+
+ param_uri -- The input parameter URI (type unicode)
+ A URI for the software based on RFC 2079.
+
+ param_installoptions -- The input parameter InstallOptions (
+ type [pywbem.Uint16,] self.Values.InstallFromURI.InstallOptions)
+ Options to control the install process. See the InstallOptions
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity method
+ for the description of these values.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.InstallFromURI)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+
+ 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)
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ try:
+ jobid = InstallationService.install_or_remove_package(
+ env, Job.JOB_METHOD_INSTALL_FROM_URI,
+ param_uri, param_target, None, param_installoptions,
+ param_installoptionsvalues)
+ rval = self.values.InstallFromURI. \
+ Method_Parameters_Checked___Job_Started
+ out_params[0].value = Job.job2model(jobid,
+ class_name="LMI_SoftwareInstallationJob")
+ except InstallationService.InstallationError as exc:
+ cmpi_logging.logger.error(
+ "installation failed: %s", exc.description)
+ rval = exc.return_code
+ return (rval, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_checksoftwareidentity(self, env, object_name,
+ param_source=None,
+ param_target=None,
+ param_collection=None):
+ """Implements LMI_SoftwareInstallationService.CheckSoftwareIdentity()
+
+ This method allows a client application to determine whether a
+ specific SoftwareIdentity can be installed (or updated) on a
+ ManagedElement. It also allows other characteristics to be
+ determined such as whether install will require a reboot. In
+ addition a client can check whether the SoftwareIdentity can be
+ added simulataneously to a specified SofwareIndentityCollection. A
+ client MAY specify either or both of the Collection and Target
+ parameters. The Collection parameter is only supported if
+ SoftwareInstallationServiceCapabilities.CanAddToCollection is
+ TRUE.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method CheckSoftwareIdentity()
+ should be invoked.
+ param_source -- The input parameter Source (type REF (
+ pywbem.CIMInstanceName(classname='CIM_SoftwareIdentity', ...))
+ Reference to the SoftwareIdentity to be checked.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ Reference to the ManagedElement that the Software Identity is
+ going to be installed in (or updated).
+
+ param_collection -- The input parameter Collection (type REF (
+ pywbem.CIMInstanceName(classname='CIM_Collection', ...))
+ Reference to the Collection to which the Software Identity will
+ be added.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.CheckSoftwareIdentity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ InstallCharacteristics -- (type [pywbem.Uint16,]
+ self.Values.CheckSoftwareIdentity.InstallCharacteristics)
+ The parameter describes the characteristics of the
+ installation/update that will take place if the Source
+ Software Identity is installed: Target automatic reset: The
+ target element will automatically reset once the installation
+ is complete. System automatic reset: The containing system of
+ the target ManagedElement (normally a logical device or the
+ system itself) will automatically reset/reboot once the
+ installation is complete. Separate target reset required:
+ EnabledLogicalElement.RequestStateChange MUST be used to reset
+ the target element after the SoftwareIdentity is installed.
+ Separate system reset required:
+ EnabledLogicalElement.RequestStateChange MUST be used to
+ reset/reboot the containing system of the target
+ ManagedElement after the SoftwareIdentity is installed.
+ Manual Reboot Required: The system MUST be manually rebooted
+ by the user. No reboot required : No reboot is required after
+ installation. User Intervention Recomended : It is
+ recommended that a user confirm installation of this
+ SoftwareIdentity. Inappropriate application MAY have serious
+ consequences. MAY be added to specified collection : The
+ SoftwareIndentity MAY be added to specified Collection.
+
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_changeaffectedelementsassignedsequence(self,
+ env, object_name,
+ param_managedelements,
+ param_assignedsequence):
+ """Implements LMI_SoftwareInstallationService. \
+ ChangeAffectedElementsAssignedSequence()
+
+ This method is called to change relative sequence in which order
+ the ManagedElements associated to the Service through
+ CIM_ServiceAffectsElement association are affected. In the case
+ when the Service represents an interface for client to execute
+ extrinsic methods and when it is used for grouping of the managed
+ elements that could be affected, the ordering represents the
+ relevant priority of the affected managed elements with respect to
+ each other. An ordered array of ManagedElement instances is
+ passed to this method, where each ManagedElement instance shall be
+ already be associated with this Service instance via
+ CIM_ServiceAffectsElement association. If one of the
+ ManagedElements is not associated to the Service through
+ CIM_ServiceAffectsElement association, the implementation shall
+ return a value of 2 ("Error Occured"). Upon successful execution
+ of this method, if the AssignedSequence parameter is NULL, the
+ value of the AssignedSequence property on each instance of
+ CIM_ServiceAffectsElement shall be updated such that the values of
+ AssignedSequence properties shall be monotonically increasing in
+ correlation with the position of the referenced ManagedElement
+ instance in the ManagedElements input parameter. That is, the
+ first position in the array shall have the lowest value for
+ AssignedSequence. The second position shall have the second lowest
+ value, and so on. Upon successful execution, if the
+ AssignedSequence parameter is not NULL, the value of the
+ AssignedSequence property of each instance of
+ CIM_ServiceAffectsElement referencing the ManagedElement instance
+ in the ManagedElements array shall be assigned the value of the
+ corresponding index of the AssignedSequence parameter array. For
+ ManagedElements instances which are associated with the Service
+ instance via CIM_ServiceAffectsElement and are not present in the
+ ManagedElements parameter array, the AssignedSequence property on
+ the CIM_ServiceAffects association shall be assigned a value of 0.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method
+ ChangeAffectedElementsAssignedSequence() should be invoked.
+ param_managedelements -- The input parameter ManagedElements (
+ type REF (pywbem.CIMInstanceName(
+ classname='CIM_ManagedElement', ...)) (Required)
+ An array of ManagedElements.
+
+ param_assignedsequence -- The input parameter AssignedSequence (
+ type [pywbem.Uint16,]) (Required)
+ An array of integers representing AssignedSequence for the
+ ManagedElement in the corresponding index of the
+ ManagedElements parameter.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values. \
+ ChangeAffectedElementsAssignedSequence)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job spawned if the operation continues after
+ the method returns. (May be null if the task is completed).
+
+
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_installfromsoftwareidentity(
+ self, env, object_name,
+ param_installoptions=None,
+ param_target=None,
+ param_collection=None,
+ param_source=None,
+ param_installoptionsvalues=None):
+ """Implements LMI_SoftwareInstallationService. \
+ InstallFromSoftwareIdentity()
+
+ Start a job to install or update a SoftwareIdentity (Source) on a
+ ManagedElement (Target). In addition the method can be used to
+ add the SoftwareIdentity simulataneously to a specified
+ SofwareIndentityCollection. A client MAY specify either or both of
+ the Collection and Target parameters. The Collection parameter is
+ only supported if InstallationService.CanAddToCollection
+ is TRUE. If 0 is returned, the function completed successfully
+ and no ConcreteJob instance was required. If 4096/0x1000 is
+ returned, a ConcreteJob will be started to perform the install.
+ The Job's reference will be returned in the output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method
+ InstallFromSoftwareIdentity()
+ should be invoked.
+ param_installoptions -- The input parameter InstallOptions (
+ type [pywbem.Uint16,] self.Values.InstallFromSoftwareIdentity.\
+ InstallOptions)
+ Options to control the install process. Defer target/system
+ reset : do not automatically reset the target/system. Force
+ installation : Force the installation of the same or an older
+ SoftwareIdentity. Install: Perform an installation of this
+ software on the managed element. Update: Perform an update of
+ this software on the managed element. Repair: Perform a repair
+ of the installation of this software on the managed element by
+ forcing all the files required for installing the software to
+ be reinstalled. Reboot: Reboot or reset the system immediately
+ after the install or update of this software, if the install
+ or the update requires a reboot or reset. Password: Password
+ will be specified as clear text without any encryption for
+ performing the install or update. Uninstall: Uninstall the
+ software on the managed element. Log: Create a log for the
+ install or update of the software. SilentMode: Perform the
+ install or update without displaying any user interface.
+ AdministrativeMode: Perform the install or update of the
+ software in the administrative mode. ScheduleInstallAt:
+ Indicates the time at which theinstall or update of the
+ software will occur.
+
+ param_target -- The input parameter Target (type REF (
+ pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target. If NULL then the SOftwareIdentity will
+ be added to Collection only. The underlying implementation is
+ expected to be able to obtain any necessary metadata from the
+ Software Identity.
+
+ param_collection -- The input parameter Collection (type REF (
+ pywbem.CIMInstanceName(classname='CIM_Collection', ...))
+ Reference to the Collection to which the Software Identity
+ SHALL be added. If NULL then the SOftware Identity will not be
+ added to a Collection.
+
+ param_source -- The input parameter Source (type REF (
+ pywbem.CIMInstanceName(classname='CIM_SoftwareIdentity', ...))
+ Reference to the source of the install.
+
+ param_installoptionsvalues -- The input parameter
+ InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additional information to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. If the
+ index in InstallOptions has the value "Password " then a value
+ at the corresponding index of InstallOptionValues shall not be
+ NULL. If the index in InstallOptions has the value
+ "ScheduleInstallAt" then the value at the corresponding index
+ of InstallOptionValues shall not be NULL and shall be in the
+ datetime type format. If the index in InstallOptions has the
+ value "Log " then a value at the corresponding index of
+ InstallOptionValues may be NULL. If the index in
+ InstallOptions has the value "Defer target/system reset",
+ "Force installation","Install", "Update", "Repair" or "Reboot"
+ then a value at the corresponding index of InstallOptionValues
+ shall be NULL.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.InstallFromSoftwareIdentity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+ 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)
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ try:
+ jobid = InstallationService.install_or_remove_package(
+ env, Job.JOB_METHOD_INSTALL_FROM_SOFTWARE_IDENTITY,
+ param_source, param_target, param_collection,
+ param_installoptions,
+ param_installoptionsvalues)
+ rval = self.values.InstallFromSoftwareIdentity. \
+ Method_Parameters_Checked___Job_Started
+ out_params[0].value = Job.job2model(jobid,
+ class_name="LMI_SoftwareInstallationJob")
+ except InstallationService.InstallationError as exc:
+ cmpi_logging.logger.error(
+ "installation failed: %s", exc.description)
+ rval = exc.return_code
+ return (rval, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_startservice(self, env, object_name):
+ """Implements LMI_SoftwareInstallationService.StartService()
+
+ The StartService method places the Service in the started state.
+ Note that the function of this method overlaps with the
+ RequestedState property. RequestedState was added to the model to
+ maintain a record (such as a persisted value) of the last state
+ request. Invoking the StartService method should set the
+ RequestedState property appropriately. The method returns an
+ integer value of 0 if the Service was successfully started, 1 if
+ the request is not supported, and any other number to indicate an
+ error. In a subclass, the set of possible return codes could be
+ specified using a ValueMap qualifier on the method. The strings to
+ which the ValueMap contents are translated can also be specified
+ in the subclass as a Values array qualifier. Note: The semantics
+ of this method overlap with the RequestStateChange method that is
+ inherited from EnabledLogicalElement. This method is maintained
+ because it has been widely implemented, and its simple "start"
+ semantics are convenient to use.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method StartService()
+ 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)
+ """
+ rval = pywbem.Uint32(1) # Not Supported
+ return (rval, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_installfrombytestream(self, env, object_name,
+ param_installoptionsvalues=None,
+ param_image=None,
+ param_installoptions=None,
+ param_target=None):
+ """Implements LMI_SoftwareInstallationService.InstallFromByteStream()
+
+ Start a job to download a series of bytes containing a software
+ image to a ManagedElement. Note that this method is provided to
+ support existing, alternative download mechanisms (such as used
+ for firmware download). The 'normal' mechanism will be to use the
+ InstallFromSoftwareIdentity method. If 0 is returned, the
+ function completed successfully and no ConcreteJob instance was
+ required. If 4096/0x1000 is returned, a ConcreteJob will be
+ started to to perform the install. The Job's reference will be
+ returned in the output parameter Job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method InstallFromByteStream()
+ should be invoked.
+ param_installoptionsvalues -- The input parameter InstallOptionsValues (type [unicode,])
+ InstallOptionsValues is an array of strings providing
+ additional information to InstallOptions for the method to
+ install the software. Each entry of this array is related to
+ the entry in InstallOptions that is located at the same index
+ providing additional information for InstallOptions. For
+ further information on the use of InstallOptionsValues
+ parameter, see the description of the InstallOptionsValues
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity
+ method.
+
+ param_image -- The input parameter Image (type [pywbem.Uint8,])
+ A array of bytes containing the install image.
+
+ param_installoptions -- The input parameter InstallOptions (type [pywbem.Uint16,] self.Values.InstallFromByteStream.InstallOptions)
+ Options to control the install process. See the InstallOptions
+ parameter of the
+ InstallationService.InstallFromSoftwareIdentity method
+ for the description of these values.
+
+ param_target -- The input parameter Target (type REF (pywbem.CIMInstanceName(classname='CIM_ManagedElement', ...))
+ The installation target.
+
+
+ Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.InstallFromByteStream)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(classname='CIM_ConcreteJob', ...))
+ Reference to the job (may be null if job completed).
+
+
+ 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)
+
+ """
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ return ( self.values.InstallFromByteStream.Not_Supported
+ , out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_verifyinstalledidentity(self, env, object_name,
+ param_source=None,
+ param_target=None):
+ """Implements LMI_SoftwareInstallationService. \
+ VerifyInstalledIdentity()
+
+ Start a job to verify installed package represented by
+ SoftwareIdentity (source) (Source) on a ManagedElement (Target).
+ If 0 is returned, the function completed successfully and no
+ ConcreteJob instance was required. If 4096/0x1000 is returned, a
+ ConcreteJob will be started to perform the verification. The Job's
+ reference will be returned in the output parameter Job. In former
+ case, the Failed parameterwill contain all associated file checks,
+ that did not pass. In the latter case this property will be NULL.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method VerifyInstalledIdentity()
+ should be invoked.
+ param_source -- The input parameter Source (
+ type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentity', ...))
+ Reference to the installed SoftwareIdentity to be verified.
+
+ param_target -- The input parameter Target (
+ type REF (pywbem.CIMInstanceName(
+ classname='CIM_ManagedElement', ...))
+ Reference to the ManagedElement that the Software Identity is
+ installed on.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.VerifyInstalledIdentity)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Job -- (type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareVerificationJob', ...))
+ Reference to the job (may be null if job completed).
+
+ Failed -- (type REF (pywbem.CIMInstanceName(
+ classname='LMI_SoftwareIdentityFileCheck', ...))
+ Array of file checks that did not pass verification. This is
+ NULL in case that asynchronous job has been started.
+
+
+ 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)
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ out_params = [pywbem.CIMParameter('Job', type='reference', value=None)]
+ try:
+ jobid = InstallationService.verify_package(
+ env, Job.JOB_METHOD_VERIFY_INSTALLED_IDENTITY,
+ param_source, param_target)
+ rval = self.values.VerifyInstalledIdentity. \
+ Method_Parameters_Checked___Job_Started
+ out_params[0].value = Job.job2model(jobid,
+ class_name="LMI_SoftwareVerificationJob")
+ except InstallationService.InstallationError as exc:
+ cmpi_logging.logger.error(
+ "failed to launch verification job: %s", exc.description)
+ rval = exc.return_code
+ return (rval, out_params)
+
diff --git a/src/software/lmi/software/LMI_SoftwareInstallationServiceAffectsElement.py b/src/software/lmi/software/LMI_SoftwareInstallationServiceAffectsElement.py
new file mode 100644
index 0000000..92d5c26
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareInstallationServiceAffectsElement.py
@@ -0,0 +1,299 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareInstallationServiceAffectsElement
+
+Instruments the CIM class LMI_SoftwareInstallationServiceAffectsElement
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem
+from lmi.software.core import Identity
+from lmi.software.core import InstallationService
+from lmi.software.core import InstallationServiceAffectsElement
+from lmi.software.yumdb import YumDB
+
+def generate_service_referents(env, model, object_name, keys_only):
+ """
+ Used in references method.
+ """
+ InstallationService.check_path(env, object_name, "object_name")
+ InstallationServiceAffectsElement.fill_model_computer_system(
+ model, keys_only=keys_only)
+ yield model
+
+ avail_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ for pkg_info in YumDB.get_instance().get_package_list('available'):
+ model["AffectedElement"] = InstallationServiceAffectsElement. \
+ fill_model_identity(model, pkg_info,
+ keys_only=keys_only,
+ identity_model=avail_model)
+ yield model
+
+class LMI_SoftwareInstallationServiceAffectsElement(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstallationServiceAffectsElement
+
+ ServiceAffectsElement represents an association between a Service and
+ the ManagedElements that might be affected by its execution.
+ Instantiating this association indicates that running the service may
+ change, manage, provide functionality for,or pose some burden on the
+ ManagedElement. This burden might affect performance, throughput,
+ availability, and so on.
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = InstallationServiceAffectsElement.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ InstallationService.check_path_property(
+ env, model, "AffectingElement")
+
+ ch = env.get_cimom_handle()
+
+ affected = model["AffectedElement"]
+ if ch.is_subclass(affected.namespace,
+ sub=affected.classname, super='LMI_SoftwareIdentity'):
+ pkg_info = Identity.object_path2pkg(affected, kind='available')
+ InstallationServiceAffectsElement.fill_model_identity(
+ model, pkg_info, keys_only=False)
+ elif ch.is_subclass(affected.namespace,
+ sub=affected.classname, super='Linux_ComputerSystem'):
+ InstallationServiceAffectsElement.fill_model_computer_system(
+ model, keys_only=False)
+ else:
+ cmpi_logging.logger.error("Unhandled classname: %s",
+ affected.classname)
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Provided AffectedElement not affected by this service.")
+
+ return model
+
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'AffectedElement': None, 'AffectingElement': None})
+
+ model['AffectingElement'] = InstallationService.get_path()
+ InstallationServiceAffectsElement.fill_model_computer_system(
+ model, keys_only=keys_only)
+ yield model
+
+ avail_model = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ for pkg_info in YumDB.get_instance().get_package_list('available'):
+ model["AffectedElement"] = InstallationServiceAffectsElement. \
+ fill_model_identity(model, pkg_info, keys_only=keys_only,
+ identity_model=avail_model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ 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.
+
+ 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)
+ """
+ ch = env.get_cimom_handle()
+
+ model.path.update({'AffectedElement': None, 'AffectingElement': None})
+ try:
+ if ( (not role or role.lower() == 'affectingelement')
+ and ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super="LMI_SoftwareInstallationService")):
+ for ref in generate_service_referents(
+ env, model, object_name, keys_only):
+ yield ref
+
+ elif ( (not role or role.lower() == 'affectedelement')
+ and ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super="CIM_ComputerSystem")):
+ ComputerSystem.check_path(env, object_name, "object_name")
+ model = InstallationServiceAffectsElement. \
+ fill_model_computer_system(model, keys_only=keys_only)
+ model["AffectingElement"] = InstallationService.get_path()
+ yield model
+
+ elif ( (not role or role.lower() == 'affectedelement')
+ and ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super="LMI_SoftwareIdentity")):
+ pkg_info = Identity.object_path2pkg(object_name, "available")
+ model = InstallationServiceAffectsElement. \
+ fill_model_identity(model, pkg_info,
+ keys_only=keys_only)
+ model["AffectingElement"] = InstallationService.get_path()
+ yield model
+
+ except pywbem.CIMError as exc:
+ if exc.args[0] != pywbem.CIM_ERR_NOT_FOUND:
+ cmpi_logging.logger.exception(
+ 'failed to generate referents of "%s"', object_name)
+ raise
diff --git a/src/software/lmi/software/LMI_SoftwareInstallationServiceCapabilities.py b/src/software/lmi/software/LMI_SoftwareInstallationServiceCapabilities.py
new file mode 100644
index 0000000..739d391
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareInstallationServiceCapabilities.py
@@ -0,0 +1,292 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareInstallationServiceCapabilities
+
+Instruments the CIM class LMI_SoftwareInstallationServiceCapabilities
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import InstallationServiceCapabilities
+
+class LMI_SoftwareInstallationServiceCapabilities(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareInstallationServiceCapabilities
+
+ A subclass of capabilities that defines the capabilities of a
+ SoftwareInstallationService. A single instance of
+ SoftwareInstallationServiceCapabilities is associated with a
+ SoftwareInstallationService using ElementCapabilities.
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = InstallationServiceCapabilities.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ InstallationServiceCapabilities.check_path(env,
+ model.path, "path")
+ return InstallationServiceCapabilities.get_instance(model)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ model.path.update({'InstanceID': None})
+
+ if keys_only:
+ model['InstanceID'] = \
+ InstallationServiceCapabilities.get_path()['InstanceID']
+ else:
+ model = InstallationServiceCapabilities.get_instance(model)
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ def cim_method_creategoalsettings(self, env, object_name,
+ param_supportedgoalsettings=None,
+ param_templategoalsettings=None):
+ """Implements LMI_SoftwareInstallationServiceCapabilities. \
+ CreateGoalSettings()
+
+ Method to create a set of supported SettingData elements, from two
+ sets of SettingData elements, provided by the caller. CreateGoal
+ should be used when the SettingData instances that represents the
+ goal will not persist beyond the execution of the client and where
+ those instances are not intended to be shared with other,
+ non-cooperating clients. Both TemplateGoalSettings and
+ SupportedGoalSettings are represented as strings containing
+ EmbeddedInstances of a CIM_SettingData subclass. These embedded
+ instances do not exist in the infrastructure supporting this
+ method but are maintained by the caller/client. This method
+ should return CIM_Error(s) representing that a single named
+ property of a setting (or other) parameter (either reference or
+ embedded object) has an invalid value or that an invalid
+ combination of named properties of a setting (or other) parameter
+ (either reference or embedded object) has been requested. If the
+ input TemplateGoalSettings is NULL or the empty string, this
+ method returns a default SettingData element that is supported by
+ this Capabilities element. If the TemplateGoalSettings specifies
+ values that cannot be supported, this method shall return an
+ appropriate CIM_Error and should return a best match for a
+ SupportedGoalSettings. The client proposes a goal using the
+ TemplateGoalSettings parameter and gets back Success if the
+ TemplateGoalSettings is exactly supportable. It gets back
+ "Alternative Proposed" if the output SupportedGoalSettings
+ represents a supported alternative. This alternative should be a
+ best match, as defined by the implementation. If the
+ implementation is conformant to a RegisteredProfile, then that
+ profile may specify the algorithms used to determine best match. A
+ client may compare the returned value of each property against the
+ requested value to determine if it is left unchanged, degraded or
+ upgraded. Otherwise, if the TemplateGoalSettings is not
+ applicable an "Invalid Parameter" error is returned. When a
+ mutually acceptable SupportedGoalSettings has been achieved, the
+ client may use the contained SettingData instances as input to
+ methods for creating a new object ormodifying an existing object.
+ Also the embedded SettingData instances returned in the
+ SupportedGoalSettings may be instantiated via CreateInstance,
+ either by a client or as a side-effect of the execution of an
+ extrinsic method for which the returned SupportedGoalSettings is
+ passed as an embedded instance.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method CreateGoalSettings()
+ should be invoked.
+ param_supportedgoalsettings -- The input parameter
+ SupportedGoalSettings (type pywbem.CIMInstance(
+ classname='CIM_SettingData', ...))
+ SupportedGoalSettings are elements of class CIM_SettingData, or
+ a derived class. At most, one instance of each SettingData
+ subclass may be supplied. All SettingData instances provided
+ by this property are interpreted as a set, relative to this
+ Capabilities instance. To enable a client to provide
+ additional information towards achieving the
+ TemplateGoalSettings, an input set of SettingData instances
+ may be provided. If not provided, this property shall be set
+ to NULL on input.. Note that when provided, what property
+ values are changed, and how, is implementation dependent and
+ may be the subject of other standards. If provided, the input
+ SettingData instances must be ones that the implementation is
+ able to support relative to the ManagedElement associated via
+ ElementCapabilities. Typically, the input SettingData
+ instances are created by a previous instantiation of
+ CreateGoalSettings. If the input SupportedGoalSettings is not
+ supported by the implementation, then an "Invalid Parameter"
+ (5) error is returned by this call. In this case, a
+ corresponding CIM_ERROR should also be returned. On output,
+ this property is used to return the best supported match to
+ the TemplateGoalSettings. If the output SupportedGoalSettings
+ matches the input SupportedGoalSettings, then the
+ implementation is unable to improve further towards meeting
+ the TemplateGoalSettings.
+
+ param_templategoalsettings -- The input parameter
+ TemplateGoalSettings (type pywbem.CIMInstance(
+ classname='CIM_SettingData', ...))
+ If provided, TemplateGoalSettings are elements of class
+ CIM_SettingData, or a derived class, that is used as the
+ template to be matched. . At most, one instance of each
+ SettingData subclass may be supplied. All SettingData
+ instances provided by this property are interpreted as a set,
+ relative to this Capabilities instance. SettingData instances
+ that are not relevant to this instance are ignored. If not
+ provided, it shall be set to NULL. In that case, a SettingData
+ instance representing the default settings of the associated
+ ManagedElement is used.
+
+
+ Returns a two-tuple containing the return value (type pywbem.Uint16
+ self.Values.CreateGoalSettings)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ SupportedGoalSettings -- (type
+ pywbem.CIMInstance(classname='CIM_SettingData', ...))
+ SupportedGoalSettings are elements of class CIM_SettingData, or
+ a derived class. At most, one instance of each SettingData
+ subclass may be supplied. All SettingData instances provided
+ by this property are interpreted as a set, relative to this
+ Capabilities instance. To enable a client to provide
+ additional information towards achieving the
+ TemplateGoalSettings, an input set of SettingData instances
+ may be provided. If not provided, this property shall be set
+ to NULL on input.. Note that when provided, what property
+ values are changed, and how, is implementation dependent and
+ may be the subject of other standards. If provided, the input
+ SettingData instances must be ones that the implementation is
+ able to support relative to the ManagedElement associated via
+ ElementCapabilities. Typically, the input SettingData
+ instances are created by a previous instantiation of
+ CreateGoalSettings. If the input SupportedGoalSettings is not
+ supported by the implementation, then an "Invalid Parameter"
+ (5) error is returned by this call. In this case, a
+ corresponding CIM_ERROR should also be returned. On output,
+ this property is used to return the best supported match to
+ the TemplateGoalSettings. If the output SupportedGoalSettings
+ matches the input SupportedGoalSettings, then the
+ implementation is unable to improve further towards meeting
+ the TemplateGoalSettings.
+
+
+ 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)
+ """
+ return (self.values.CreateGoalSettings.Not_Supported, [])
+
diff --git a/src/software/lmi/software/LMI_SoftwareJob.py b/src/software/lmi/software/LMI_SoftwareJob.py
new file mode 100644
index 0000000..79275bf
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareJob.py
@@ -0,0 +1,385 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareInstallationJob and
+LMI_SoftwareVerificationJob. They will be referred to as LMI_SoftwareJob,
+which is their base class.
+
+Instruments the CIM class LMI_SoftwareJob
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import Job
+from lmi.software.yumdb import errors, YumDB
+
+class LMI_SoftwareJob(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareJob
+
+ A concrete version of Job. This class represents a generic and
+ instantiable unit of work, such as a batch or a print job.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.debug('Initializing provider %s from %s' \
+ % (self.__class__.__name__, __file__))
+ self.values = Job.Values
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ job = Job.object_path2job(model.path)
+ return Job.job2model(job, keys_only=False, model=model)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ # 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({'InstanceID': None})
+
+ ch = env.get_cimom_handle()
+ for job in YumDB.get_instance().get_job_list():
+ if ch.is_subclass(model.path.namespace,
+ sub=model.path.classname,
+ super=Job.job_class2cim_class_name(job.__class__)):
+ yield Job.job2model(job, 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.
+
+ 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)
+
+ """
+ if not modify_existing:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED,
+ "Can not create new instance.")
+ return Job.modify_instance(instance)
+
+ @cmpi_logging.trace_method
+ 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)
+ """
+ with YumDB.get_instance() as ydb:
+ job = Job.object_path2job(instance_name)
+ try:
+ cmpi_logging.logger.info('deleting job "%s"' % job)
+ ydb.delete_job(job.jobid)
+ cmpi_logging.logger.info('job "%s" removed' % job)
+ except errors.JobNotFound as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ getattr(exc, 'message', str(exc)))
+ except errors.JobControlError as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ getattr(exc, 'message', str(exc)))
+
+ @cmpi_logging.trace_method
+ def cim_method_requeststatechange(self, env, object_name,
+ param_requestedstate=None,
+ param_timeoutperiod=None):
+ """Implements LMI_SoftwareJob.RequestStateChange()
+
+ Requests that the state of the job be changed to the value
+ specified in the RequestedState parameter. Invoking the
+ RequestStateChange method multiple times could result in earlier
+ requests being overwritten or lost. If 0 is returned, then the
+ task completed successfully. Any other return code indicates an
+ error condition.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method RequestStateChange()
+ should be invoked.
+ param_requestedstate -- The input parameter RequestedState (
+ type pywbem.Uint16
+ self.Values.RequestStateChange.RequestedState)
+ RequestStateChange changes the state of a job. The possible
+ values are as follows: Start (2) changes the state to
+ 'Running'. Suspend (3) stops the job temporarily. The
+ intention is to subsequently restart the job with 'Start'. It
+ might be possible to enter the 'Service' state while
+ suspended. (This is job-specific.) Terminate (4) stops the
+ job cleanly, saving data, preserving the state, and shutting
+ down all underlying processes in an orderly manner. Kill (5)
+ terminates the job immediately with no requirement to save
+ data or preserve the state. Service (6) puts the job into a
+ vendor-specific service state. It might be possible to restart
+ the job.
+
+ param_timeoutperiod -- The input parameter TimeoutPeriod (
+ type pywbem.CIMDateTime)
+ A timeout period that specifies the maximum amount of time that
+ the client expects the transition to the new state to take.
+ The interval format must be used to specify the TimeoutPeriod.
+ A value of 0 or a null parameter indicates that the client has
+ no time requirements for the transition. If this property
+ does not contain 0 or null and the implementation does not
+ support this parameter, a return code of 'Use Of Timeout
+ Parameter Not Supported' must be returned.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.RequestStateChange)
+ 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)
+ """
+ job = Job.object_path2job(object_name)
+ try:
+ if param_requestedstate not in {
+ self.values.RequestStateChange.RequestedState.Terminate }:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Valid RequestedState can by only Terminate (%d)" %
+ self.values.RequestStateChange.RequestedState.Terminate)
+ if param_timeoutperiod:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Timeout period is not supported.")
+ YumDB.get_instance().terminate_job(job.jobid)
+ except errors.JobNotFound as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ getattr(exc, 'message', str(exc)))
+ except errors.JobControlError as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_FAILED,
+ getattr(exc, 'message', str(exc)))
+ return (self.values.GetErrors.Success, [])
+
+ @cmpi_logging.trace_method
+ def cim_method_geterrors(self, env, object_name):
+ """Implements LMI_SoftwareJob.GetErrors()
+
+ If JobState is "Completed" and Operational Status is "Completed"
+ then no instance of CIM_Error is returned. If JobState is
+ "Exception" then GetErrors may return intances of CIM_Error
+ related to the execution of the procedure or method invoked by the
+ job. If Operatational Status is not "OK" or "Completed"then
+ GetErrors may return CIM_Error instances related to the running of
+ the job.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method GetErrors()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.GetErrors)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Errors -- (type pywbem.CIMInstance(classname='CIM_Error', ...))
+ If the OperationalStatus on the Job is not "OK", then this
+ method will return one or more CIM Error instance(s).
+ Otherwise, when the Job is "OK", null is returned.
+
+
+ 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)
+ """
+ job = Job.object_path2job(object_name)
+ error = Job.job2error(job)
+ out_params = []
+ if error is not None:
+ param = pywbem.CIMParameter('Errors', type='instance',
+ is_array=True, array_size=1, value=[error])
+ out_params.append(param)
+ return (self.values.GetErrors.Success, out_params)
+
+ @cmpi_logging.trace_method
+ def cim_method_killjob(self, env, object_name,
+ param_deleteonkill=None):
+ """Implements LMI_SoftwareJob.KillJob()
+
+ KillJob is being deprecated because there is no distinction made
+ between an orderly shutdown and an immediate kill.
+ CIM_ConcreteJob.RequestStateChange() provides 'Terminate' and
+ 'Kill' options to allow this distinction. A method to kill this
+ job and any underlying processes, and to remove any 'dangling'
+ associations.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method KillJob()
+ should be invoked.
+ param_deleteonkill -- The input parameter DeleteOnKill (type bool)
+ Indicates whether or not the Job should be automatically
+ deleted upon termination. This parameter takes precedence over
+ the property, DeleteOnCompletion.
+
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.KillJob)
+ 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)
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE)
+
+ @cmpi_logging.trace_method
+ def cim_method_geterror(self, env, object_name):
+ """Implements LMI_SoftwareJob.GetError()
+
+ GetError is deprecated because Error should be an array,not a
+ scalar. When the job is executing or has terminated without error,
+ then this method returns no CIM_Error instance. However, if the
+ job has failed because of some internal problem or because the job
+ has been terminated by a client, then a CIM_Error instance is
+ returned.
+
+ Keyword arguments:
+ env -- Provider Environment (pycimmb.ProviderEnvironment)
+ object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName
+ specifying the object on which the method GetError()
+ should be invoked.
+
+ Returns a two-tuple containing the return value (
+ type pywbem.Uint32 self.Values.GetError)
+ and a list of CIMParameter objects representing the output parameters
+
+ Output parameters:
+ Error -- (type pywbem.CIMInstance(classname='CIM_Error', ...))
+ If the OperationalStatus on the Job is not "OK", then this
+ method will return a CIM Error instance. Otherwise, when the
+ Job is "OK", null is returned.
+
+
+ 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)
+ """
+ job = Job.object_path2job(object_name)
+ error = Job.job2error(job)
+ out_params = []
+ if error is not None:
+ param = pywbem.CIMParameter('Error', type='instance', value=error)
+ out_params.append(param)
+ return (self.values.GetErrors.Success, out_params)
diff --git a/src/software/lmi/software/LMI_SoftwareMethodResult.py b/src/software/lmi/software/LMI_SoftwareMethodResult.py
new file mode 100644
index 0000000..b4d5a21
--- /dev/null
+++ b/src/software/lmi/software/LMI_SoftwareMethodResult.py
@@ -0,0 +1,163 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SoftwareMethodResult
+
+Instruments the CIM class LMI_SoftwareMethodResult
+
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import MethodResult
+from lmi.software.yumdb import YumDB
+
+class LMI_SoftwareMethodResult(CIMProvider2):
+ """Instrument the CIM class LMI_SoftwareMethodResult
+
+ Jobs are sometimes used to represent extrinsic method invocations that
+ execute for times longer than the length of time is reasonable to
+ require a client to wait. The method executing continues beyond the
+ method return to the client. The class provides the result of the
+ execution of a Job that was itself started by the side-effect of this
+ extrinsic method invocation. The indication instances embedded an
+ instance of this class shall be the same indications delivered to
+ listening clients or recorded, all or in part, to logs. Basically,
+ this approach is a corollary to the functionality provided by an
+ instance of ListenerDestinationLog (as defined in the Interop Model).
+ The latter provides a comprehensive, persistent mechanism for
+ recording Job results, but is also more resource-intensive and
+ requires supporting logging functionality. Both the extra resources
+ and logging may not be available in all environments (for example,
+ embedded environments). Therefore, this instance-based approach is
+ also provided. The MethodResult instances shall not exist after the
+ associated ConcreteJob is deleted.
+
+ """
+
+ def __init__ (self, _env):
+ cmpi_logging.logger.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)
+
+ """
+ job = MethodResult.object_path2job(model.path)
+ return MethodResult.job2model(job, 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)
+
+ """
+ # 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({'InstanceID': None})
+
+ for job in YumDB.get_instance().get_job_list():
+ yield MethodResult.job2model(job, keys_only=keys_only, model=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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
diff --git a/src/software/lmi/software/LMI_SystemSoftwareCollection.py b/src/software/lmi/software/LMI_SystemSoftwareCollection.py
new file mode 100644
index 0000000..6aea1d9
--- /dev/null
+++ b/src/software/lmi/software/LMI_SystemSoftwareCollection.py
@@ -0,0 +1,173 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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_SystemSoftwareCollection
+
+Instruments the CIM class LMI_SystemSoftwareCollection
+"""
+
+import pywbem
+from pywbem.cim_provider2 import CIMProvider2
+
+from lmi.common import cmpi_logging
+from lmi.software.core import SystemCollection
+
+class LMI_SystemSoftwareCollection(CIMProvider2):
+ """Instrument the CIM class LMI_SystemSoftwareCollection
+
+ SystemSoftwareCollection represents the general concept of a collection
+ that is scoped (or contained) by a System. It represents a Collection
+ that has meaning only in the context of a System, a Collection whose
+ elements are restricted by the definition of the System, or both of
+ these types of Collections. This meaning is explicitly described by
+ the (required) association, HostedCollection. An example of a
+ SystemSoftwareCollection is a Fibre Channel zone that collects network
+ ports, port groupings, and aliases (as required by a customer) in the
+ context of an AdminDomain. The Collection is not a part of the domain,
+ but merely an arbitrary grouping of the devices and other Collections
+ in the domain. In other words, the context of the Collection is
+ restricted to the domain, and its members are also limited by the
+ domain.
+
+ """
+
+ 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.
+
+ 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)
+
+ """
+ if not 'InstanceID' in model:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing InstanceID key property")
+ if model['InstanceID'] != SystemCollection.get_path()['InstanceID']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No such instance.")
+
+ model['Caption'] = "System RPM Package Collection"
+ #model['Description'] = '' # TODO
+ #model['ElementName'] = '' # TODO
+ return model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ # 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({'InstanceID': None})
+
+ model['InstanceID'] = SystemCollection.get_path()["InstanceID"]
+ if keys_only is False:
+ yield self.get_instance(env, model)
+ else:
+ yield model
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
+ @cmpi_logging.trace_method
+ 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)
+
+ """
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED)
+
diff --git a/src/software/lmi/software/__init__.py b/src/software/lmi/software/__init__.py
new file mode 100644
index 0000000..ffdae3f
--- /dev/null
+++ b/src/software/lmi/software/__init__.py
@@ -0,0 +1,25 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+CIM providers for software management.
+Part of OpenLMI project.
+"""
diff --git a/src/software/lmi/software/cimom_entry.py b/src/software/lmi/software/cimom_entry.py
new file mode 100644
index 0000000..89ad17f
--- /dev/null
+++ b/src/software/lmi/software/cimom_entry.py
@@ -0,0 +1,212 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Entry module for OpenLMI Software providers.
+"""
+
+from multiprocessing import Queue
+from lmi.common import cmpi_logging
+from lmi.common import JobManager
+from lmi.common.IndicationManager import IndicationManager
+from lmi.software.core import Job
+from lmi.software.LMI_SoftwareIdentity import LMI_SoftwareIdentity
+from lmi.software.LMI_SystemSoftwareCollection import \
+ LMI_SystemSoftwareCollection
+from lmi.software.LMI_HostedSoftwareCollection import \
+ LMI_HostedSoftwareCollection
+from lmi.software.LMI_MemberOfSoftwareCollection import \
+ LMI_MemberOfSoftwareCollection
+from lmi.software.LMI_InstalledSoftwareIdentity import \
+ LMI_InstalledSoftwareIdentity
+from lmi.software.LMI_SoftwareIdentityResource import \
+ LMI_SoftwareIdentityResource
+from lmi.software.LMI_ResourceForSoftwareIdentity import \
+ LMI_ResourceForSoftwareIdentity
+from lmi.software.LMI_HostedSoftwareIdentityResource import \
+ LMI_HostedSoftwareIdentityResource
+from lmi.software.LMI_SoftwareInstallationService import \
+ LMI_SoftwareInstallationService
+from lmi.software.LMI_SoftwareInstallationServiceCapabilities import \
+ LMI_SoftwareInstallationServiceCapabilities
+from lmi.software.LMI_AssociatedSoftwareInstallationServiceCapabilities \
+ import LMI_AssociatedSoftwareInstallationServiceCapabilities
+from lmi.software.LMI_HostedSoftwareInstallationService import \
+ LMI_HostedSoftwareInstallationService
+from lmi.software.LMI_SoftwareInstallationServiceAffectsElement import \
+ LMI_SoftwareInstallationServiceAffectsElement
+from lmi.software.LMI_SoftwareJob import LMI_SoftwareJob
+from lmi.software.LMI_SoftwareMethodResult import LMI_SoftwareMethodResult
+from lmi.software.LMI_AffectedSoftwareJobElement import \
+ LMI_AffectedSoftwareJobElement
+from lmi.software.LMI_AssociatedSoftwareJobMethodResult import \
+ LMI_AssociatedSoftwareJobMethodResult
+from lmi.software.LMI_OwningSoftwareJobElement import \
+ LMI_OwningSoftwareJobElement
+from lmi.software.LMI_SoftwareIdentityFileCheck import \
+ LMI_SoftwareIdentityFileCheck
+from lmi.software.LMI_SoftwareIdentityChecks import LMI_SoftwareIdentityChecks
+from lmi.software.yumdb import jobmanager, YumDB
+
+def get_providers(env):
+ """
+ @return mapping of provider names to corresponding provider instances.
+ """
+ cmpi_logging.LogManager.LOGGER_NAME = 'lmi.software'
+ cmpi_logging.LogManager(env)
+
+ # jobmanager does not understand CIM models, give it a way to transform
+ # job to CIMIndication instance
+ jobmanager.JOB_TO_MODEL = lambda job: Job.job2model(job, keys_only=False)
+
+ providers = {
+ "LMI_SoftwareIdentity" : LMI_SoftwareIdentity(env),
+ "LMI_SystemSoftwareCollection" : LMI_SystemSoftwareCollection(env),
+ "LMI_HostedSoftwareCollection" : LMI_HostedSoftwareCollection(env),
+ "LMI_MemberOfSoftwareCollection" : LMI_MemberOfSoftwareCollection(env),
+ "LMI_InstalledSoftwareIdentity" : LMI_InstalledSoftwareIdentity(env),
+ "LMI_SoftwareIdentityResource" : LMI_SoftwareIdentityResource(env),
+ "LMI_ResourceForSoftwareIdentity" :
+ LMI_ResourceForSoftwareIdentity(env),
+ "LMI_HostedSoftwareIdentityResource" :
+ LMI_HostedSoftwareIdentityResource(env),
+ "LMI_SoftwareInstallationService" : \
+ LMI_SoftwareInstallationService(env),
+ "LMI_SoftwareInstallationServiceCapabilities" : \
+ LMI_SoftwareInstallationServiceCapabilities(env),
+ "LMI_AssociatedSoftwareInstallationServiceCapabilities" : \
+ LMI_AssociatedSoftwareInstallationServiceCapabilities(env),
+ "LMI_HostedSoftwareInstallationService" : \
+ LMI_HostedSoftwareInstallationService(env),
+ "LMI_SoftwareInstallationServiceAffectsElement" : \
+ LMI_SoftwareInstallationServiceAffectsElement(env),
+ "LMI_SoftwareJob" : LMI_SoftwareJob(env),
+ "LMI_SoftwareInstallationJob" : LMI_SoftwareJob(env),
+ "LMI_SoftwareVerificationJob" : LMI_SoftwareJob(env),
+ "LMI_SoftwareInstCreation" : LMI_SoftwareJob(env),
+ "LMI_SoftwareInstModification" : LMI_SoftwareJob(env),
+ "LMI_SoftwareMethodResult" : LMI_SoftwareMethodResult(env),
+ "LMI_AffectedSoftwareJobElement" : LMI_AffectedSoftwareJobElement(env),
+ "LMI_AssociatedSoftwareJobMethodResult" : \
+ LMI_AssociatedSoftwareJobMethodResult(env),
+ "LMI_OwningSoftwareJobElement" : LMI_OwningSoftwareJobElement(env),
+ "LMI_SoftwareIdentityFileCheck" : LMI_SoftwareIdentityFileCheck(env),
+ "LMI_SoftwareIdentityChecks" : LMI_SoftwareIdentityChecks(env)
+ }
+
+ # Initialization of indication manager -- running in separate thread as
+ # daemon. That means it does not have to be cleaned up.
+ im = IndicationManager.get_instance(
+ env, "Software", "root/cimv2", queue=Queue())
+ JobManager.register_filters("LMI_SoftwareInstallationJob", im)
+ JobManager.register_filters("LMI_SoftwareVerificationJob", im)
+
+ return providers
+
+def authorize_filter(env, fltr, class_name, op, owner):
+ """
+ CIMOM callback.
+
+ It asks us to verify whether this filter is allowed.
+
+ :param fltr: (``String``) Contains the filter that must be authorized.
+ :param class_name: (``String``) Contains the class name extracted
+ from the filter FROM clause.
+ :param op: The name of the class for which monitoring is required.
+ Only the namespace part is set if className is a process indication.
+ :param owner The owner argument is the destination owner.
+ """
+ IndicationManager.get_instance().authorize_filter(
+ env, fltr, class_name, op, owner)
+
+def activate_filter(env, fltr, class_name, class_path, first_activation):
+ """
+ CIMOM callback.
+
+ It ask us to begin monitoring a resource. The function shall begin
+ monitoring the resource according to the filter express only.
+
+ :param fltr: (``String``) The filter argument contains the filter
+ specification for this subscription to become active.
+ :param class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param class_path: (``CIMInstanceName``) The name of the class for which
+ monitoring is required. Only the namespace part is set if eventType
+ is a process indication.
+ :param first_activation: (``bool``) Set to true if this is the first
+ filter for className.
+ """
+ IndicationManager.get_instance().activate_filter(
+ env, fltr, class_name, class_path, first_activation)
+
+def deactivate_filter(env, fltr, class_name, class_path, last_activation):
+ """
+ CIMOM callback.
+
+ Informs us that monitoring using this filter should stop.
+
+ :param fltr: (``String``) The filter argument contains the filter
+ specification for this subscription to become active.
+ :param class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param class_path: (``CIMInstanceName``) class_path The name of the class
+ for which monitoring is required. Only the namespace part is set
+ if className is a process indication.
+ :last_activation: (``bool``) Set to true if this is the last filter for
+ className.
+ """
+ IndicationManager.get_instance().deactivate_filter(
+ env, fltr, class_name, class_path, last_activation)
+
+def enable_indications(env):
+ """
+ CIMOM callback.
+
+ Tells us that indications can now be generated. The MB is now prepared
+ to process indications. The function is normally called by the MB after
+ having done its intialization and processing of persistent subscription
+ requests.
+ """
+ IndicationManager.get_instance().enable_indications(env)
+
+def disable_indications(env):
+ """
+ CIMOM callback.
+
+ Tells us that we should stop generating indications. MB will not accept any
+ indications until enabled again. The function is normally called when the
+ MB is shutting down indication services either temporarily or permanently.
+ """
+ IndicationManager.get_instance().disable_indications(env)
+
+def can_unload(_env):
+ """
+ Says, whether providers can be unloaded.
+ """
+ return True
+
+def shutdown(_env):
+ """
+ Release resources upon cleanup.
+ """
+ if YumDB.isInstantiated():
+ YumDB.get_instance().clean_up()
diff --git a/src/software/lmi/software/core/AffectedSoftwareJobElement.py b/src/software/lmi/software/core/AffectedSoftwareJobElement.py
new file mode 100644
index 0000000..90014eb
--- /dev/null
+++ b/src/software/lmi/software/core/AffectedSoftwareJobElement.py
@@ -0,0 +1,264 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 AffectedSoftwareJobElement provider.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+from lmi.software import util
+from lmi.software.core import ComputerSystem
+from lmi.software.core import Identity
+from lmi.software.core import IdentityFileCheck
+from lmi.software.core import Job
+from lmi.software.core import SystemCollection
+from lmi.software.yumdb import jobs
+from lmi.software.yumdb import PackageInfo
+
+class Values(object):
+ class ElementEffects(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Exclusive_Use = pywbem.Uint16(2)
+ Performance_Impact = pywbem.Uint16(3)
+ Element_Integrity = pywbem.Uint16(4)
+ Create = pywbem.Uint16(5)
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Exclusive Use',
+ 3: 'Performance Impact',
+ 4: 'Element Integrity',
+ 5: 'Create'
+ }
+
+@cmpi_logging.trace_function
+def check_path(env, op):
+ """
+ Checks, whether object path is valid.
+
+ Return internal object representing job and object path of affected element
+ as a pair: ``(job, affected)``.
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be a CIMInstanceName")
+ ch = env.get_cimom_handle()
+
+ job = op['AffectingElement'] = Job.object_path2job(op['AffectingElement'])
+ affected = op['AffectedElement']
+ if ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SoftwareIdentity'):
+ pkg_info = Identity.object_path2pkg(affected, kind='all')
+ if isinstance(job, jobs.YumSpecificPackageJob):
+ if isinstance(job.pkg, PackageInfo):
+ if pkg_info != job.pkg:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "AffectedElement does not match job's package:"
+ " \"%s\" != \"%s\"." % (pkg_info, job.pkg))
+ else:
+ flt = pkg_info.key_props
+ flt.pop('repoid', None)
+ if util.nevra2filter(job.pkg) != flt:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "AffectedElement does not match job's package:"
+ " \"%s\" != \"%s\"." % (pkg_info, job.pkg))
+ affected = Identity.pkg2model(pkg_info)
+ elif isinstance(job, jobs.YumInstallPackageFromURI):
+ if job.state == job.COMPLETED:
+ affected = Identity.pkg2model(job.result_data)
+ else:
+ # TODO: this should be somehow obtained from downloaded
+ # package before installation
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No SoftwareIdentity is associated to given job.")
+ else:
+ cmpi_logging.logger.error("Unsupported async job: %s", job)
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No associated SoftwareIdentity.")
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SystemSoftwareCollection'):
+ SystemCollection.check_path(env, affected, "AffectedElement")
+ affected = SystemCollection.get_path()
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='Linux_ComputerSystem'):
+ ComputerSystem.check_path(env, affected, "AffectedElement")
+ affected = ComputerSystem.get_path()
+ elif ch.is_subclass(affected.namespace, sub=affected.classname,
+ super='LMI_SoftwareIdentityFileCheck'):
+ if not isinstance(job, jobs.YumCheckPackage):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Job must point to verification job, not to: \"%s\"" % job)
+ if job.state != job.COMPLETED:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Associations to failed file checks for verification job"
+ " \"%s\" are not yet known, since job has not completed"
+ " yet." % op["AffectingElement"]["InstanceID"])
+ pkg_info, _ = job.result_data
+ file_check = IdentityFileCheck.object_path2file_check(affected)
+ if file_check.pkg_info.nevra != pkg_info.nevra:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Affected element associated to job for another package:"
+ " \"%s\" != \"%s\"." % (file_check.pkg_info, pkg_info))
+ if IdentityFileCheck.file_check_passed(file_check):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Given file check reference passed the verification.")
+ else:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Expected an instance of LMI_SoftwareIdentity,"
+ " LMI_SystemSoftwareCollection or Linux_ComputerSystem.")
+ return (job, affected)
+
+@cmpi_logging.trace_function
+def job2affected_software_identity(job):
+ """
+ @return (path of SoftwareIdentity, ElementEffects array,
+ OtherElementEffectsDescriptions array)
+ """
+ effects = [Values.ElementEffects.Other]
+ descriptions = []
+ if isinstance(job, jobs.YumSpecificPackageJob):
+ if job.state == job.COMPLETED and job.result_data:
+ if isinstance(job, jobs.YumCheckPackage):
+ # get the first item out of (pkg_info, pkg_check)
+ affected = Identity.pkg2model(job.result_data[0])
+ else:
+ affected = Identity.pkg2model(job.result_data)
+ else:
+ affected = Identity.pkg2model(job.pkg)
+ if isinstance(job, jobs.YumInstallPackage):
+ descriptions.append("Installing")
+ elif isinstance(job, jobs.YumRemovePackage):
+ descriptions.append("Removing")
+ elif isinstance(job,
+ (jobs.YumUpdatePackage, jobs.YumUpdateToPackage)):
+ descriptions.append("Updating")
+ elif isinstance(job, jobs.YumCheckPackage):
+ descriptions.append("Verifying")
+ else:
+ descriptions.append("Modifying")
+ cmpi_logging.logger.error("Unhandled job: %s", job)
+ elif isinstance(job, jobs.YumInstallPackageFromURI):
+ if job.state == job.COMPLETED:
+ affected = Identity.pkg2model(job.result_data)
+ descriptions.append("Installing")
+ else:
+ # TODO: this should be somehow obtained from from downloaded
+ # package before installation
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No SoftwareIdentity is associated to given job.")
+ else:
+ cmpi_logging.logger.error("Unsupported async job: %s", job)
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "No associated SoftwareIdentity.")
+ return (affected, effects, descriptions)
+
+@cmpi_logging.trace_function
+def fill_model_computer_system(model, job, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = ComputerSystem.get_path()
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Other]
+ description = "Modifying software collection."
+ if isinstance(job, (jobs.YumInstallPackage,
+ jobs.YumInstallPackageFromURI)):
+ description = "Installing software package to collection."
+ elif isinstance(job, jobs.YumRemovePackage):
+ description = "Removing package from software collection."
+ elif isinstance(job, (jobs.YumUpdatePackage, jobs.YumUpdateToPackage)):
+ description = "Updating software package."
+ model["OtherElementEffectsDescriptions"] = [description]
+ return model
+
+@cmpi_logging.trace_function
+def fill_model_system_collection(model, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = SystemCollection.get_path()
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Exclusive_Use]
+ model["OtherElementEffectsDescriptions"] = [
+ "Package database is locked."
+ ]
+ return model
+
+@cmpi_logging.trace_function
+def fill_model_failed_check(model, failed_check, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+
+ :param failed_check: (``CIMInstanceName``) Is on object path of failed
+ file check.
+ """
+ if not isinstance(failed_check, pywbem.CIMInstanceName):
+ raise TypeError("failed_check must be a CIMInstanceName")
+ model["AffectedElement"] = failed_check
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Other]
+ model["OtherElementEffectsDescriptions"] = [
+ "File did not pass the verification."
+ ]
+ return model
+
+@cmpi_logging.trace_function
+def generate_failed_checks(model, job, keys_only=True):
+ """
+ Generates associations between LMI_SoftwareVerificationJob and
+ LMI_SoftwareIdentityFileCheck for files that did not pass the check.
+ """
+ out_params = Job.get_verification_out_params(job)
+ if not "Failed" in out_params:
+ return
+ for failed in out_params["Failed"].value:
+ yield fill_model_failed_check(model, failed, keys_only)
+
+@cmpi_logging.trace_function
+def generate_models_from_job(job, keys_only=True, model=None):
+ """
+ Generates all associations between job and affected elements.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("pkg must be an instance of PackageInfo or nevra")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_AffectedSoftwareJobElement",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_AffectedSoftwareJobElement",
+ path=model)
+ model["AffectingElement"] = Job.job2model(job)
+ (si, element_effects, element_effects_descriptions) = \
+ job2affected_software_identity(job)
+ model["AffectedElement"] = si
+ if not keys_only:
+ model["ElementEffects"] = element_effects
+ model["OtherElementEffectsDescriptions"] = \
+ element_effects_descriptions
+ yield model
+ if not isinstance(job, jobs.YumCheckPackage):
+ fill_model_system_collection(model, keys_only=keys_only)
+ yield model
+ else: # package verification - associate to failed file checks
+ for model in generate_failed_checks(model, job, keys_only=keys_only):
+ yield model
+ fill_model_computer_system(model, job, keys_only=keys_only)
+ yield model
+
diff --git a/src/software/lmi/software/core/AssociatedInstallationServiceCapabilities.py b/src/software/lmi/software/core/AssociatedInstallationServiceCapabilities.py
new file mode 100644
index 0000000..cb1b24e
--- /dev/null
+++ b/src/software/lmi/software/core/AssociatedInstallationServiceCapabilities.py
@@ -0,0 +1,35 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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
+LMI_AssociatedInstallationServiceCapabilities.
+"""
+
+import pywbem
+
+class Values(object):
+ class Characteristics(object):
+ Default = pywbem.Uint16(2)
+ Current = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 2: 'Default',
+ 3: 'Current'
+ }
diff --git a/src/software/lmi/software/core/ComputerSystem.py b/src/software/lmi/software/core/ComputerSystem.py
new file mode 100644
index 0000000..53c0738
--- /dev/null
+++ b/src/software/lmi/software/core/ComputerSystem.py
@@ -0,0 +1,87 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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
+
+from lmi.common import cmpi_logging
+
+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
+
+@cmpi_logging.trace_function
+def check_path(env, system, prop_name):
+ """
+ Checks instance name of ComputerSystem.
+ @param system instance name
+ @param prop_name name of object path
+ """
+ if not isinstance(system, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%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
+
+@cmpi_logging.trace_function
+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.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
+
diff --git a/src/software/lmi/software/core/Error.py b/src/software/lmi/software/core/Error.py
new file mode 100644
index 0000000..d36ebb0
--- /dev/null
+++ b/src/software/lmi/software/core/Error.py
@@ -0,0 +1,457 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 class CIM_Error.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+
+class Values(object):
+ class ErrorSourceFormat(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ CIMObjectPath = pywbem.Uint16(2)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'CIMObjectPath'
+ }
+
+ class ErrorType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Communications_Error = pywbem.Uint16(2)
+ Quality_of_Service_Error = pywbem.Uint16(3)
+ Software_Error = pywbem.Uint16(4)
+ Hardware_Error = pywbem.Uint16(5)
+ Environmental_Error = pywbem.Uint16(6)
+ Security_Error = pywbem.Uint16(7)
+ Oversubscription_Error = pywbem.Uint16(8)
+ Unavailable_Resource_Error = pywbem.Uint16(9)
+ Unsupported_Operation_Error = pywbem.Uint16(10)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Communications Error',
+ 3 : 'Quality of Service Error',
+ 4 : 'Software Error',
+ 5 : 'Hardware Error',
+ 6 : 'Environmental Error',
+ 7 : 'Security Error',
+ 8 : 'Oversubscription Error',
+ 9 : 'Unavailable Resource Error',
+ 10 : 'Unsupported Operation Error'
+ }
+
+ class CIMStatusCode(object):
+ CIM_ERR_FAILED = pywbem.Uint32(1)
+ CIM_ERR_ACCESS_DENIED = pywbem.Uint32(2)
+ CIM_ERR_INVALID_NAMESPACE = pywbem.Uint32(3)
+ CIM_ERR_INVALID_PARAMETER = pywbem.Uint32(4)
+ CIM_ERR_INVALID_CLASS = pywbem.Uint32(5)
+ CIM_ERR_NOT_FOUND = pywbem.Uint32(6)
+ CIM_ERR_NOT_SUPPORTED = pywbem.Uint32(7)
+ CIM_ERR_CLASS_HAS_CHILDREN = pywbem.Uint32(8)
+ CIM_ERR_CLASS_HAS_INSTANCES = pywbem.Uint32(9)
+ CIM_ERR_INVALID_SUPERCLASS = pywbem.Uint32(10)
+ CIM_ERR_ALREADY_EXISTS = pywbem.Uint32(11)
+ CIM_ERR_NO_SUCH_PROPERTY = pywbem.Uint32(12)
+ CIM_ERR_TYPE_MISMATCH = pywbem.Uint32(13)
+ CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED = pywbem.Uint32(14)
+ CIM_ERR_INVALID_QUERY = pywbem.Uint32(15)
+ CIM_ERR_METHOD_NOT_AVAILABLE = pywbem.Uint32(16)
+ CIM_ERR_METHOD_NOT_FOUND = pywbem.Uint32(17)
+ CIM_ERR_UNEXPECTED_RESPONSE = pywbem.Uint32(18)
+ CIM_ERR_INVALID_RESPONSE_DESTINATION = pywbem.Uint32(19)
+ CIM_ERR_NAMESPACE_NOT_EMPTY = pywbem.Uint32(20)
+ CIM_ERR_INVALID_ENUMERATION_CONTEXT = pywbem.Uint32(21)
+ CIM_ERR_INVALID_OPERATION_TIMEOUT = pywbem.Uint32(22)
+ CIM_ERR_PULL_HAS_BEEN_ABANDONED = pywbem.Uint32(23)
+ CIM_ERR_PULL_CANNOT_BE_ABANDONED = pywbem.Uint32(24)
+ CIM_ERR_FILTERED_ENUMERATION_NOT_SUPPORTED = pywbem.Uint32(25)
+ CIM_ERR_CONTINUATION_ON_ERROR_NOT_SUPPORTED = pywbem.Uint32(26)
+ CIM_ERR_SERVER_LIMITS_EXCEEDED = pywbem.Uint32(27)
+ CIM_ERR_SERVER_IS_SHUTTING_DOWN = pywbem.Uint32(28)
+ CIM_ERR_QUERY_FEATURE_NOT_SUPPORTED = pywbem.Uint32(29)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 1 : 'CIM_ERR_FAILED',
+ 2 : 'CIM_ERR_ACCESS_DENIED',
+ 3 : 'CIM_ERR_INVALID_NAMESPACE',
+ 4 : 'CIM_ERR_INVALID_PARAMETER',
+ 5 : 'CIM_ERR_INVALID_CLASS',
+ 6 : 'CIM_ERR_NOT_FOUND',
+ 7 : 'CIM_ERR_NOT_SUPPORTED',
+ 8 : 'CIM_ERR_CLASS_HAS_CHILDREN',
+ 9 : 'CIM_ERR_CLASS_HAS_INSTANCES',
+ 10 : 'CIM_ERR_INVALID_SUPERCLASS',
+ 11 : 'CIM_ERR_ALREADY_EXISTS',
+ 12 : 'CIM_ERR_NO_SUCH_PROPERTY',
+ 13 : 'CIM_ERR_TYPE_MISMATCH',
+ 14 : 'CIM_ERR_QUERY_LANGUAGE_NOT_SUPPORTED',
+ 15 : 'CIM_ERR_INVALID_QUERY',
+ 16 : 'CIM_ERR_METHOD_NOT_AVAILABLE',
+ 17 : 'CIM_ERR_METHOD_NOT_FOUND',
+ 18 : 'CIM_ERR_UNEXPECTED_RESPONSE',
+ 19 : 'CIM_ERR_INVALID_RESPONSE_DESTINATION',
+ 20 : 'CIM_ERR_NAMESPACE_NOT_EMPTY',
+ 21 : 'CIM_ERR_INVALID_ENUMERATION_CONTEXT',
+ 22 : 'CIM_ERR_INVALID_OPERATION_TIMEOUT',
+ 23 : 'CIM_ERR_PULL_HAS_BEEN_ABANDONED',
+ 24 : 'CIM_ERR_PULL_CANNOT_BE_ABANDONED',
+ 25 : 'CIM_ERR_FILTERED_ENUMERATION_NOT_SUPPORTED',
+ 26 : 'CIM_ERR_CONTINUATION_ON_ERROR_NOT_SUPPORTED',
+ 27 : 'CIM_ERR_SERVER_LIMITS_EXCEEDED',
+ 28 : 'CIM_ERR_SERVER_IS_SHUTTING_DOWN',
+ 29 : 'CIM_ERR_QUERY_FEATURE_NOT_SUPPORTED'
+ }
+
+ class PerceivedSeverity(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Information = pywbem.Uint16(2)
+ Degraded_Warning = pywbem.Uint16(3)
+ Minor = pywbem.Uint16(4)
+ Major = pywbem.Uint16(5)
+ Critical = pywbem.Uint16(6)
+ Fatal_NonRecoverable = pywbem.Uint16(7)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Information',
+ 3 : 'Degraded/Warning',
+ 4 : 'Minor',
+ 5 : 'Major',
+ 6 : 'Critical',
+ 7 : 'Fatal/NonRecoverable'
+ }
+
+ class ProbableCause(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Adapter_Card_Error = pywbem.Uint16(2)
+ Application_Subsystem_Failure = pywbem.Uint16(3)
+ Bandwidth_Reduced = pywbem.Uint16(4)
+ Connection_Establishment_Error = pywbem.Uint16(5)
+ Communications_Protocol_Error = pywbem.Uint16(6)
+ Communications_Subsystem_Failure = pywbem.Uint16(7)
+ Configuration_Customization_Error = pywbem.Uint16(8)
+ Congestion = pywbem.Uint16(9)
+ Corrupt_Data = pywbem.Uint16(10)
+ CPU_Cycles_Limit_Exceeded = pywbem.Uint16(11)
+ Dataset_Modem_Error = pywbem.Uint16(12)
+ Degraded_Signal = pywbem.Uint16(13)
+ DTE_DCE_Interface_Error = pywbem.Uint16(14)
+ Enclosure_Door_Open = pywbem.Uint16(15)
+ Equipment_Malfunction = pywbem.Uint16(16)
+ Excessive_Vibration = pywbem.Uint16(17)
+ File_Format_Error = pywbem.Uint16(18)
+ Fire_Detected = pywbem.Uint16(19)
+ Flood_Detected = pywbem.Uint16(20)
+ Framing_Error = pywbem.Uint16(21)
+ HVAC_Problem = pywbem.Uint16(22)
+ Humidity_Unacceptable = pywbem.Uint16(23)
+ I_O_Device_Error = pywbem.Uint16(24)
+ Input_Device_Error = pywbem.Uint16(25)
+ LAN_Error = pywbem.Uint16(26)
+ Non_Toxic_Leak_Detected = pywbem.Uint16(27)
+ Local_Node_Transmission_Error = pywbem.Uint16(28)
+ Loss_of_Frame = pywbem.Uint16(29)
+ Loss_of_Signal = pywbem.Uint16(30)
+ Material_Supply_Exhausted = pywbem.Uint16(31)
+ Multiplexer_Problem = pywbem.Uint16(32)
+ Out_of_Memory = pywbem.Uint16(33)
+ Output_Device_Error = pywbem.Uint16(34)
+ Performance_Degraded = pywbem.Uint16(35)
+ Power_Problem = pywbem.Uint16(36)
+ Pressure_Unacceptable = pywbem.Uint16(37)
+ Processor_Problem__Internal_Machine_Error_ = pywbem.Uint16(38)
+ Pump_Failure = pywbem.Uint16(39)
+ Queue_Size_Exceeded = pywbem.Uint16(40)
+ Receive_Failure = pywbem.Uint16(41)
+ Receiver_Failure = pywbem.Uint16(42)
+ Remote_Node_Transmission_Error = pywbem.Uint16(43)
+ Resource_at_or_Nearing_Capacity = pywbem.Uint16(44)
+ Response_Time_Excessive = pywbem.Uint16(45)
+ Retransmission_Rate_Excessive = pywbem.Uint16(46)
+ Software_Error = pywbem.Uint16(47)
+ Software_Program_Abnormally_Terminated = pywbem.Uint16(48)
+ Software_Program_Error__Incorrect_Results_ = pywbem.Uint16(49)
+ Storage_Capacity_Problem = pywbem.Uint16(50)
+ Temperature_Unacceptable = pywbem.Uint16(51)
+ Threshold_Crossed = pywbem.Uint16(52)
+ Timing_Problem = pywbem.Uint16(53)
+ Toxic_Leak_Detected = pywbem.Uint16(54)
+ Transmit_Failure = pywbem.Uint16(55)
+ Transmitter_Failure = pywbem.Uint16(56)
+ Underlying_Resource_Unavailable = pywbem.Uint16(57)
+ Version_Mismatch = pywbem.Uint16(58)
+ Previous_Alert_Cleared = pywbem.Uint16(59)
+ Login_Attempts_Failed = pywbem.Uint16(60)
+ Software_Virus_Detected = pywbem.Uint16(61)
+ Hardware_Security_Breached = pywbem.Uint16(62)
+ Denial_of_Service_Detected = pywbem.Uint16(63)
+ Security_Credential_Mismatch = pywbem.Uint16(64)
+ Unauthorized_Access = pywbem.Uint16(65)
+ Alarm_Received = pywbem.Uint16(66)
+ Loss_of_Pointer = pywbem.Uint16(67)
+ Payload_Mismatch = pywbem.Uint16(68)
+ Transmission_Error = pywbem.Uint16(69)
+ Excessive_Error_Rate = pywbem.Uint16(70)
+ Trace_Problem = pywbem.Uint16(71)
+ Element_Unavailable = pywbem.Uint16(72)
+ Element_Missing = pywbem.Uint16(73)
+ Loss_of_Multi_Frame = pywbem.Uint16(74)
+ Broadcast_Channel_Failure = pywbem.Uint16(75)
+ Invalid_Message_Received = pywbem.Uint16(76)
+ Routing_Failure = pywbem.Uint16(77)
+ Backplane_Failure = pywbem.Uint16(78)
+ Identifier_Duplication = pywbem.Uint16(79)
+ Protection_Path_Failure = pywbem.Uint16(80)
+ Sync_Loss_or_Mismatch = pywbem.Uint16(81)
+ Terminal_Problem = pywbem.Uint16(82)
+ Real_Time_Clock_Failure = pywbem.Uint16(83)
+ Antenna_Failure = pywbem.Uint16(84)
+ Battery_Charging_Failure = pywbem.Uint16(85)
+ Disk_Failure = pywbem.Uint16(86)
+ Frequency_Hopping_Failure = pywbem.Uint16(87)
+ Loss_of_Redundancy = pywbem.Uint16(88)
+ Power_Supply_Failure = pywbem.Uint16(89)
+ Signal_Quality_Problem = pywbem.Uint16(90)
+ Battery_Discharging = pywbem.Uint16(91)
+ Battery_Failure = pywbem.Uint16(92)
+ Commercial_Power_Problem = pywbem.Uint16(93)
+ Fan_Failure = pywbem.Uint16(94)
+ Engine_Failure = pywbem.Uint16(95)
+ Sensor_Failure = pywbem.Uint16(96)
+ Fuse_Failure = pywbem.Uint16(97)
+ Generator_Failure = pywbem.Uint16(98)
+ Low_Battery = pywbem.Uint16(99)
+ Low_Fuel = pywbem.Uint16(100)
+ Low_Water = pywbem.Uint16(101)
+ Explosive_Gas = pywbem.Uint16(102)
+ High_Winds = pywbem.Uint16(103)
+ Ice_Buildup = pywbem.Uint16(104)
+ Smoke = pywbem.Uint16(105)
+ Memory_Mismatch = pywbem.Uint16(106)
+ Out_of_CPU_Cycles = pywbem.Uint16(107)
+ Software_Environment_Problem = pywbem.Uint16(108)
+ Software_Download_Failure = pywbem.Uint16(109)
+ Element_Reinitialized = pywbem.Uint16(110)
+ Timeout = pywbem.Uint16(111)
+ Logging_Problems = pywbem.Uint16(112)
+ Leak_Detected = pywbem.Uint16(113)
+ Protection_Mechanism_Failure = pywbem.Uint16(114)
+ Protecting_Resource_Failure = pywbem.Uint16(115)
+ Database_Inconsistency = pywbem.Uint16(116)
+ Authentication_Failure = pywbem.Uint16(117)
+ Breach_of_Confidentiality = pywbem.Uint16(118)
+ Cable_Tamper = pywbem.Uint16(119)
+ Delayed_Information = pywbem.Uint16(120)
+ Duplicate_Information = pywbem.Uint16(121)
+ Information_Missing = pywbem.Uint16(122)
+ Information_Modification = pywbem.Uint16(123)
+ Information_Out_of_Sequence = pywbem.Uint16(124)
+ Key_Expired = pywbem.Uint16(125)
+ Non_Repudiation_Failure = pywbem.Uint16(126)
+ Out_of_Hours_Activity = pywbem.Uint16(127)
+ Out_of_Service = pywbem.Uint16(128)
+ Procedural_Error = pywbem.Uint16(129)
+ Unexpected_Information = pywbem.Uint16(130)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Adapter/Card Error',
+ 3 : 'Application Subsystem Failure',
+ 4 : 'Bandwidth Reduced',
+ 5 : 'Connection Establishment Error',
+ 6 : 'Communications Protocol Error',
+ 7 : 'Communications Subsystem Failure',
+ 8 : 'Configuration/Customization Error',
+ 9 : 'Congestion',
+ 10 : 'Corrupt Data',
+ 11 : 'CPU Cycles Limit Exceeded',
+ 12 : 'Dataset/Modem Error',
+ 13 : 'Degraded Signal',
+ 14 : 'DTE-DCE Interface Error',
+ 15 : 'Enclosure Door Open',
+ 16 : 'Equipment Malfunction',
+ 17 : 'Excessive Vibration',
+ 18 : 'File Format Error',
+ 19 : 'Fire Detected',
+ 20 : 'Flood Detected',
+ 21 : 'Framing Error',
+ 22 : 'HVAC Problem',
+ 23 : 'Humidity Unacceptable',
+ 24 : 'I/O Device Error',
+ 25 : 'Input Device Error',
+ 26 : 'LAN Error',
+ 27 : 'Non-Toxic Leak Detected',
+ 28 : 'Local Node Transmission Error',
+ 29 : 'Loss of Frame',
+ 30 : 'Loss of Signal',
+ 31 : 'Material Supply Exhausted',
+ 32 : 'Multiplexer Problem',
+ 33 : 'Out of Memory',
+ 34 : 'Output Device Error',
+ 35 : 'Performance Degraded',
+ 36 : 'Power Problem',
+ 37 : 'Pressure Unacceptable',
+ 38 : 'Processor Problem (Internal Machine Error)',
+ 39 : 'Pump Failure',
+ 40 : 'Queue Size Exceeded',
+ 41 : 'Receive Failure',
+ 42 : 'Receiver Failure',
+ 43 : 'Remote Node Transmission Error',
+ 44 : 'Resource at or Nearing Capacity',
+ 45 : 'Response Time Excessive',
+ 46 : 'Retransmission Rate Excessive',
+ 47 : 'Software Error',
+ 48 : 'Software Program Abnormally Terminated',
+ 49 : 'Software Program Error (Incorrect Results)',
+ 50 : 'Storage Capacity Problem',
+ 51 : 'Temperature Unacceptable',
+ 52 : 'Threshold Crossed',
+ 53 : 'Timing Problem',
+ 54 : 'Toxic Leak Detected',
+ 55 : 'Transmit Failure',
+ 56 : 'Transmitter Failure',
+ 57 : 'Underlying Resource Unavailable',
+ 58 : 'Version Mismatch',
+ 59 : 'Previous Alert Cleared',
+ 60 : 'Login Attempts Failed',
+ 61 : 'Software Virus Detected',
+ 62 : 'Hardware Security Breached',
+ 63 : 'Denial of Service Detected',
+ 64 : 'Security Credential Mismatch',
+ 65 : 'Unauthorized Access',
+ 66 : 'Alarm Received',
+ 67 : 'Loss of Pointer',
+ 68 : 'Payload Mismatch',
+ 69 : 'Transmission Error',
+ 70 : 'Excessive Error Rate',
+ 71 : 'Trace Problem',
+ 72 : 'Element Unavailable',
+ 73 : 'Element Missing',
+ 74 : 'Loss of Multi Frame',
+ 75 : 'Broadcast Channel Failure',
+ 76 : 'Invalid Message Received',
+ 77 : 'Routing Failure',
+ 78 : 'Backplane Failure',
+ 79 : 'Identifier Duplication',
+ 80 : 'Protection Path Failure',
+ 81 : 'Sync Loss or Mismatch',
+ 82 : 'Terminal Problem',
+ 83 : 'Real Time Clock Failure',
+ 84 : 'Antenna Failure',
+ 85 : 'Battery Charging Failure',
+ 86 : 'Disk Failure',
+ 87 : 'Frequency Hopping Failure',
+ 88 : 'Loss of Redundancy',
+ 89 : 'Power Supply Failure',
+ 90 : 'Signal Quality Problem',
+ 91 : 'Battery Discharging',
+ 92 : 'Battery Failure',
+ 93 : 'Commercial Power Problem',
+ 94 : 'Fan Failure',
+ 95 : 'Engine Failure',
+ 96 : 'Sensor Failure',
+ 97 : 'Fuse Failure',
+ 98 : 'Generator Failure',
+ 99 : 'Low Battery',
+ 100 : 'Low Fuel',
+ 101 : 'Low Water',
+ 102 : 'Explosive Gas',
+ 103 : 'High Winds',
+ 104 : 'Ice Buildup',
+ 105 : 'Smoke',
+ 106 : 'Memory Mismatch',
+ 107 : 'Out of CPU Cycles',
+ 108 : 'Software Environment Problem',
+ 109 : 'Software Download Failure',
+ 110 : 'Element Reinitialized',
+ 111 : 'Timeout',
+ 112 : 'Logging Problems',
+ 113 : 'Leak Detected',
+ 114 : 'Protection Mechanism Failure',
+ 115 : 'Protecting Resource Failure',
+ 116 : 'Database Inconsistency',
+ 117 : 'Authentication Failure',
+ 118 : 'Breach of Confidentiality',
+ 119 : 'Cable Tamper',
+ 120 : 'Delayed Information',
+ 121 : 'Duplicate Information',
+ 122 : 'Information Missing',
+ 123 : 'Information Modification',
+ 124 : 'Information Out of Sequence',
+ 125 : 'Key Expired',
+ 126 : 'Non-Repudiation Failure',
+ 127 : 'Out of Hours Activity',
+ 128 : 'Out of Service',
+ 129 : 'Procedural Error',
+ 130 : 'Unexpected Information'
+ }
+
+@cmpi_logging.trace_function
+def make_instance(
+ status_code=Values.CIMStatusCode.CIM_ERR_FAILED,
+ error_type=Values.ErrorType.Software_Error,
+ probable_cause=Values.ErrorType.Unknown,
+ error_source=None,
+ status_code_description=None,
+ message=None,
+ message_arguments=None,
+ probable_cause_description=None,
+ recommended_actions=None):
+ for param in ('status_code', 'probable_cause', 'error_type'):
+ if not isinstance(locals()[param], (int, long)):
+ raise TypeError('%s must be integer'%param)
+ if error_source is None:
+ # this is a cyclic dependency
+ from lmi.software.core import InstallationService
+ error_source = InstallationService.get_path()
+ if not isinstance(error_source, pywbem.CIMInstanceName):
+ raise TypeError('error_source must be a CIMInstanceName')
+
+ inst = pywbem.CIMInstance(classname="CIM_Error",
+ path=pywbem.CIMInstanceName(classname="CIM_Error",
+ namespace="root/cimv2"))
+ inst['CIMStatusCode'] = pywbem.Uint32(status_code)
+ if status_code_description is not None:
+ inst['CIMStatusCodeDescription'] = status_code_description
+ inst['ErrorType'] = pywbem.Uint16(error_type)
+ inst['ErrorSource'] = str(error_source)
+ inst['ErrorSourceFormat'] = Values.ErrorSourceFormat.CIMObjectPath
+ if message is not None:
+ inst['Message'] = message
+ if message_arguments is not None:
+ inst['MessageArguments'] = message_arguments
+ inst['ProbableCause'] = pywbem.Uint16(probable_cause)
+ if probable_cause_description is not None:
+ inst['ProbableCauseDescription'] = probable_cause_description
+ if recommended_actions is not None:
+ inst['RecommendedActions'] = recommended_actions
+ return inst
+
diff --git a/src/software/lmi/software/core/Identity.py b/src/software/lmi/software/core/Identity.py
new file mode 100644
index 0000000..2df43a9
--- /dev/null
+++ b/src/software/lmi/software/core/Identity.py
@@ -0,0 +1,270 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 LMI_SoftwareIdentity provider.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+from lmi.software import util
+from lmi.software.yumdb import PackageInfo, YumDB
+
+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 Classifications(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Driver = pywbem.Uint16(2)
+ Configuration_Software = pywbem.Uint16(3)
+ Application_Software = pywbem.Uint16(4)
+ Instrumentation = pywbem.Uint16(5)
+ Firmware_BIOS = pywbem.Uint16(6)
+ Diagnostic_Software = pywbem.Uint16(7)
+ Operating_System = pywbem.Uint16(8)
+ Middleware = pywbem.Uint16(9)
+ Firmware = pywbem.Uint16(10)
+ BIOS_FCode = pywbem.Uint16(11)
+ Support_Service_Pack = pywbem.Uint16(12)
+ Software_Bundle = pywbem.Uint16(13)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+
+ class ExtendedResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Not_Applicable = pywbem.Uint16(2)
+ Linux_RPM = pywbem.Uint16(3)
+ HP_UX_Depot = pywbem.Uint16(4)
+ Windows_MSI = pywbem.Uint16(5)
+ Solaris_Package = pywbem.Uint16(6)
+ Macintosh_Disk_Image = pywbem.Uint16(7)
+ Debian_linux_Package = pywbem.Uint16(8)
+ VMware_vSphere_Installation_Bundle = pywbem.Uint16(9)
+ VMware_Software_Bulletin = pywbem.Uint16(10)
+ HP_Smart_Component = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+ 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 PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+
+
+@cmpi_logging.trace_function
+def object_path2nevra(op, with_epoch='NOT_ZERO'):
+ """Get nevra out of object path. Also checks for validity."""
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if (not "InstanceID" in op or not op['InstanceID']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, "Wrong keys.")
+ instid = op['InstanceID']
+ if not instid.lower().startswith("lmi:lmi_softwareidentity:"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "InstanceID must start with LMI:LMI_SoftwareIdentity: prefix.")
+ instid = instid[len("LMI:LMI_SoftwareIdentity:"):]
+ match = util.RE_NEVRA_OPT_EPOCH.match(instid)
+ if not match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Wrong InstanceID. Expected valid nevra"
+ ' (name-[epoch:]version-release.arch): "%s".' %
+ instid)
+ epoch = match.group('epoch')
+ if not epoch:
+ epoch = "0"
+ return util.make_nevra(
+ match.group('name'),
+ epoch,
+ match.group('version'),
+ match.group('release'),
+ match.group('arch'), with_epoch=with_epoch)
+
+@cmpi_logging.trace_function
+def object_path2pkg(op,
+ kind='installed',
+ include_repos=None,
+ exclude_repos=None,
+ repoid=None,
+ return_all=False):
+ """
+ @param op must contain precise information of package,
+ otherwise an error is raised
+ @param kind one of yumdb.jobs.YumGetPackageList.SUPPORTED_KINDS
+ says, where to look for given package
+ @param repoid if not None, specifies repoid filter on package;
+ note, that this does not make sure, that repoid will be enabled.
+ @param return_all if True, return list of matching packages as returned
+ by YumDB.filter_packages(), otherwise single package is returned
+ """
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+
+ pkglist = YumDB.get_instance().filter_packages(kind,
+ allow_duplicates=kind not in ('installed', 'avail_reinst'),
+ include_repos=include_repos,
+ exclude_repos=exclude_repos,
+ repoid=repoid,
+ nevra=object_path2nevra(op, 'ALWAYS'))
+ if return_all is True:
+ return pkglist
+ if len(pkglist) > 0:
+ return pkglist[0]
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No matching package found for InstanceID=\"%s\".' %
+ op["InstanceID"])
+
+@cmpi_logging.trace_function
+def pkg2model(pkg, keys_only=True, model=None):
+ """
+ @param pkg can either be an instance of PackageInfo or nevra as string
+ @param model if not None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ if not isinstance(pkg, (basestring, PackageInfo)):
+ raise TypeError("pkg must be an instance of PackageInfo or nevra")
+ if isinstance(pkg, basestring) and not keys_only:
+ raise ValueError("can not create instance out of nevra")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareIdentity",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareIdentity", path=model)
+ nevra = pkg if isinstance(pkg, basestring) else pkg.nevra
+ model['InstanceID'] = 'LMI:LMI_SoftwareIdentity:'+nevra
+ if not keys_only:
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ model['Caption'] = pkg.summary
+ model['Classifications'] = [pywbem.Uint16(0)]
+ model['Description'] = pkg.description
+ model['ElementName'] = pkg.nevra
+ if pkg.installed:
+ model['InstallDate'] = pywbem.CIMDateTime(pkg.install_time)
+ else:
+ model['InstallDate'] = pywbem.CIMProperty(
+ 'InstallDate', None, type='datetime')
+ model['IsEntity'] = True
+ model['Name'] = pkg.name
+ try:
+ model["Epoch"] = pywbem.Uint32(int(pkg.epoch))
+ except ValueError:
+ cmpi_logging.logger.error('Could not convert epoch "%s"'
+ ' to integer for package \"%s\"!' % (pkg.epoch, pkg))
+ model["Epoch"] = pywbem.CIMProperty('Epoch', None, type='uint32')
+ model['Version'] = pkg.version
+ model['Release'] = pkg.release
+ model['Architecture'] = pkg.arch
+ model['TargetTypes'] = ['rpm', 'yum']
+ model['VersionString'] = pkg.evra
+ return model
+
diff --git a/src/software/lmi/software/core/IdentityFileCheck.py b/src/software/lmi/software/core/IdentityFileCheck.py
new file mode 100644
index 0000000..665d822
--- /dev/null
+++ b/src/software/lmi/software/core/IdentityFileCheck.py
@@ -0,0 +1,965 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Just a common functionality related to SoftwareIdentityFileCheck provider.
+"""
+
+import hashlib
+import os
+import pywbem
+import stat
+import yum
+
+from lmi.common import cmpi_logging
+from lmi.software import util
+from lmi.software.yumdb import errors
+from lmi.software.yumdb import jobs
+from lmi.software.yumdb import packageinfo
+from lmi.software.yumdb import packagecheck
+from lmi.software.yumdb import YumDB
+
+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)
+ Microsoft_Windows_Server_2011 = pywbem.Uint16(111)
+ Microsoft_Windows_Server_2012 = pywbem.Uint16(113)
+ Microsoft_Windows_8 = pywbem.Uint16(114)
+ Microsoft_Windows_8_64_bit = pywbem.Uint16(115)
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'MACOS',
+ 3 : 'ATTUNIX',
+ 4 : 'DGUX',
+ 5 : 'DECNT',
+ 6 : 'Tru64 UNIX',
+ 7 : 'OpenVMS',
+ 8 : 'HPUX',
+ 9 : 'AIX',
+ 10 : 'MVS',
+ 11 : 'OS400',
+ 12 : 'OS/2',
+ 13 : 'JavaVM',
+ 14 : 'MSDOS',
+ 15 : 'WIN3x',
+ 16 : 'WIN95',
+ 17 : 'WIN98',
+ 18 : 'WINNT',
+ 19 : 'WINCE',
+ 20 : 'NCR3000',
+ 21 : 'NetWare',
+ 22 : 'OSF',
+ 23 : 'DC/OS',
+ 24 : 'Reliant UNIX',
+ 25 : 'SCO UnixWare',
+ 26 : 'SCO OpenServer',
+ 27 : 'Sequent',
+ 28 : 'IRIX',
+ 29 : 'Solaris',
+ 30 : 'SunOS',
+ 31 : 'U6000',
+ 32 : 'ASERIES',
+ 33 : 'HP NonStop OS',
+ 34 : 'HP NonStop OSS',
+ 35 : 'BS2000',
+ 36 : 'LINUX',
+ 37 : 'Lynx',
+ 38 : 'XENIX',
+ 39 : 'VM',
+ 40 : 'Interactive UNIX',
+ 41 : 'BSDUNIX',
+ 42 : 'FreeBSD',
+ 43 : 'NetBSD',
+ 44 : 'GNU Hurd',
+ 45 : 'OS9',
+ 46 : 'MACH Kernel',
+ 47 : 'Inferno',
+ 48 : 'QNX',
+ 49 : 'EPOC',
+ 50 : 'IxWorks',
+ 51 : 'VxWorks',
+ 52 : 'MiNT',
+ 53 : 'BeOS',
+ 54 : 'HP MPE',
+ 55 : 'NextStep',
+ 56 : 'PalmPilot',
+ 57 : 'Rhapsody',
+ 58 : 'Windows 2000',
+ 59 : 'Dedicated',
+ 60 : 'OS/390',
+ 61 : 'VSE',
+ 62 : 'TPF',
+ 63 : 'Windows (R) Me',
+ 64 : 'Caldera Open UNIX',
+ 65 : 'OpenBSD',
+ 66 : 'Not Applicable',
+ 67 : 'Windows XP',
+ 68 : 'z/OS',
+ 69 : 'Microsoft Windows Server 2003',
+ 70 : 'Microsoft Windows Server 2003 64-Bit',
+ 71 : 'Windows XP 64-Bit',
+ 72 : 'Windows XP Embedded',
+ 73 : 'Windows Vista',
+ 74 : 'Windows Vista 64-Bit',
+ 75 : 'Windows Embedded for Point of Service',
+ 76 : 'Microsoft Windows Server 2008',
+ 77 : 'Microsoft Windows Server 2008 64-Bit',
+ 78 : 'FreeBSD 64-Bit',
+ 79 : 'RedHat Enterprise Linux',
+ 80 : 'RedHat Enterprise Linux 64-Bit',
+ 81 : 'Solaris 64-Bit',
+ 82 : 'SUSE',
+ 83 : 'SUSE 64-Bit',
+ 84 : 'SLES',
+ 85 : 'SLES 64-Bit',
+ 86 : 'Novell OES',
+ 87 : 'Novell Linux Desktop',
+ 88 : 'Sun Java Desktop System',
+ 89 : 'Mandriva',
+ 90 : 'Mandriva 64-Bit',
+ 91 : 'TurboLinux',
+ 92 : 'TurboLinux 64-Bit',
+ 93 : 'Ubuntu',
+ 94 : 'Ubuntu 64-Bit',
+ 95 : 'Debian',
+ 96 : 'Debian 64-Bit',
+ 97 : 'Linux 2.4.x',
+ 98 : 'Linux 2.4.x 64-Bit',
+ 99 : 'Linux 2.6.x',
+ 100 : 'Linux 2.6.x 64-Bit',
+ 101 : 'Linux 64-Bit',
+ 102 : 'Other 64-Bit',
+ 103 : 'Microsoft Windows Server 2008 R2',
+ 104 : 'VMware ESXi',
+ 105 : 'Microsoft Windows 7',
+ 106 : 'CentOS 32-bit',
+ 107 : 'CentOS 64-bit',
+ 108 : 'Oracle Enterprise Linux 32-bit',
+ 109 : 'Oracle Enterprise Linux 64-bit',
+ 110 : 'eComStation 32-bitx',
+ 111 : 'Microsoft Windows Server 2011',
+ 113 : 'Microsoft Windows Server 2012',
+ 114 : 'Microsoft Windows 8',
+ 115 : 'Microsoft Windows 8 64-bit'
+ }
+
+ class ChecksumType(object):
+ UNKNOWN = pywbem.Uint16(0)
+ MD5 = pywbem.Uint16(1)
+ SHA_1 = pywbem.Uint16(2)
+ RIPE_MD_160 = pywbem.Uint16(3)
+ SHA256 = pywbem.Uint16(8)
+ SHA384 = pywbem.Uint16(9)
+ SHA512 = pywbem.Uint16(10)
+ SHA224 = pywbem.Uint16(11)
+ _reverse_map = {
+ 0 : 'UNKNOWN',
+ 1 : 'MD5',
+ 2 : 'SHA-1',
+ 3 : 'RIPE-MD/160',
+ 8 : 'SHA256',
+ 9 : 'SHA384',
+ 10 : 'SHA512',
+ 11 : 'SHA224'
+ }
+
+ 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)
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'File',
+ 2 : 'Directory',
+ 3 : 'Symlink',
+ 4 : 'FIFO',
+ 5 : 'Character Device',
+ 6 : 'Block Device'
+ }
+
+ class FileModeFlags(object):
+ Execute_Other = pywbem.Uint8(0)
+ Write_Other = pywbem.Uint8(1)
+ Read_Other = pywbem.Uint8(2)
+ Execute_Group = pywbem.Uint8(3)
+ Write_Group = pywbem.Uint8(4)
+ Read_Group = pywbem.Uint8(5)
+ Execute_User = pywbem.Uint8(6)
+ Write_User = pywbem.Uint8(7)
+ Read_User = pywbem.Uint8(8)
+ Sticky = pywbem.Uint8(9)
+ SGID = pywbem.Uint8(10)
+ SUID = pywbem.Uint8(11)
+ _reverse_map = {
+ 0 : 'Execute Other',
+ 1 : 'Write Other',
+ 2 : 'Read Other',
+ 3 : 'Execute Group',
+ 4 : 'Write Group',
+ 5 : 'Read Group',
+ 6 : 'Execute User',
+ 7 : 'Write User',
+ 8 : 'Read User',
+ 9 : 'Sticky',
+ 10 : 'SGID',
+ 11 : 'SUID'
+ }
+
+ class FileModeFlagsOriginal(object):
+ Execute_Other = pywbem.Uint8(0)
+ Write_Other = pywbem.Uint8(1)
+ Read_Other = pywbem.Uint8(2)
+ Execute_Group = pywbem.Uint8(3)
+ Write_Group = pywbem.Uint8(4)
+ Read_Group = pywbem.Uint8(5)
+ Execute_User = pywbem.Uint8(6)
+ Write_User = pywbem.Uint8(7)
+ Read_User = pywbem.Uint8(8)
+ Sticky = pywbem.Uint8(9)
+ SGID = pywbem.Uint8(10)
+ SUID = pywbem.Uint8(11)
+ _reverse_map = {
+ 0 : 'Execute Other',
+ 1 : 'Write Other',
+ 2 : 'Read Other',
+ 3 : 'Execute Group',
+ 4 : 'Write Group',
+ 5 : 'Read Group',
+ 6 : 'Execute User',
+ 7 : 'Write User',
+ 8 : 'Read User',
+ 9 : 'Sticky',
+ 10 : 'SGID',
+ 11 : 'SUID'
+ }
+
+ class FailedFlags(object):
+ Existence = pywbem.Uint16(0)
+ FileSize = pywbem.Uint16(1)
+ FileMode = pywbem.Uint16(2)
+ Checksum = pywbem.Uint16(3)
+ Device_Number = pywbem.Uint16(4)
+ LinkTarget = pywbem.Uint16(5)
+ UserID = pywbem.Uint16(6)
+ GroupID = pywbem.Uint16(7)
+ Last_Modification_Time = pywbem.Uint16(8)
+ _reverse_map = {
+ 0 : 'Existence',
+ 1 : 'FileSize',
+ 2 : 'FileMode',
+ 3 : 'Checksum',
+ 4 : 'Device Number',
+ 5 : 'LinkTarget',
+ 6 : 'UserID',
+ 7 : 'GroupID',
+ 8 : 'Last Modification Time'
+ }
+
+ class SoftwareElementState(object):
+ Deployable = pywbem.Uint16(0)
+ Installable = pywbem.Uint16(1)
+ Executable = pywbem.Uint16(2)
+ Running = pywbem.Uint16(3)
+ _reverse_map = {
+ 0 : 'Deployable',
+ 1 : 'Installable',
+ 2 : 'Executable',
+ 3 : 'Running'
+ }
+
+ class FileTypeOriginal(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)
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'File',
+ 2 : 'Directory',
+ 3 : 'Symlink',
+ 4 : 'FIFO',
+ 5 : 'Character Device',
+ 6 : 'Block Device'
+ }
+
+ class Invoke(object):
+ """
+ This is not an enumeration from mof file, it just gives human
+ names to numeric values.
+ """
+ Satisfied = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Not_Satisfied = pywbem.Uint32(2)
+
+class FileCheck(object):
+ """
+ File attribute storage keeping both RPM database metadata and info
+ from local filesystem for single file. Most attributes are loaded at the
+ time of first request and cached for later use.
+
+ Most of properties return tuple:
+ ``(installed, original)``
+ where
+ ``installed`` is a state of property of installed file, while
+ ``original`` is the value stored in RPM database.
+
+ If any of those values are None, it means that property could not be
+ retrieved or it's not applicable to file's type.
+ """
+
+ def __init__(self, pkg_info, pkg_file, checksum_type):
+ """
+ :param pkg_file: (``yumdb.packagecheck.PackageFile``) Metadata for
+ file loaded from rpm database.
+ :param checksum_type: (``int``) Indentificator of checksum algorithm
+ corresponding to values in yum.constants.RPM_CHECKSUM_TYPES.
+ """
+ if not isinstance(pkg_info, packageinfo.PackageInfo):
+ raise TypeError("pkg_info must be an instance of PackageInfo")
+ if not isinstance(pkg_file, packagecheck.PackageFile):
+ raise TypeError("pkg_file must be an instance of PackageFile")
+ self._pkg_info = pkg_info
+ self._pkg_file = pkg_file
+ self._exists = None
+ # (md5 checksum, rpm-type checksum) - this is made on first request
+ self._checksums = None
+ self._checksum_type = checksum_type
+ self._link_target = None
+ # stores result of os.stat() call
+ self._stat = None
+
+ @property
+ def pkg_info(self):
+ """:rtype: (``PackageInfo``)"""
+ return self._pkg_info
+
+ @property
+ def pkg_file(self):
+ """:rtype: (``PackageFile``)"""
+ return self._pkg_file
+
+ @property
+ def path(self):
+ """Return absolute file path."""
+ return self._pkg_file.path
+
+ @property
+ def exists(self):
+ """Return true if file exists on local file system."""
+ if self._exists is None:
+ self._exists = os.path.lexists(self._pkg_file.path)
+ return self._exists
+
+ @property
+ def stat(self):
+ """
+ Return cached stat object - result of ``os.lstat()``.
+ None is returned if file does not exist.
+ """
+ if self._stat is None and self.exists:
+ self._stat = os.lstat(self.path)
+ return self._stat
+
+ @property
+ def file_type(self):
+ """
+ Return identifiers of file type for installed and rpm file.
+ If the installed file does not exist, None is returned on
+ its position.
+ :rtype: (``tuple``) Pair of (installed, original).
+ """
+ fm = self.file_mode[0]
+ if fm is None:
+ ft = None
+ elif stat.S_ISLNK(fm):
+ ft = packagecheck.FILE_TYPE_SYMLINK
+ elif stat.S_ISDIR(fm):
+ ft = packagecheck.FILE_TYPE_DIRECTORY
+ elif stat.S_ISFIFO(fm):
+ ft = packagecheck.FILE_TYPE_FIFO
+ elif stat.S_ISCHR(fm):
+ ft = packagecheck.FILE_TYPE_CHARACTER_DEVICE
+ elif stat.S_ISBLK(fm):
+ ft = packagecheck.FILE_TYPE_BLOCK_DEVICE
+ elif stat.S_ISREG(fm):
+ ft = packagecheck.FILE_TYPE_FILE
+ else:
+ ft = packagecheck.FILE_TYPE_UNKNOWN
+ return (ft, self._pkg_file.file_type)
+
+ @property
+ def md5_checksum(self):
+ """
+ Return md5 checksum string of installed file.
+ This is computed on first access either to this or ``file_checksum``
+ property and cached for later access.
+ :rtype: (``str``)
+ """
+ if self._checksums is None:
+ self.make_checksums()
+ return self._checksums[0] if self._checksums else None
+
+ @property
+ def file_checksum_type(self):
+ """
+ Return identifier of hash algorithm used in rpm package.
+ :rtype: (``int``)
+ """
+ return self._checksum_type
+
+ @property
+ def file_checksum(self):
+ """
+ Return checksum strings for installed and rpm file.
+ This is computed on first access either to this or ``md5_checksum``
+ property and cached for later access.
+ :rtype: (``tuple``) Pair of (installed, original).
+ """
+ if self._checksums is None:
+ self.make_checksums()
+ return ( self._checksums[1] if self._checksums else None
+ , self._pkg_file.checksum)
+
+ @property
+ def file_size(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('size'), self._pkg_file.size)
+
+ @property
+ def file_mode(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('mode'), self._pkg_file.mode)
+
+ @property
+ def device(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('dev'), self._pkg_file.device)
+
+ @property
+ def link_target(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ if ( self.file_type[0] == packagecheck.FILE_TYPE_SYMLINK
+ and self._link_target is None):
+ self._link_target = os.readlink(self.path)
+ return (self._link_target, self._pkg_file.link_target)
+
+ @property
+ def user_id(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('uid'), self._pkg_file.uid)
+
+ @property
+ def group_id(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('gid'), self._pkg_file.gid)
+
+ @property
+ def last_modification_time(self):
+ """:rtype: (``tuple``) Pair of (installed, original)."""
+ return (self.getstat('mtime'), self._pkg_file.mtime)
+
+ def getstat(self, attr):
+ """
+ Get attribute from stat object for given file.
+ :param attr: (``str``) Will be prefixed with "st_".
+ """
+ if self.stat is not None:
+ return getattr(self.stat, "st_"+attr)
+
+ def make_checksums(self):
+ """
+ Compute checksums for installed file and cache them.
+ :rtype: (``tuple``) MD5 and rpm-type checksums for given file
+ as a pair.
+ """
+ if self.file_type[0] == packagecheck.FILE_TYPE_FILE:
+ self._checksums = compute_checksums(
+ self._checksum_type, self.path)
+ else:
+ self._checksums = None
+ return self._checksums
+
+
+@cmpi_logging.trace_function
+def hashfile(afile, hashers, blocksize=65536):
+ """
+ Generic function computing multiple hashes from single file at once.
+
+ :param hashers: (``list``) A list of hash objects.
+ :rtype: (``list``) List of digest strings (in hex format) for each
+ given hash object 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_path):
+ """
+ Compute file checksums in one go.
+
+ :param checksum_type: (``int`) Selected hash algorithm to compute second
+ checksum (the one used for rpm package). Correct value can be
+ obtained from ``yum.constants.RPM_CHECKSUM_TYPES``.
+ :param file_path: (``str``) Absolute path of regular file to hash.
+ :rtype (md5sum, checksum) Tuple of strings containing hex represention
+ of computed checksums.
+
+ 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.
+ """
+ cmpi_logging.logger.debug(
+ "checksuming file %s", file_path)
+ hashers = [hashlib.md5()] #pylint: disable=E1101
+ if checksum_type != packagecheck.CHECKSUMTYPE_STR2NUM["md5"]:
+ hashers.append(checksumtype_num2hash(checksum_type)())
+ 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 checksumtype_num2hash(csumt):
+ """
+ Get checksum function for rpm hash identifier.
+
+ :param csumt: (``int``) Checksum type as a number obtained from package.
+ :rtype: (``function``) Hash function object corresponding to csumt.
+ """
+ return getattr(hashlib, yum.constants.RPM_CHECKSUM_TYPES[csumt])
+
+@cmpi_logging.trace_function
+def mode2pywbem_flags(mode):
+ """
+ Utility for creation of value of ``FileModeFlags`` property out of file's
+ raw mode.
+
+ :param mode: (``int``) File's mode. Retrieved from os.lstat().
+ If None, file does not exist.
+ :rtype: (``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
+
+def get_existing_check_from_job(check_id):
+ """
+ ``CheckID`` of ``LMI_SoftwareIdentityFileCheck`` refers to asynchronous job
+ that caused creation of this file check. Let's parse it, get the job out
+ of YumWorker and ask it for results so they don't have to be computed
+ again.
+
+ :param check_id: (``str``) Value of ``CheckID`` property.
+ :rtype: (``tuple``) Pair of (pkg_info, pkg_check).
+ ``None`` if job does not exists anymore.
+ """
+ try:
+ match = util.RE_INSTANCE_ID.match(check_id)
+ if not match:
+ raise ValueError(check_id)
+ if ( not match.group('clsname').lower()
+ == "LMI_SoftwareVerificationJob".lower()):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "CheckID does not contain supported job class name: "
+ " %s != %s" % (match.group('clsname'),
+ "LMI_SoftwareVerificationJob".lower()))
+ job_id = int(match.group('id'))
+ job = YumDB.get_instance().get_job(check_id)
+ if not isinstance(job,
+ (jobs.YumCheckPackage, jobs.YumCheckPackageFile)):
+ cmpi_logging.logger.error(
+ 'CheckID="%s" of LMI_SoftwareIdentityFileCheck refers'
+ ' to job %s.', check_id, job)
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "CheckID refers to wrong job.")
+ return job.result_data
+
+ except errors.JobNotFound:
+ # allow this - job could already be deleted
+ cmpi_logging.logger.warn("could not find YumCheckPackage or"
+ " YumCheckPackageFile job with id \"%d\"" % job_id)
+ except ValueError:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Could not parse \"CheckID\" key property: \"%s\"." % check_id)
+
+@cmpi_logging.trace_function
+def object_path2file_check(objpath):
+ """
+ @return (package_info, package_check, file_name)
+ """
+ if not isinstance(objpath, pywbem.CIMInstanceName):
+ raise TypeError("objpath must be instance of CIMInstanceName, "
+ "not \"%s\"" % objpath.__class__.__name__)
+
+ for key in ('Name', 'SoftwareElementID', 'Version',
+ 'SoftwareElementState', 'TargetOperatingSystem'):
+ if not key in objpath or not objpath[key]:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing key property \"%s\"." % key)
+ version_match = util.RE_EVRA.match(objpath['Version'])
+ if not version_match:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Failed to parse \"Version\" property for valid EVRA: "%s".' %
+ objpath['Version'])
+ pkg_fltr = util.nevra2filter(objpath["SoftwareElementID"])
+ if any( pkg_fltr[attr] != version_match.group(attr)
+ for attr in ('epoch', 'version', 'release', 'arch')):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'Inconsistent \"Version\" and \"SoftwareElementID\" key'
+ ' properties.')
+ if ( objpath['SoftwareElementState']
+ != Values.SoftwareElementState.Executable):
+ 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.")
+
+ ydb = YumDB.get_instance()
+ pkg_check = None
+ if ( objpath["CheckID"].lower()
+ != "LMI:LMI_SoftwareIdentityFileCheck".lower()):
+ check = get_existing_check_from_job(objpath["CheckID"])
+ if check is not None:
+ pkg_info, pkg_check = check
+ # else - job does not exist (could be deleted)
+
+ if pkg_check is None:
+ # let the YumWorker check the rest
+ try:
+ pkg_info, pkg_check = ydb.check_package_file(
+ objpath["SoftwareElementID"], objpath["Name"])
+ except errors.PackageNotFound:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Could not find package matching SoftwareElementID \"%s\"" %
+ objpath["SoftwareElementID"])
+ except errors.FileNotFound as exc:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, exc.args[1])
+
+ # last check for file path
+ if objpath["Name"] not in pkg_check:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'File \"%s\" not found in package \"%s\".' %
+ objpath["SoftwareElementID"])
+ return FileCheck(pkg_info, pkg_check[objpath["Name"]],
+ pkg_check.file_checksum_type)
+
+@cmpi_logging.trace_function
+def test_file(pkg_info, checksum_type, pkg_file):
+ """
+ :param checksum_type: (``int``) Identifier of checksum algorithm used
+ in rpm package. It's one of keys of
+ ``yum.constants.RPM_CHECKSUM_TYPES`` dictionary.
+ :rtype: (``FileCheck``)
+ """
+ for var, cls in (
+ ("pkg_info", packageinfo.PackageInfo),
+ ("pkg_file", packagecheck.PackageFile)):
+ if not isinstance(locals()[var], cls):
+ raise TypeError("%s must be an instance of %s, not %s" % (
+ var, cls.__name__, pkg_file.__class__.__name__))
+ return FileCheck(pkg_info, pkg_file, checksum_type)
+
+@cmpi_logging.trace_function
+def _file_check2failed_flags(file_check):
+ """
+ Makes value of ``FailedFlags`` property from ``FileCheck`` instance.
+
+ :param file_check: (``FileCheck``)
+ :rtype: (``pywbem.CIMProperty``) Property ``FailedFlags`` value.
+ """
+ if not isinstance(file_check, FileCheck):
+ raise TypeError("file_check must be an instance of FileCheck")
+
+ if not file_check.exists:
+ return [Values.FailedFlags.Existence]
+ flags = set()
+ fails_on = lambda attr: (
+ getattr(file_check, attr)[0] != getattr(file_check, attr)[1])
+ if fails_on('file_type'):
+ flags.add(Values.FailedFlags.FileMode)
+
+ # file type specific checks
+ if file_check.file_type[1] == packagecheck.FILE_TYPE_FILE:
+ if fails_on('file_size'):
+ flags.add(Values.FailedFlags.FileSize)
+ if fails_on('last_modification_time'):
+ flags.add(Values.FailedFlags.Last_Modification_Time)
+ if fails_on('file_checksum'):
+ flags.add(Values.FailedFlags.Checksum)
+ elif file_check.file_type[1] in (
+ packagecheck.FILE_TYPE_CHARACTER_DEVICE,
+ packagecheck.FILE_TYPE_BLOCK_DEVICE):
+ if fails_on('device'):
+ flags.add(Values.FailedFlags.Device_Number)
+ elif file_check.file_type[1] == packagecheck.FILE_TYPE_SYMLINK:
+ if fails_on('link_target'):
+ flags.add(Values.FailedFlags.LinkTarget)
+
+ # generic checks
+ if ( fails_on('file_mode')
+ # do not check permissions for symlinks
+ and file_check.file_type[1] != packagecheck.FILE_TYPE_SYMLINK):
+ flags.add(Values.FailedFlags.FileMode)
+ if fails_on('user_id'):
+ flags.add(Values.FailedFlags.UserID)
+ if fails_on('group_id'):
+ flags.add(Values.FailedFlags.GroupID)
+
+ return list(flags)
+
+@cmpi_logging.trace_function
+def file_check_passed(file_check):
+ """
+ Verify single file and return true if it passes.
+ """
+ return len(_file_check2failed_flags(file_check)) == 0
+
+@cmpi_logging.trace_function
+def _fill_non_key_values(model, file_check):
+ """
+ Fills a non key values into instance of SoftwareIdentityFileCheck.
+
+ :param model: (``CIMInstance``) Model, that will be filled with
+ all supported non-key properties.
+ """
+ set_prop = lambda name, kwargs: \
+ model.__setitem__(name, pywbem.CIMProperty(name, **kwargs))
+
+ set_prop("CheckMode", dict(type='boolean', value=True))
+ model["ChecksumType"] = pywbem.Uint16(file_check.file_checksum_type)
+
+ set_prop("FailedFlags", dict(type='uint16', is_array=True,
+ value=_file_check2failed_flags(file_check)))
+ for mattr, fattr, kwargs, convert in (
+ ('LastModificationTime', 'last_modification_time',
+ {'type':'uint64'}, pywbem.Uint64),
+ ('FileType' , 'file_type' ,{'type':'uint16'}, pywbem.Uint16),
+ ('UserID' , 'user_id' ,{'type':'uint32'}, pywbem.Uint32),
+ ('GroupID' , 'group_id' ,{'type':'uint32'}, pywbem.Uint32),
+ ('FileMode' , 'file_mode' ,{'type':'uint32'}, pywbem.Uint32),
+ ('FileSize' , 'file_size' ,{'type':'uint64'}, pywbem.Uint64),
+ ('LinkTarget' , 'link_target' ,{'type':'string'}, str),
+ ('FileChecksum', 'file_checksum',{'type':'string'}, str)):
+ installed, orig = getattr(file_check, fattr)
+ kwargs['value'] = None if orig is None else convert(orig)
+ set_prop(mattr+"Original", kwargs)
+ kwargs['value'] = None if installed is None else convert(installed)
+ set_prop(mattr, kwargs)
+ model['FileName'] = os.path.basename(file_check.path)
+ for suf in ('', 'Original'):
+ set_prop('FileModeFlags'+suf, dict(type='uint8', is_array=True,
+ value=mode2pywbem_flags(file_check.file_mode[1])))
+ set_prop("FileExists", dict(type='boolean', value=file_check.exists))
+ set_prop('MD5Checksum', dict(type='string',
+ value=file_check.md5_checksum))
+
+@cmpi_logging.trace_function
+def file_check2model(file_check, keys_only=True, model=None, job=None):
+ """
+ :param file_check: (``FileCheck``) File check information to transform
+ to cim instance.
+ :param keys_only: (``bool``) Whether to fill non-key properties.
+ :param model: (``CIMInstance`` | ``CIMInstanceName``) If given,
+ it will be used as a template. Only property values will be set.
+ This instance will be returned on completion.
+ :param job: (``YumAsyncJob``) If this is a consequence of asynchronous
+ job operation, the instance of job should be supplied so that
+ ``CheckID`` property can be filled.
+ :rtype: (``CIMInstance`` | ``CIMInstanceName``) Object of
+ LMI_SoftwareIdentityFileCheck with filled desired values.
+ """
+ if job is not None and not isinstance(job, jobs.YumCheckPackage):
+ raise TypeError("job must be instance of jobs.YumCheckPackage")
+ if not isinstance(file_check, FileCheck):
+ raise TypeError("file_check must be an instance of FileCheck")
+
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareIdentityFileCheck",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareIdentityFileCheck",
+ path=model)
+
+ if not keys_only:
+ model.path.update( #pylint: disable=E1103
+ {k: None for k in ("Name", "SoftwareElementID",
+ "SoftwareElementState", "TargetOperatingSystem", "Version",
+ "CheckID")})
+
+ model['Name'] = file_check.path
+ model['SoftwareElementID'] = file_check.pkg_info.nevra
+ model['SoftwareElementState'] = Values.SoftwareElementState.Executable
+ model['TargetOperatingSystem'] = pywbem.Uint16(
+ util.get_target_operating_system()[0])
+ model['Version'] = file_check.pkg_info.evra
+ model['CheckID'] = ("LMI:LMI_SoftwareVerificationJob:%d" % job.jobid
+ if job is not None else "LMI:%s" % model.classname)
+ if not keys_only:
+ _fill_non_key_values(model, file_check)
+ return model
+
diff --git a/src/software/lmi/software/core/IdentityResource.py b/src/software/lmi/software/core/IdentityResource.py
new file mode 100644
index 0000000..228015b
--- /dev/null
+++ b/src/software/lmi/software/core/IdentityResource.py
@@ -0,0 +1,650 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 LMI_SoftwareIdentityResource provider.
+"""
+
+import pywbem
+import socket
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem
+from lmi.software.yumdb import YumDB
+from lmi.software.yumdb.repository import Repository
+
+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..
+ _reverse_map = {
+ 0 : 'Not Available',
+ 1 : 'No Additional Information',
+ 2 : 'Stressed',
+ 3 : 'Predictive Failure',
+ 4 : 'Non-Recoverable Error',
+ 5 : 'Supporting Entity in Error'
+ }
+
+ class RequestedState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 5 : 'No Change',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Deferred',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset',
+ 12 : 'Not Applicable'
+ }
+
+ 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
+ _reverse_map = {
+ 0 : 'Unknown',
+ 5 : 'OK',
+ 10 : 'Degraded/Warning',
+ 15 : 'Minor failure',
+ 20 : 'Major failure',
+ 25 : 'Critical failure',
+ 30 : 'Non-recoverable error'
+ }
+
+ class TransitioningToState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 5 : 'No Change',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Defer',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset',
+ 12 : 'Not Applicable'
+ }
+
+ class ResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Installer_and_Payload = pywbem.Uint16(2)
+ Installer = pywbem.Uint16(3)
+ Payload = pywbem.Uint16(4)
+ Installability_checker = pywbem.Uint16(5)
+ Security_Advisory = pywbem.Uint16(6)
+ Engineering_Advisory = pywbem.Uint16(7)
+ Technical_release_notes = pywbem.Uint16(9)
+ Change_notification = pywbem.Uint16(10)
+ Whitepaper = pywbem.Uint16(11)
+ Marketing_Documentation = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Installer and Payload',
+ 3 : 'Installer',
+ 4 : 'Payload',
+ 5 : 'Installability checker',
+ 6 : 'Security Advisory',
+ 7 : 'Engineering Advisory',
+ 9 : 'Technical release notes',
+ 10 : 'Change notification',
+ 11 : 'Whitepaper',
+ 12 : 'Marketing Documentation'
+ }
+
+ class EnabledState(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shutting_Down = pywbem.Uint16(4)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ In_Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Starting = pywbem.Uint16(10)
+ # DMTF_Reserved = 11..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shutting Down',
+ 5 : 'Not Applicable',
+ 6 : 'Enabled but Offline',
+ 7 : 'In Test',
+ 8 : 'Deferred',
+ 9 : 'Quiesce',
+ 10 : 'Starting'
+ }
+
+ class ExtendedResourceType(object):
+ Unknown = pywbem.Uint16(0)
+ Not_Applicable = pywbem.Uint16(2)
+ Linux_RPM = pywbem.Uint16(3)
+ HP_UX_Depot = pywbem.Uint16(4)
+ Windows_MSI = pywbem.Uint16(5)
+ Solaris_Package = pywbem.Uint16(6)
+ Macintosh_Disk_Image = pywbem.Uint16(7)
+ Debian_linux_Package = pywbem.Uint16(8)
+ HP_Smart_Component = pywbem.Uint16(11)
+ # Vendor_Reserved = 101..200
+ HTML = pywbem.Uint16(201)
+ PDF = pywbem.Uint16(202)
+ Text_File = pywbem.Uint16(203)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+ _reverse_map = {
+ 0 : 'Unknown',
+ 2 : 'Not Applicable',
+ 3 : 'Linux RPM',
+ 4 : 'HP-UX Depot',
+ 5 : 'Windows MSI',
+ 6 : 'Solaris Package',
+ 7 : 'Macintosh Disk Image',
+ 8 : 'Debian linux Package',
+ 201 : 'HTML',
+ 202 : 'PDF',
+ 11 : 'HP Smart Component',
+ 203 : 'Text File'
+ }
+
+ class AccessContext(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Default_Gateway = pywbem.Uint16(2)
+ DNS_Server = pywbem.Uint16(3)
+ SNMP_Trap_Destination = pywbem.Uint16(4)
+ MPLS_Tunnel_Destination = pywbem.Uint16(5)
+ DHCP_Server = pywbem.Uint16(6)
+ SMTP_Server = pywbem.Uint16(7)
+ LDAP_Server = pywbem.Uint16(8)
+ Network_Time_Protocol__NTP__Server = pywbem.Uint16(9)
+ Management_Service = pywbem.Uint16(10)
+ internet_Storage_Name_Service__iSNS_ = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Default Gateway',
+ 3 : 'DNS Server',
+ 4 : 'SNMP Trap Destination',
+ 5 : 'MPLS Tunnel Destination',
+ 6 : 'DHCP Server',
+ 7 : 'SMTP Server',
+ 8 : 'LDAP Server',
+ 9 : 'Network Time Protocol (NTP) Server',
+ 10 : 'Management Service',
+ 11 : 'internet Storage Name Service (iSNS)'
+ }
+
+ class AvailableRequestedStates(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 4 : 'Shut Down',
+ 6 : 'Offline',
+ 7 : 'Test',
+ 8 : 'Defer',
+ 9 : 'Quiesce',
+ 10 : 'Reboot',
+ 11 : 'Reset'
+ }
+
+ 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 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..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Not Available',
+ 2 : 'Communication OK',
+ 3 : 'Lost Communication',
+ 4 : 'No Contact'
+ }
+
+ 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..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'OK',
+ 3 : 'Degraded',
+ 4 : 'Stressed',
+ 5 : 'Predictive Failure',
+ 6 : 'Error',
+ 7 : 'Non-Recoverable Error',
+ 8 : 'Starting',
+ 9 : 'Stopping',
+ 10 : 'Stopped',
+ 11 : 'In Service',
+ 12 : 'No Contact',
+ 13 : 'Lost Communication',
+ 14 : 'Aborted',
+ 15 : 'Dormant',
+ 16 : 'Supporting Entity in Error',
+ 17 : 'Completed',
+ 18 : 'Power Mode',
+ 19 : 'Relocating'
+ }
+
+ 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..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Not Available',
+ 2 : 'Servicing',
+ 3 : 'Starting',
+ 4 : 'Stopping',
+ 5 : 'Stopped',
+ 6 : 'Aborted',
+ 7 : 'Dormant',
+ 8 : 'Completed',
+ 9 : 'Migrating',
+ 10 : 'Emigrating',
+ 11 : 'Immigrating',
+ 12 : 'Snapshotting',
+ 13 : 'Shutting Down',
+ 14 : 'In Test',
+ 15 : 'Transitioning',
+ 16 : 'In Service'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_or_Unspecified_Error = pywbem.Uint32(2)
+ Cannot_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+
+ class EnabledDefault(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ No_Default = pywbem.Uint16(7)
+ Quiesce = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2 : 'Enabled',
+ 3 : 'Disabled',
+ 5 : 'Not Applicable',
+ 6 : 'Enabled but Offline',
+ 7 : 'No Default',
+ 9 : 'Quiesce'
+ }
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'OK',
+ 2 : 'Degraded',
+ 3 : 'Error'
+ }
+
+ class InfoFormat(object):
+ Other = pywbem.Uint16(1)
+ Host_Name = pywbem.Uint16(2)
+ IPv4_Address = pywbem.Uint16(3)
+ IPv6_Address = pywbem.Uint16(4)
+ IPX_Address = pywbem.Uint16(5)
+ DECnet_Address = pywbem.Uint16(6)
+ SNA_Address = pywbem.Uint16(7)
+ Autonomous_System_Number = pywbem.Uint16(8)
+ MPLS_Label = pywbem.Uint16(9)
+ IPv4_Subnet_Address = pywbem.Uint16(10)
+ IPv6_Subnet_Address = pywbem.Uint16(11)
+ IPv4_Address_Range = pywbem.Uint16(12)
+ IPv6_Address_Range = pywbem.Uint16(13)
+ Dial_String = pywbem.Uint16(100)
+ Ethernet_Address = pywbem.Uint16(101)
+ Token_Ring_Address = pywbem.Uint16(102)
+ ATM_Address = pywbem.Uint16(103)
+ Frame_Relay_Address = pywbem.Uint16(104)
+ URL = pywbem.Uint16(200)
+ FQDN = pywbem.Uint16(201)
+ User_FQDN = pywbem.Uint16(202)
+ DER_ASN1_DN = pywbem.Uint16(203)
+ DER_ASN1_GN = pywbem.Uint16(204)
+ Key_ID = pywbem.Uint16(205)
+ Parameterized_URL = pywbem.Uint16(206)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 1 : 'Other',
+ 2 : 'Host Name',
+ 3 : 'IPv4 Address',
+ 4 : 'IPv6 Address',
+ 5 : 'IPX Address',
+ 6 : 'DECnet Address',
+ 7 : 'SNA Address',
+ 8 : 'Autonomous System Number',
+ 9 : 'MPLS Label',
+ 10 : 'IPv4 Subnet Address',
+ 11 : 'IPv6 Subnet Address',
+ 12 : 'IPv4 Address Range',
+ 13 : 'IPv6 Address Range',
+ 200 : 'URL',
+ 201 : 'FQDN',
+ 202 : 'User FQDN',
+ 203 : 'DER ASN1 DN',
+ 204 : 'DER ASN1 GN',
+ 205 : 'Key ID',
+ 206 : 'Parameterized URL',
+ 100 : 'Dial String',
+ 101 : 'Ethernet Address',
+ 102 : 'Token Ring Address',
+ 103 : 'ATM Address',
+ 104 : 'Frame Relay Address'
+ }
+
+@cmpi_logging.trace_function
+def object_path2repo(env, op, kind='enabled'):
+ """
+ @param op must contain precise information of repository,
+ otherwise an error is raised
+ """
+ if not isinstance(kind, basestring):
+ raise TypeError("kind must be a string")
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if ( (not "CreationClassName" in op or not op['CreationClassName'])
+ or (not "Name" in op or not op["Name"])
+ or ( not "SystemCreationClassName" in op
+ or not op["SystemCreationClassName"])
+ or (not "SystemName" in op or not op["SystemName"])):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, "Wrong keys.")
+ if op["SystemName"] != socket.gethostname():
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'SystemName "%s" does not match "%s".' % (
+ op["SystemName"], socket.gethostname()))
+ ch = env.get_cimom_handle()
+ if not ch.is_subclass("root/cimv2",
+ sub=op["CreationClassName"],
+ super="CIM_SoftwareIdentityResource"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'CreationClassName \"%s\" must be a subclass of "%s".' % (
+ op["CreationClassName"], "CIM_SoftwareIdentityResource"))
+ if not ch.is_subclass("root/cimv2",
+ sub=op["SystemCreationClassName"],
+ super="CIM_ComputerSystem"):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ 'SystemCreationClassName of \"%s\" must be a subclass of "%s".'
+ % (op["CreationClassName"], "CIM_SoftwareIdentityResource"))
+ repos = YumDB.get_instance().filter_repositories(kind, repoid=op["Name"])
+ if len(repos) < 1:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No matching repository found for Name=\"%s\".' % op["Name"])
+ return repos[0]
+
+@cmpi_logging.trace_function
+def _fill_non_keys(repo, model):
+ """
+ Fills into the model of instance all non-key properties.
+ """
+ model['AccessContext'] = Values.AccessContext.Other
+ if repo.mirror_list:
+ access_info = repo.mirror_list
+ elif repo.base_urls:
+ if len(repo.base_urls) > 0:
+ if len(repo.base_urls) > 1:
+ cmpi_logging.logger.warn(
+ 'multiple base urls found for repository "%s", selecting'
+ ' the last one', repo)
+ access_info = repo.base_urls[-1]
+ else:
+ cmpi_logging.logger.error(
+ 'no base url found for repository "%s"' % repo)
+ access_info = pywbem.CIMProperty('AccessInfo',
+ None, type='string')
+ model["AccessInfo"] = access_info
+ model['AvailableRequestedStates'] = [
+ Values.AvailableRequestedStates.Enabled,
+ Values.AvailableRequestedStates.Disabled]
+ model['Caption'] = repo.name
+ model['Cost'] = pywbem.Sint32(repo.cost)
+ model['Description'] = "[%s] - %s for %s architecture with cost %d" % (
+ repo.repoid, repo.name, repo.basearch, repo.cost)
+ model['ElementName'] = repo.repoid
+ model['EnabledDefault'] = Values.EnabledDefault.Not_Applicable
+ if repo.enabled:
+ model['EnabledState'] = Values.EnabledState.Enabled
+ else:
+ model['EnabledState'] = Values.EnabledState.Disabled
+ model['ExtendedResourceType'] = Values.ExtendedResourceType.Linux_RPM
+ model['GPGCheck'] = repo.gpg_check
+ if repo.ready:
+ model['HealthState'] = Values.HealthState.OK
+ else:
+ model['HealthState'] = Values.HealthState.Major_failure
+ if repo.revision is not None:
+ model["Generation"] = pywbem.Uint64(repo.revision)
+ else:
+ model['Generation'] = pywbem.CIMProperty('Generation',
+ None, type='uint64')
+ model['InfoFormat'] = Values.InfoFormat.URL
+ model['InstanceID'] = 'LMI:LMI_SoftwareIdentityResource:' + repo.repoid
+ if repo.mirror_list:
+ model["MirrorList"] = repo.mirror_list
+ else:
+ model['MirrorList'] = pywbem.CIMProperty('MirrorList',
+ None, type='string')
+ model['OperationalStatus'] = [ Values.OperationalStatus.OK
+ if repo.ready else Values.OperationalStatus.Error]
+ model['OtherAccessContext'] = "YUM package repository"
+ model['OtherResourceType'] = "RPM Software Package"
+ # this would need to populateSack, which is expensive
+ #model["PackageCount"] = pywbem.Uint32(repo.pkg_count)
+ if repo.ready:
+ model['PrimaryStatus'] = Values.PrimaryStatus.OK
+ else:
+ model['PrimaryStatus'] = Values.PrimaryStatus.Error
+ model['RepoGPGCheck'] = repo.repo_gpg_check
+ if repo.enabled:
+ model['RequestedState'] = Values.RequestedState.Enabled
+ else:
+ model['RequestedState'] = Values.RequestedState.Disabled
+ model['ResourceType'] = Values.ResourceType.Other
+ model['StatusDescriptions'] = [ "Ready" if repo.ready else "Not Ready" ]
+ model['TimeOfLastStateChange'] = pywbem.CIMDateTime(repo.last_edit)
+ if repo.last_update is not None:
+ model['TimeOfLastUpdate'] = pywbem.CIMDateTime(repo.last_update)
+ else:
+ model['TimeOfLastUpdate'] = pywbem.CIMProperty('TimeOfLastUpdate',
+ None, type='datetime')
+ model['TransitioningToState'] = Values.TransitioningToState.Not_Applicable
+
+@cmpi_logging.trace_function
+def repo2model(repo, keys_only=True, model=None):
+ """
+ @param model if not None, will be filled with data, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created
+ """
+ if not isinstance(repo, Repository):
+ raise TypeError("pkg must be an instance of Repository")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareIdentityResource",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance(
+ "LMI_SoftwareIdentityResource", 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('CreationClassName', "LMI_SoftwareIdentityResource")
+ _set_key("Name", repo.repoid)
+ _set_key("SystemCreationClassName", "Linux_ComputerSystem")
+ _set_key("SystemName", ComputerSystem.get_path()["Name"])
+ if not keys_only:
+ _fill_non_keys(repo, model)
+
+ return model
+
diff --git a/src/software/lmi/software/core/InstMethodCall.py b/src/software/lmi/software/core/InstMethodCall.py
new file mode 100644
index 0000000..82b7b1e
--- /dev/null
+++ b/src/software/lmi/software/core/InstMethodCall.py
@@ -0,0 +1,144 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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/>.
+
+"""
+CIM values for enumeration types of CIM_InstMethodCall indication class.
+"""
+
+import pywbem
+import socket
+
+from lmi.common import cmpi_logging
+from lmi.software.core import Job
+from lmi.software.core import InstallationService
+from lmi.software.yumdb import jobs, errors
+
+class Values(object):
+ class ReturnValueType(object):
+ boolean = pywbem.Uint16(2)
+ string = pywbem.Uint16(3)
+ char16 = pywbem.Uint16(4)
+ uint8 = pywbem.Uint16(5)
+ sint8 = pywbem.Uint16(6)
+ uint16 = pywbem.Uint16(7)
+ sint16 = pywbem.Uint16(8)
+ uint32 = pywbem.Uint16(9)
+ sint32 = pywbem.Uint16(10)
+ uint64 = pywbem.Uint16(11)
+ sint64 = pywbem.Uint16(12)
+ datetime = pywbem.Uint16(13)
+ real32 = pywbem.Uint16(14)
+ real64 = pywbem.Uint16(15)
+ reference = pywbem.Uint16(16)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 2: 'boolean',
+ 3: 'string',
+ 4: 'char16',
+ 5: 'uint8',
+ 6: 'sint8',
+ 7: 'uint16',
+ 8: 'sint16',
+ 9: 'uint32',
+ 10: 'sint32',
+ 11: 'uint64',
+ 12: 'sint64',
+ 13: 'datetime',
+ 14: 'real32',
+ 15: 'real64',
+ 16: 'reference'
+ }
+
+ class PerceivedSeverity(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Information = pywbem.Uint16(2)
+ Degraded_Warning = pywbem.Uint16(3)
+ Minor = pywbem.Uint16(4)
+ Major = pywbem.Uint16(5)
+ Critical = pywbem.Uint16(6)
+ Fatal_NonRecoverable = pywbem.Uint16(7)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Information',
+ 3: 'Degraded/Warning',
+ 4: 'Minor',
+ 5: 'Major',
+ 6: 'Critical',
+ 7: 'Fatal/NonRecoverable'
+ }
+
+@cmpi_logging.trace_function
+def job2model(job, pre=True):
+ """
+ Create post or pre indication instance used by clients to subscribe
+ to job's state changes.
+
+ :param job: (``YumJob``) Instance of job created as a result of method
+ invocation.
+ :param pre: (``bool``) says, whether to make pre or post indication
+ instance.
+ :rtype CIMInstance of CIM_InstMethodCall.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be a YumJob")
+ if not pre and job.state == job.NEW or job.state == job.RUNNING:
+ raise ValueError("job must be finished to make a post indication"
+ " instance")
+ path = pywbem.CIMInstanceName(
+ classname="CIM_InstMethodCall",
+ host=socket.gethostname(),
+ namespace="root/cimv2")
+ inst = pywbem.CIMInstance(classname="CIM_InstMethodCall", path=path)
+ src_instance = Job.job2model(job, keys_only=False)
+ inst['SourceInstance'] = pywbem.CIMProperty("SourceInstance",
+ type="instance", value=src_instance)
+ inst['SourceInstanceModelPath'] = \
+ str(src_instance.path) #pylint: disable=E1103
+ inst['MethodName'] = Job.JOB_METHOD_NAMES[
+ job.metadata["method"]]
+ inst['MethodParameters'] = Job.make_method_params(
+ job, '__MethodParameters', True, not pre)
+ inst['PreCall'] = pre
+
+ if not pre:
+ inst["Error"] = pywbem.CIMProperty("Error", type="instance",
+ is_array=True, value=[])
+ error = Job.job2error(job)
+ if error is not None:
+ inst["Error"].append(error)
+ inst["ReturnValueType"] = Values.ReturnValueType.uint32
+ if job.state == job.COMPLETED:
+ inst["ReturnValue"] = str(InstallationService. \
+ Values.InstallFromURI.Job_Completed_with_No_Error)
+ elif job.state == job.EXCEPTION:
+ if issubclass(job.result_data[0], (
+ errors.InvalidNevra, errors.InvalidURI,
+ errors.PackageNotFound)):
+ inst["ReturnValue"] = str(InstallationService.Values. \
+ InstallFromURI.Invalid_Parameter)
+ else:
+ inst["ReturnValue"] = str(InstallationService.Values. \
+ InstallFromURI.Failed)
+ else:
+ inst["ReturnValue"] = str(InstallationService.Values. \
+ InstallFromURI.Unspecified_Error)
+ return inst
+
diff --git a/src/software/lmi/software/core/InstallationService.py b/src/software/lmi/software/core/InstallationService.py
new file mode 100644
index 0000000..557f913
--- /dev/null
+++ b/src/software/lmi/software/core/InstallationService.py
@@ -0,0 +1,826 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 LMI_SoftwareInstallationService
+provider.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem
+from lmi.software.core import Identity
+from lmi.software.core import Job
+from lmi.software.core import SystemCollection
+from lmi.software.yumdb import errors
+from lmi.software.yumdb import YumDB
+
+JOB_METHOD_SRC_PARAM_NAMES = ["URI", "Source", "Image", "Source"]
+
+class InstallationError(Exception):
+ """This exception shall be raised upon any error within
+ install_or_remove_package() function.
+ """
+ def __init__(self, return_code, description):
+ Exception.__init__(self, return_code, description)
+ @property
+ def return_code(self):
+ """@return return code of CIM method"""
+ return self.args[0]
+ @property
+ def description(self):
+ """@return description of error"""
+ return self.args[1]
+
+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..
+ _reverse_map = {
+ 0: 'Not Available',
+ 1: 'No Additional Information',
+ 2: 'Stressed',
+ 3: 'Predictive Failure',
+ 4: 'Non-Recoverable Error',
+ 5: 'Supporting Entity in Error'
+ }
+
+ class RequestedState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 5: 'No Change',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Deferred',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset',
+ 12: 'Not Applicable'
+ }
+
+ 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
+ _reverse_map = {
+ 0: 'Unknown',
+ 5: 'OK',
+ 10: 'Degraded/Warning',
+ 15: 'Minor failure',
+ 20: 'Major failure',
+ 25: 'Critical failure',
+ 30: 'Non-recoverable error'
+ }
+
+ class InstallFromURI(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ URI_not_accessible = pywbem.Uint32(4107)
+ # Method_Reserved = 4108..32767
+ # Vendor_Specific = 32768..65535
+ class InstallOptions(object):
+ Defer_target_system_reset = pywbem.Uint16(2)
+ Force_installation = pywbem.Uint16(3)
+ Install = pywbem.Uint16(4)
+ Update = pywbem.Uint16(5)
+ Repair = pywbem.Uint16(6)
+ Reboot = pywbem.Uint16(7)
+ Password = pywbem.Uint16(8)
+ Uninstall = pywbem.Uint16(9)
+ Log = pywbem.Uint16(10)
+ SilentMode = pywbem.Uint16(11)
+ AdministrativeMode = pywbem.Uint16(12)
+ ScheduleInstallAt = pywbem.Uint16(13)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 2 : "Defer Target/System Reset",
+ 3 : "Force Installation",
+ 4 : "Install",
+ 5 : "Update",
+ 6 : "Repair",
+ 7 : "Reboot",
+ 8 : "Password",
+ 9 : "Uninstall",
+ 10 : "Log",
+ 11 : "SilentMode",
+ 12 : "AdministrativeMode",
+ 13 : "ScheduleInstallAt",
+ }
+
+ InstallOptions.supported = {
+ InstallOptions.Install,
+ InstallOptions.Update,
+ InstallOptions.Uninstall,
+ InstallOptions.Force_installation,
+ InstallOptions.Repair,
+ }
+
+
+ class CheckSoftwareIdentity(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Reserved = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ Cannot_add_to_Collection = pywbem.Uint32(4108)
+ Asynchronous_Job_already_in_progress = pywbem.Uint32(4109)
+ # Method_Reserved = 4110..32767
+ # Vendor_Specific = 32768..65535
+ class InstallCharacteristics(object):
+ Target_automatic_reset = pywbem.Uint16(2)
+ System_automatic_reset = pywbem.Uint16(3)
+ Separate_target_reset_Required = pywbem.Uint16(4)
+ Separate_system_reset_Required = pywbem.Uint16(5)
+ Manual_Reboot_Required = pywbem.Uint16(6)
+ No_Reboot_Required = pywbem.Uint16(7)
+ User_Intervention_recommended = pywbem.Uint16(8)
+ MAY_be_added_to_specified_Collection = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 0x7FFF..0xFFFF
+
+ class ChangeAffectedElementsAssignedSequence(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Error_Occured = pywbem.Uint32(2)
+ Busy = pywbem.Uint32(3)
+ Invalid_Reference = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = 7..32767
+ # Vendor_Specified = 32768..65535
+
+ class TransitioningToState(object):
+ Unknown = pywbem.Uint16(0)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ No_Change = pywbem.Uint16(5)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ Not_Applicable = pywbem.Uint16(12)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 0: 'Unknown',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 5: 'No Change',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Defer',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset',
+ 12: 'Not Applicable'
+ }
+
+ class EnabledDefault(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ No_Default = pywbem.Uint16(7)
+ Quiesce = pywbem.Uint16(9)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 5: 'Not Applicable',
+ 6: 'Enabled but Offline',
+ 7: 'No Default',
+ 9: 'Quiesce'
+ }
+
+ class EnabledState(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shutting_Down = pywbem.Uint16(4)
+ Not_Applicable = pywbem.Uint16(5)
+ Enabled_but_Offline = pywbem.Uint16(6)
+ In_Test = pywbem.Uint16(7)
+ Deferred = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Starting = pywbem.Uint16(10)
+ # DMTF_Reserved = 11..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shutting Down',
+ 5: 'Not Applicable',
+ 6: 'Enabled but Offline',
+ 7: 'In Test',
+ 8: 'Deferred',
+ 9: 'Quiesce',
+ 10: 'Starting'
+ }
+
+ class VerifyInstalledIdentity(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Method_Reserved = pywbem.Uint32(4098)
+ # Software_Identity_Not_Installed = 4099..32767
+ Vendor_Specific = pywbem.Uint32(32768)
+
+ class AvailableRequestedStates(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ _reverse_map = {
+ 2: 'Enabled',
+ 3: 'Disabled',
+ 4: 'Shut Down',
+ 6: 'Offline',
+ 7: 'Test',
+ 8: 'Defer',
+ 9: 'Quiesce',
+ 10: 'Reboot',
+ 11: 'Reset'
+ }
+
+ 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 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Communication OK',
+ 3: 'Lost Communication',
+ 4: 'No Contact'
+ }
+
+ 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'OK',
+ 3: 'Degraded',
+ 4: 'Stressed',
+ 5: 'Predictive Failure',
+ 6: 'Error',
+ 7: 'Non-Recoverable Error',
+ 8: 'Starting',
+ 9: 'Stopping',
+ 10: 'Stopped',
+ 11: 'In Service',
+ 12: 'No Contact',
+ 13: 'Lost Communication',
+ 14: 'Aborted',
+ 15: 'Dormant',
+ 16: 'Supporting Entity in Error',
+ 17: 'Completed',
+ 18: 'Power Mode',
+ 19: 'Relocating'
+ }
+
+ 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Servicing',
+ 3: 'Starting',
+ 4: 'Stopping',
+ 5: 'Stopped',
+ 6: 'Aborted',
+ 7: 'Dormant',
+ 8: 'Completed',
+ 9: 'Migrating',
+ 10: 'Emigrating',
+ 11: 'Immigrating',
+ 12: 'Snapshotting',
+ 13: 'Shutting Down',
+ 14: 'In Test',
+ 15: 'Transitioning',
+ 16: 'In Service'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_or_Unspecified_Error = pywbem.Uint32(2)
+ Cannot_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Enabled = pywbem.Uint16(2)
+ Disabled = pywbem.Uint16(3)
+ Shut_Down = pywbem.Uint16(4)
+ Offline = pywbem.Uint16(6)
+ Test = pywbem.Uint16(7)
+ Defer = pywbem.Uint16(8)
+ Quiesce = pywbem.Uint16(9)
+ Reboot = pywbem.Uint16(10)
+ Reset = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 32768..65535
+
+ class InstallFromByteStream(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ # Method_Reserved = 4108..32767
+ # Vendor_Specific = 32768..65535
+
+ InstallFromByteStream.InstallOptions = InstallFromURI.InstallOptions
+
+ class InstallFromSoftwareIdentity(object):
+ Job_Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Target_In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Job_Started = pywbem.Uint32(4096)
+ Unsupported_TargetType = pywbem.Uint32(4097)
+ Unattended_silent_installation_not_supported = pywbem.Uint32(4098)
+ Downgrade_reinstall_not_supported = pywbem.Uint32(4099)
+ Not_enough_memory = pywbem.Uint32(4100)
+ Not_enough_swap_space = pywbem.Uint32(4101)
+ Unsupported_version_transition = pywbem.Uint32(4102)
+ Not_enough_disk_space = pywbem.Uint32(4103)
+ Software_and_target_operating_system_mismatch = pywbem.Uint32(4104)
+ Missing_dependencies = pywbem.Uint32(4105)
+ Not_applicable_to_target = pywbem.Uint32(4106)
+ No_supported_path_to_image = pywbem.Uint32(4107)
+ Cannot_add_to_Collection = pywbem.Uint32(4108)
+ # Method_Reserved = 4109..32767
+ # Vendor_Specific = 32768..65535
+
+ InstallFromSoftwareIdentity.InstallOptions = InstallFromURI.InstallOptions
+
+ class StartMode(object):
+ Automatic = 'Automatic'
+ Manual = 'Manual'
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'OK',
+ 2: 'Degraded',
+ 3: 'Error'
+ }
+
+def get_path():
+ """@return instance name with prefilled properties"""
+ op = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareInstallationService",
+ namespace="root/cimv2")
+ op['CreationClassName'] = op.classname
+ systemop = ComputerSystem.get_path()
+ op["SystemCreationClassName"] = systemop.classname
+ op['SystemName'] = systemop["Name"]
+ op["Name"] = "LMI:LMI_SoftwareInstallationService"
+ return op
+
+@cmpi_logging.trace_function
+def check_path(env, service, prop_name):
+ """
+ Checks instance name of SoftwareInstallationService.
+ @param prop_name name of object name; used for error descriptions
+ """
+ if not isinstance(service, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_service = get_path()
+ ch = env.get_cimom_handle()
+ if service.namespace != our_service.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_service.namespace))
+ if not ch.is_subclass(our_service.namespace,
+ sub=service.classname,
+ super=our_service.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a subclass of %s" % (
+ prop_name, our_service.classname))
+ for key in our_service.keybindings.keys():
+ if not key in service:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" is missing %s key property" % ( prop_name, key))
+ if service[key] != our_service[key]:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" key property %s(%s) does not match \"%s\"" %(
+ prop_name, key, service[key], our_service[key]))
+ return True
+
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether prop_name property of op object path is correct.
+ If not, an exception will be raised.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
+
+@cmpi_logging.trace_function
+def _check_target_and_collection(env, method, target, collection):
+ """
+ Checks Target and Collection parameters of provider's installation
+ methods.
+ """
+ values = Values.InstallFromSoftwareIdentity
+ if target:
+ try:
+ ComputerSystem.check_path(env, target, "Target")
+ except pywbem.CIMError as exc:
+ raise InstallationError(values.Unspecified_Error,
+ "Target must be either NULL or match managed"
+ " computer system: %s", str(exc))
+ if collection:
+ try:
+ SystemCollection.check_path(env, collection, "Collection")
+ except pywbem.CIMError as exc:
+ raise InstallationError(values.Unspecified_Error,
+ "Collection does not match system software collection: %s" %
+ str(exc))
+ if target and collection:
+ raise InstallationError(values.Unspecified_Error,
+ "Only one of Target and Collection parameters can be specified"
+ " at the same time.")
+ if not target and not collection:
+ raise InstallationError(values.Unspecified_Error,
+ "Either Target or Collection parameter must be specified."
+ if method == Job.JOB_METHOD_INSTALL_FROM_SOFTWARE_IDENTITY
+ else "Missing Target parameter.")
+
+@cmpi_logging.trace_function
+def _install_or_remove_check_params(
+ env, method, source, target, collection,
+ install_options,
+ install_options_values):
+ """
+ Checks parameters of provider's installation methods.
+ Upon any invalid option an InstallationError will be raised.
+ @return tuple (action, force, repair)
+ where action is one of Values.InstallFromSoftwareIdentity properties
+ """
+ if not method in (
+ Job.JOB_METHOD_INSTALL_FROM_URI,
+ Job.JOB_METHOD_INSTALL_FROM_SOFTWARE_IDENTITY,
+ Job.JOB_METHOD_INSTALL_FROM_BYTE_STREAM):
+ raise ValueError("unknown method")
+
+ values = Values.InstallFromSoftwareIdentity
+ supported_options = values.InstallOptions.supported.copy()
+ if method == Job.JOB_METHOD_INSTALL_FROM_URI:
+ supported_options.remove(values.InstallOptions.Uninstall)
+
+ if not source:
+ raise InstallationError(values.Unspecified_Error,
+ "Missing %s parameter." % (JOB_METHOD_SRC_PARAM_NAMES[method]))
+ if not install_options:
+ install_options = []
+ elif not isinstance(install_options, list):
+ raise InstallationError(values.Unspecified_Error,
+ "InstallOptions must be a list of uint16 values.")
+ options = {p for p in install_options}
+
+ if options - supported_options:
+ raise InstallationError(values.Unspecified_Error,
+ "unsupported install options: {%s}" %
+ ", ".join([ values.InstallOptions._reverse_map[p]
+ for p in options - supported_options]))
+ if install_options_values and len(options) != len(install_options_values):
+ raise InstallationError(values.Unspecified_Error,
+ "InstallOptions array must have the same"
+ " length as InstallOptionsValues: %d != %d" % (
+ len(options), len(install_options_values)))
+ if install_options_values:
+ for opt, val in zip(install_options, install_options_values):
+ if val:
+ raise InstallationError(values.Unspecified_Error,
+ "install option \"%s\" can not have any"
+ " associated value: %s" % (opt, val))
+ _check_target_and_collection(env, method, target, collection)
+ exclusive = [opt for opt in options if opt in {
+ values.InstallOptions.Install,
+ values.InstallOptions.Update,
+ values.InstallOptions.Uninstall }]
+ if len(exclusive) > 1:
+ raise InstallationError(values.Unspecified_Error,
+ "specified more than one mutually exclusive option at once: {%s}" %
+ ", ".join([ values.InstallOptions._reverse_map[p]
+ for p in exclusive]))
+ if not exclusive:
+ exclusive.append(values.InstallOptions.Install)
+ return ( exclusive[0]
+ , values.InstallOptions.Force_installation in options
+ , values.InstallOptions.Repair in options)
+
+@cmpi_logging.trace_function
+def make_job_input_params(method, source, target, collection,
+ install_options=None,
+ install_options_values=None):
+ """
+ Make dictionary of input parameters, that are stored in job's metadata.
+ This dictionary is used in creation of CIM_ConcreteJob and
+ CIM_InstMethodCall.
+ """
+ input_params = {
+ "Target" : pywbem.CIMProperty(
+ name="Target", type="reference", value=target),
+ }
+
+ if method == Job.JOB_METHOD_VERIFY_INSTALLED_IDENTITY:
+ input_params["Source"] = pywbem.CIMProperty(
+ name="Source", type="reference", value=source)
+ return input_params
+
+ input_params["InstallOptionsValues"] = pywbem.CIMProperty(
+ name="InstallOptionsValues",
+ type="string",
+ is_array=True,
+ value=install_options_values)
+ input_params["InstallOptions"] = pywbem.CIMProperty(
+ name="InstallOptions", type="uint16",
+ is_array=True,
+ value=install_options)
+ if method == Job.JOB_METHOD_INSTALL_FROM_URI:
+ input_params["URI"] = pywbem.CIMProperty(
+ name="URI", type="string", value=source)
+ elif method == Job.JOB_METHOD_INSTALL_FROM_SOFTWARE_IDENTITY:
+ input_params["Source"] = pywbem.CIMProperty(
+ name="Source", type="reference", value=source)
+ input_params["Collection"] = pywbem.CIMProperty(
+ name="Collection", type="reference", value=collection)
+ elif method == Job.JOB_METHOD_INSTALL_FROM_BYTE_STREAM:
+ input_params["Image"] = pywbem.CIMProperty(
+ name="Image", type="reference", value=source)
+
+ return input_params
+
+@cmpi_logging.trace_function
+def install_or_remove_package(env, method,
+ source, target, collection,
+ install_options,
+ install_options_values):
+ """
+ :param method: (``int``) Identifier of method defined in ``core.Job``
+ module with variables prefixed with ``JOB_METHOD_``.
+ """
+ values = Values.InstallFromSoftwareIdentity
+ (action, force, repair) = _install_or_remove_check_params(
+ env, method, source, target, collection,
+ install_options, install_options_values)
+ input_params = make_job_input_params(method,
+ source, target, collection, install_options,
+ install_options_values)
+ metadata = { "method" : method, "input_params" : input_params }
+ try:
+ ydb = YumDB.get_instance()
+ if action == values.InstallOptions.Uninstall:
+ src = Identity.object_path2nevra(source, with_epoch='ALWAYS')
+ cmpi_logging.logger.info('removing package %s', src)
+ jobid = ydb.remove_package(src, async=True, **metadata)
+ else:
+ update = action == values.InstallOptions.Update
+ if method == Job.JOB_METHOD_INSTALL_FROM_URI:
+ cmpi_logging.logger.info('%s package "%s"',
+ 'updating' if update else 'installing', source)
+ src = source
+ jobid = ydb.install_package_from_uri(
+ source, update_only=update, force=force or repair,
+ async=True, **metadata)
+ else: # software identity
+ src = Identity.object_path2nevra(source, with_epoch='ALWAYS')
+ if update:
+ jobid = ydb.update_package(src,
+ force=force or repair, async=True, **metadata)
+ else:
+ jobid = ydb.install_package(src,
+ force=force or repair, async=True, **metadata)
+ cmpi_logging.logger.info('installation job %s for pkg "%s"'
+ ' enqueued', jobid, src)
+ return jobid
+
+ except (pywbem.CIMError, errors.InvalidURI) as exc:
+ cmpi_logging.logger.exception('failed to install/remove package "%s"'
+ ' from %s: %s', source,
+ JOB_METHOD_SRC_PARAM_NAMES[method].lower(), str(exc))
+ raise InstallationError(values.Unspecified_Error, str(exc))
+
+@cmpi_logging.trace_function
+def verify_package(env, method, source, target):
+ """
+ :param method: (``int``) Identifier of method defined in ``core.Job``
+ module with variables prefixed with ``JOB_METHOD_``.
+ """
+ values = Values.VerifyInstalledIdentity
+
+ if method != Job.JOB_METHOD_VERIFY_INSTALLED_IDENTITY:
+ raise ValueError("unknown method")
+
+ if not source:
+ raise InstallationError(values.Unspecified_Error,
+ "Missing %s parameter." % (JOB_METHOD_SRC_PARAM_NAMES[method]))
+
+ _check_target_and_collection(env, method, target, None)
+
+ input_params = make_job_input_params(method, source, target, None)
+ metadata = { "method" : method, "input_params" : input_params }
+ nevra = Identity.object_path2nevra(source, with_epoch='ALWAYS')
+ ydb = YumDB.get_instance()
+ cmpi_logging.logger.info('verifying package "%s"', nevra)
+ jobid = ydb.check_package(nevra, async=True, **metadata)
+ cmpi_logging.logger.info('verification job %s for pkg "%s" enqueued',
+ jobid, nevra)
+ return jobid
+
diff --git a/src/software/lmi/software/core/InstallationServiceAffectsElement.py b/src/software/lmi/software/core/InstallationServiceAffectsElement.py
new file mode 100644
index 0000000..c305468
--- /dev/null
+++ b/src/software/lmi/software/core/InstallationServiceAffectsElement.py
@@ -0,0 +1,87 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 class
+LMI_SoftwareInstallationServiceAffectsElement.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+from lmi.software.core import ComputerSystem
+from lmi.software.core import Identity
+
+class Values(object):
+ class ElementEffects(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Exclusive_Use = pywbem.Uint16(2)
+ Performance_Impact = pywbem.Uint16(3)
+ Element_Integrity = pywbem.Uint16(4)
+ Manages = pywbem.Uint16(5)
+ Consumes = pywbem.Uint16(6)
+ Enhances_Integrity = pywbem.Uint16(7)
+ Degrades_Integrity = pywbem.Uint16(8)
+ Enhances_Performance = pywbem.Uint16(9)
+ Degrades_Performance = pywbem.Uint16(10)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..0xFFFF
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Exclusive Use',
+ 3 : 'Performance Impact',
+ 4 : 'Element Integrity',
+ 5 : 'Manages',
+ 6 : 'Consumes',
+ 7 : 'Enhances Integrity',
+ 8 : 'Degrades Integrity',
+ 9 : 'Enhances Performance',
+ 10 : 'Degrades Performance'
+ }
+
+@cmpi_logging.trace_function
+def fill_model_computer_system(model, keys_only=True):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = ComputerSystem.get_path()
+ if not keys_only:
+ model["ElementEffects"] = [
+ Values.ElementEffects.Enhances_Integrity,
+ Values.ElementEffects.Degrades_Integrity,
+ Values.ElementEffects.Other,
+ Values.ElementEffects.Other]
+ model["OtherElementEffectsDescriptions"] = [
+ "Enhances Integrity",
+ "Degrades Integrity",
+ "Enhances Functionality",
+ "Degrades Functionality"]
+ return model
+
+@cmpi_logging.trace_function
+def fill_model_identity(model, pkg, keys_only=True, identity_model=None):
+ """
+ Fills model's AffectedElement and all non-key properties.
+ """
+ model["AffectedElement"] = Identity.pkg2model(pkg, model=identity_model)
+ if not keys_only:
+ model["ElementEffects"] = [Values.ElementEffects.Other]
+ model["OtherElementEffectsDescriptions"] = ["Allows to install"]
+ return model
diff --git a/src/software/lmi/software/core/InstallationServiceCapabilities.py b/src/software/lmi/software/core/InstallationServiceCapabilities.py
new file mode 100644
index 0000000..62209bf
--- /dev/null
+++ b/src/software/lmi/software/core/InstallationServiceCapabilities.py
@@ -0,0 +1,226 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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
+LMI_SoftwareInstallationServiceCapabilities provider.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+
+class Values(object):
+ class SupportedExtendedResourceTypes(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Not_Applicable = pywbem.Uint16(2)
+ Linux_RPM = pywbem.Uint16(3)
+ HP_UX_Depot = pywbem.Uint16(4)
+ Windows_MSI = pywbem.Uint16(5)
+ Solaris_Package = pywbem.Uint16(6)
+ Macintosh_Disk_Image = pywbem.Uint16(7)
+ Debian_linux_Package = pywbem.Uint16(8)
+ VMware_vSphere_Installation_Bundle = pywbem.Uint16(9)
+ VMware_Software_Bulletin = pywbem.Uint16(10)
+ HP_Smart_Component = pywbem.Uint16(11)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0 : 'Unknown',
+ 1 : 'Other',
+ 2 : 'Not Applicable',
+ 3 : 'Linux RPM',
+ 4 : 'HP-UX Depot',
+ 5 : 'Windows MSI',
+ 6 : 'Solaris Package',
+ 7 : 'Macintosh Disk Image',
+ 8 : 'Debian linux Package',
+ 9 : 'VMware vSphere Installation Bundle',
+ 10 : 'VMware Software Bulletin',
+ 11 : 'HP Smart Component'
+ }
+
+ class SupportedInstallOptions(object):
+ Defer_target_system_reset = pywbem.Uint16(2)
+ Force_installation = pywbem.Uint16(3)
+ Install = pywbem.Uint16(4)
+ Update = pywbem.Uint16(5)
+ Repair = pywbem.Uint16(6)
+ Reboot = pywbem.Uint16(7)
+ Password = pywbem.Uint16(8)
+ Uninstall = pywbem.Uint16(9)
+ Log = pywbem.Uint16(10)
+ SilentMode = pywbem.Uint16(11)
+ AdministrativeMode = pywbem.Uint16(12)
+ ScheduleInstallAt = pywbem.Uint16(13)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+ _reverse_map = {
+ 2 : 'Defer target/system reset',
+ 3 : 'Force installation',
+ 4 : 'Install',
+ 5 : 'Update',
+ 6 : 'Repair',
+ 7 : 'Reboot',
+ 8 : 'Password',
+ 9 : 'Uninstall',
+ 10 : 'Log',
+ 11 : 'SilentMode',
+ 12 : 'AdministrativeMode',
+ 13 : 'ScheduleInstallAt'
+ }
+
+ class SupportedURISchemes(object):
+ data = pywbem.Uint16(2)
+ file = pywbem.Uint16(3)
+ ftp = pywbem.Uint16(4)
+ http = pywbem.Uint16(5)
+ https = pywbem.Uint16(6)
+ nfs = pywbem.Uint16(7)
+ tftp = pywbem.Uint16(8)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 0x8000..0xFFFF
+ _reverse_map = {
+ 2 : 'data',
+ 3 : 'file',
+ 4 : 'ftp',
+ 5 : 'http',
+ 6 : 'https',
+ 7 : 'nfs',
+ 8 : 'tftp'
+ }
+
+ class SupportedAsynchronousActions(object):
+ None_supported = pywbem.Uint16(2)
+ Install_From_Software_Identity = pywbem.Uint16(3)
+ Install_from_ByteStream = pywbem.Uint16(4)
+ Install_from_URI = pywbem.Uint16(5)
+ _reverse_map = {
+ 2 : 'None supported',
+ 3 : 'Install From Software Identity',
+ 4 : 'Install from ByteStream',
+ 5 : 'Install from URI'
+ }
+
+ class CreateGoalSettings(object):
+ Success = pywbem.Uint16(0)
+ Not_Supported = pywbem.Uint16(1)
+ Unknown = pywbem.Uint16(2)
+ Timeout = pywbem.Uint16(3)
+ Failed = pywbem.Uint16(4)
+ Invalid_Parameter = pywbem.Uint16(5)
+ Alternative_Proposed = pywbem.Uint16(6)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class SupportedSynchronousActions(object):
+ None_supported = pywbem.Uint16(2)
+ Install_From_Software_Identity = pywbem.Uint16(3)
+ Install_from_ByteStream = pywbem.Uint16(4)
+ Install_from_URI = pywbem.Uint16(5)
+ _reverse_map = {
+ 2 : 'None supported',
+ 3 : 'Install From Software Identity',
+ 4 : 'Install from ByteStream',
+ 5 : 'Install from URI'
+ }
+
+@cmpi_logging.trace_function
+def get_path():
+ """@return instance name with prefilled properties"""
+ op = pywbem.CIMInstanceName(
+ classname="LMI_SoftwareInstallationServiceCapabilities",
+ namespace="root/cimv2")
+ op['InstanceID'] = "LMI:LMI_SoftwareInstallationServiceCapabilities"
+ return op
+
+@cmpi_logging.trace_function
+def check_path(env, caps, prop_name):
+ """
+ Checks instance name of SoftwareInstallationServiceCapabilities.
+ @param prop_name name of object name; used for error descriptions
+ """
+ if not isinstance(caps, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_caps = get_path()
+ ch = env.get_cimom_handle()
+ if caps.namespace != our_caps.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_caps.namespace))
+ if not ch.is_subclass(our_caps.namespace,
+ sub=caps.classname,
+ super=our_caps.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a sublass of %s" % (
+ prop_name, our_caps.classname))
+ if caps['InstanceID'] != our_caps['InstanceID']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "InstanceID of \"%s\" does not match \"%s\"" %
+ prop_name, our_caps['InstanceID'])
+ return True
+
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether prop_name property of op object path is correct.
+ If not, an exception will be raised.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
+
+def get_instance(model=None):
+ """
+ Makes instance of LMI_SoftwareInstallationServiceCapabilities or
+ fills given model with properties.
+ """
+ path = get_path()
+ if model is None:
+ model = pywbem.CIMInstance(
+ "LMI_SoftwareInstallationServiceCapabilities", path=path)
+ model['InstanceID'] = path['InstanceID']
+
+ model['CanAddToCollection'] = True
+ model['Caption'] = 'Capabilities of LMI:LMI_SoftwareInstallationService'
+ model['Description'] = ('This instance provides information'
+ ' about LMI:LMI_SoftwareInstallationService\'s capabilities.')
+ model['SupportedAsynchronousActions'] = [
+ Values.SupportedAsynchronousActions.Install_From_Software_Identity,
+ Values.SupportedAsynchronousActions.Install_from_URI]
+ model['SupportedExtendedResourceTypes'] = [
+ Values.SupportedExtendedResourceTypes.Linux_RPM]
+ model['SupportedInstallOptions'] = [
+ Values.SupportedInstallOptions.Install,
+ Values.SupportedInstallOptions.Update,
+ Values.SupportedInstallOptions.Uninstall,
+ Values.SupportedInstallOptions.Force_installation,
+ Values.SupportedInstallOptions.Repair]
+ model['SupportedSynchronousActions'] = [
+ Values.SupportedSynchronousActions.None_supported]
+ model['SupportedTargetTypes'] = ['rpm', 'yum']
+ model['SupportedURISchemes'] = [
+ Values.SupportedURISchemes.file,
+ Values.SupportedURISchemes.ftp,
+ Values.SupportedURISchemes.http,
+ Values.SupportedURISchemes.https]
+ return model
+
diff --git a/src/software/lmi/software/core/Job.py b/src/software/lmi/software/core/Job.py
new file mode 100644
index 0000000..4a7753a
--- /dev/null
+++ b/src/software/lmi/software/core/Job.py
@@ -0,0 +1,754 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 SoftwareInstallationJob
+and SoftwareVerificationJob.
+"""
+
+from datetime import datetime, timedelta
+import pywbem
+import time
+
+from lmi.common import cmpi_logging
+from lmi.software import util
+from lmi.software.core import Error
+from lmi.software.yumdb import errors, jobs
+from lmi.software.yumdb import YumDB
+
+JOB_CLASS_NAMES = (
+ "LMI_SoftwareInstallationJob",
+ "LMI_SoftwareVerificationJob"
+)
+
+JOB_DESCRIPTIONS = {
+ jobs.YumInstallPackage :
+ 'Software package installation job %(jobid)d for "%(pkg)s".',
+ jobs.YumRemovePackage :
+ 'Software package removal job %(jobid)d for "%(pkg)s".',
+ jobs.YumUpdateToPackage :
+ 'Software package update job %(jobid)d for "%(pkg)s".',
+ jobs.YumUpdatePackage :
+ 'Software package update job %(jobid)d for "%(pkg)s".',
+ jobs.YumInstallPackageFromURI :
+ 'Software package installation job %(jobid)d from uri: "%(uri)s".',
+ jobs.YumCheckPackage :
+ 'Software package check job %(jobid)d for "%(pkg)s".',
+ jobs.YumCheckPackageFile :
+ 'File verification job %(jobid)d for package "%(pkg)s".',
+}
+
+# identificators of InstallationService method, which may trigger
+# creation of asynchronous job
+( JOB_METHOD_INSTALL_FROM_SOFTWARE_IDENTITY
+, JOB_METHOD_INSTALL_FROM_URI
+, JOB_METHOD_INSTALL_FROM_BYTE_STREAM
+, JOB_METHOD_VERIFY_INSTALLED_IDENTITY) = range(4)
+
+# above identificators point to this array to their description
+JOB_METHOD_NAMES = (
+ "InstallFromSoftwareIdentity",
+ "InstallFromURI",
+ "InstallFromByteStream",
+ "VerifyInstalledIdentity")
+
+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..
+ _reverse_map = {
+ 0: 'Not Available',
+ 1: 'No Additional Information',
+ 2: 'Stressed',
+ 3: 'Predictive Failure',
+ 4: 'Non-Recoverable Error',
+ 5: 'Supporting Entity in Error'
+ }
+
+ 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
+ _reverse_map = {
+ 0: 'Unknown',
+ 5: 'OK',
+ 10: 'Degraded/Warning',
+ 15: 'Minor failure',
+ 20: 'Major failure',
+ 25: 'Critical failure',
+ 30: 'Non-recoverable error'
+ }
+
+ class JobState(object):
+ New = pywbem.Uint16(2)
+ Starting = pywbem.Uint16(3)
+ Running = pywbem.Uint16(4)
+ Suspended = pywbem.Uint16(5)
+ Shutting_Down = pywbem.Uint16(6)
+ Completed = pywbem.Uint16(7)
+ Terminated = pywbem.Uint16(8)
+ Killed = pywbem.Uint16(9)
+ Exception = pywbem.Uint16(10)
+ Service = pywbem.Uint16(11)
+ Query_Pending = pywbem.Uint16(12)
+ # DMTF_Reserved = 13..32767
+ # Vendor_Reserved = 32768..65535
+ _reverse_map = {
+ 2: 'New',
+ 3: 'Starting',
+ 4: 'Running',
+ 5: 'Suspended',
+ 6: 'Shutting Down',
+ 7: 'Completed',
+ 8: 'Terminated',
+ 9: 'Killed',
+ 10: 'Exception',
+ 11: 'Service',
+ 12: 'Query Pending'
+ }
+
+ class GetError(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class KillJob(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Access_Denied = pywbem.Uint32(6)
+ Not_Found = pywbem.Uint32(7)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ class RecoveryAction(object):
+ Unknown = pywbem.Uint16(0)
+ Other = pywbem.Uint16(1)
+ Do_Not_Continue = pywbem.Uint16(2)
+ Continue_With_Next_Job = pywbem.Uint16(3)
+ Re_run_Job = pywbem.Uint16(4)
+ Run_Recovery_Job = pywbem.Uint16(5)
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'Do Not Continue',
+ 3: 'Continue With Next Job',
+ 4: 'Re-run Job',
+ 5: 'Run Recovery Job'
+ }
+
+ class RunDayOfWeek(object):
+ _Saturday = pywbem.Sint8(-7)
+ _Friday = pywbem.Sint8(-6)
+ _Thursday = pywbem.Sint8(-5)
+ _Wednesday = pywbem.Sint8(-4)
+ _Tuesday = pywbem.Sint8(-3)
+ _Monday = pywbem.Sint8(-2)
+ _Sunday = pywbem.Sint8(-1)
+ ExactDayOfMonth = pywbem.Sint8(0)
+ Sunday = pywbem.Sint8(1)
+ Monday = pywbem.Sint8(2)
+ Tuesday = pywbem.Sint8(3)
+ Wednesday = pywbem.Sint8(4)
+ Thursday = pywbem.Sint8(5)
+ Friday = pywbem.Sint8(6)
+ Saturday = pywbem.Sint8(7)
+ _reverse_map = {
+ 0: 'ExactDayOfMonth',
+ 1: 'Sunday',
+ 2: 'Monday',
+ 3: 'Tuesday',
+ 4: 'Wednesday',
+ 5: 'Thursday',
+ 6: 'Friday',
+ 7: 'Saturday',
+ -1: '-Sunday',
+ -7: '-Saturday',
+ -6: '-Friday',
+ -5: '-Thursday',
+ -4: '-Wednesday',
+ -3: '-Tuesday',
+ -2: '-Monday'
+ }
+
+ class RunMonth(object):
+ January = pywbem.Uint8(0)
+ February = pywbem.Uint8(1)
+ March = pywbem.Uint8(2)
+ April = pywbem.Uint8(3)
+ May = pywbem.Uint8(4)
+ June = pywbem.Uint8(5)
+ July = pywbem.Uint8(6)
+ August = pywbem.Uint8(7)
+ September = pywbem.Uint8(8)
+ October = pywbem.Uint8(9)
+ November = pywbem.Uint8(10)
+ December = pywbem.Uint8(11)
+ _reverse_map = {
+ 0: 'January',
+ 1: 'February',
+ 2: 'March',
+ 3: 'April',
+ 4: 'May',
+ 5: 'June',
+ 6: 'July',
+ 7: 'August',
+ 8: 'September',
+ 9: 'October',
+ 10: 'November',
+ 11: 'December'
+ }
+
+ class GetErrors(object):
+ Success = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unspecified_Error = pywbem.Uint32(2)
+ Timeout = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ Access_Denied = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ # Vendor_Specific = 32768..65535
+
+ 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Communication OK',
+ 3: 'Lost Communication',
+ 4: 'No Contact'
+ }
+
+ 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Other',
+ 2: 'OK',
+ 3: 'Degraded',
+ 4: 'Stressed',
+ 5: 'Predictive Failure',
+ 6: 'Error',
+ 7: 'Non-Recoverable Error',
+ 8: 'Starting',
+ 9: 'Stopping',
+ 10: 'Stopped',
+ 11: 'In Service',
+ 12: 'No Contact',
+ 13: 'Lost Communication',
+ 14: 'Aborted',
+ 15: 'Dormant',
+ 16: 'Supporting Entity in Error',
+ 17: 'Completed',
+ 18: 'Power Mode',
+ 19: 'Relocating'
+ }
+
+ 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..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'Not Available',
+ 2: 'Servicing',
+ 3: 'Starting',
+ 4: 'Stopping',
+ 5: 'Stopped',
+ 6: 'Aborted',
+ 7: 'Dormant',
+ 8: 'Completed',
+ 9: 'Migrating',
+ 10: 'Emigrating',
+ 11: 'Immigrating',
+ 12: 'Snapshotting',
+ 13: 'Shutting Down',
+ 14: 'In Test',
+ 15: 'Transitioning',
+ 16: 'In Service'
+ }
+
+ class LocalOrUtcTime(object):
+ Local_Time = pywbem.Uint16(1)
+ UTC_Time = pywbem.Uint16(2)
+ _reverse_map = {
+ 1: 'Local Time',
+ 2: 'UTC Time'
+ }
+
+ class RequestStateChange(object):
+ Completed_with_No_Error = pywbem.Uint32(0)
+ Not_Supported = pywbem.Uint32(1)
+ Unknown_Unspecified_Error = pywbem.Uint32(2)
+ Can_NOT_complete_within_Timeout_Period = pywbem.Uint32(3)
+ Failed = pywbem.Uint32(4)
+ Invalid_Parameter = pywbem.Uint32(5)
+ In_Use = pywbem.Uint32(6)
+ # DMTF_Reserved = ..
+ Method_Parameters_Checked___Transition_Started = pywbem.Uint32(4096)
+ Invalid_State_Transition = pywbem.Uint32(4097)
+ Use_of_Timeout_Parameter_Not_Supported = pywbem.Uint32(4098)
+ Busy = pywbem.Uint32(4099)
+ # Method_Reserved = 4100..32767
+ # Vendor_Specific = 32768..65535
+ class RequestedState(object):
+ Start = pywbem.Uint16(2)
+ Suspend = pywbem.Uint16(3)
+ Terminate = pywbem.Uint16(4)
+ Kill = pywbem.Uint16(5)
+ Service = pywbem.Uint16(6)
+ # DMTF_Reserved = 7..32767
+ # Vendor_Reserved = 32768..65535
+
+ class PrimaryStatus(object):
+ Unknown = pywbem.Uint16(0)
+ OK = pywbem.Uint16(1)
+ Degraded = pywbem.Uint16(2)
+ Error = pywbem.Uint16(3)
+ # DMTF_Reserved = ..
+ # Vendor_Reserved = 0x8000..
+ _reverse_map = {
+ 0: 'Unknown',
+ 1: 'OK',
+ 2: 'Degraded',
+ 3: 'Error'
+ }
+
+@cmpi_logging.trace_function
+def get_verification_out_params(job):
+ """
+ Get the output parameters for verification job. They may not be computed
+ yet. In that case compute them a update the job in YumWorker process.
+
+ :param job: (``jobs.YumCheckPackage``)
+ :rtype: (``dict``) Dictionary of output parameters with pywbem values.
+ """
+ if not isinstance(job, jobs.YumCheckPackage):
+ raise TypeError("job must be a YumCheckPackage instance")
+ if ( not job.metadata.get("output_params", [])
+ and job.state == job.COMPLETED):
+ from lmi.software.core import IdentityFileCheck
+ failed = []
+ pkg_info, pkg_check = job.result_data
+ for file_name in pkg_check:
+ pkg_file = pkg_check[file_name]
+ file_check = IdentityFileCheck.test_file(pkg_info,
+ pkg_check.file_checksum_type, pkg_file)
+ if not IdentityFileCheck.file_check_passed(file_check):
+ failed.append(IdentityFileCheck.file_check2model(
+ file_check, job=job))
+ metadata = {
+ 'output_params' : {
+ 'Failed' : pywbem.CIMProperty("Failed",
+ type="reference", is_array=True, value=failed)
+ }
+ }
+ # update local instance
+ job.update(metadata=metadata)
+ if YumDB.RUNNING_UNDER_CIMOM_PROCESS:
+ # update instance on server
+ YumDB.get_instance().update_job(job.jobid, metadata=metadata)
+ # else: we are called from YumWorker process;
+ # (update on server already occured)
+ # moreover YumWorker blocks on us - we can not wait for another job
+ # to finish
+ return job.metadata.get('output_params', [])
+
+@cmpi_logging.trace_function
+def make_method_params(job, class_name, include_input, include_output):
+ """
+ Create a class of given name with all input or output parameters
+ of the asynchronous method. Typically used to assemble
+ CIM_ConcreteJob.JobInParameters or CIM_InstMethodCall.MethodParameters
+ values.
+
+ :param job: (``YumJob``) Instance of job created as a result of method
+ invocation. It carries method parameters.
+ :param class_name: (``str``) Name of the class to create.
+ :param include_input: (``bool``) Whether input parameters should be
+ included in the returned class.
+ :param include_output: (``bool``) Whether output parameters should be
+ included in the returned class.
+ :rtype: CIMInstance of the created class.
+ """
+ # TODO: this is workaround for bug #920763, use class_name
+ # when it's fixed
+ clsname = "CIM_ManagedElement"
+ path = pywbem.CIMInstanceName(classname=clsname, namespace="root/cimv2")
+ inst = pywbem.CIMInstance(classname=clsname, path=path)
+ if include_input and "input_params" in job.metadata:
+ for (name, value) in job.metadata["input_params"].items():
+ inst[name] = value
+ if include_output:
+ if isinstance(job, jobs.YumCheckPackage):
+ # make sure, that output parameters are computed
+ get_verification_out_params(job)
+ if "output_params" in job.metadata:
+ # overwrite any input parameter
+ for (name, value) in job.metadata["output_params"].iteritems():
+ inst[name] = value
+ return inst
+
+def job_class2cim_class_name(jobcls):
+ """
+ Here we map classes of job objects to their corresponding CIM class
+ name.
+
+ :param jobcls: (``type``) Subclass of jobs.YumJob.
+ """
+ if not issubclass(jobcls, (
+ jobs.YumInstallPackageFromURI,
+ jobs.YumSpecificPackageJob)):
+ raise ValueError("Job class \"%s\" does not have any associated"
+ " CIM class." % jobcls.__name__)
+ if issubclass(jobcls, (jobs.YumCheckPackage, jobs.YumCheckPackageFile)):
+ return "LMI_SoftwareVerificationJob"
+ return "LMI_SoftwareInstallationJob"
+
+@cmpi_logging.trace_function
+def _fill_nonkeys(job, model):
+ """
+ Fills into the model of instance all non-key properties.
+ """
+ model['Caption'] = 'Software installation job with id=%d' % job.jobid
+ model['CommunicationStatus'] = Values.CommunicationStatus.Not_Available
+ model['DeleteOnCompletion'] = job.delete_on_completion
+ try:
+ description = JOB_DESCRIPTIONS[job.__class__]
+ kwargs = job.job_kwargs
+ kwargs['jobid'] = job.jobid
+ model['Description'] = description % kwargs
+ except KeyError:
+ cmpi_logging.logger.error(
+ 'no description string found for job class %s' %
+ job.__class__.__name__)
+ model['Description'] = pywbem.CIMProperty('Description',
+ type='string', value=None)
+ if job.started:
+ if job.finished:
+ elapsed = job.finished - job.started
+ else:
+ elapsed = time.time() - job.started
+ model['ElapsedTime'] = pywbem.CIMDateTime(timedelta(seconds=elapsed))
+ else:
+ model["ElapsedTime"] = pywbem.CIMProperty('ElapsedTime',
+ type='datetime', value=None)
+ model['ErrorCode'] = pywbem.Uint16(0 if job.state != job.EXCEPTION else 1)
+ try:
+ model['JobState'], model['OperationalStatus'], model['JobStatus'] = {
+ jobs.YumJob.NEW : (Values.JobState.New,
+ [Values.OperationalStatus.Dormant], 'Enqueued'),
+ jobs.YumJob.RUNNING : (Values.JobState.Running,
+ [Values.OperationalStatus.OK], 'Running'),
+ jobs.YumJob.TERMINATED : (Values.JobState.Terminated,
+ [Values.OperationalStatus.Stopped], 'Terminated'),
+ jobs.YumJob.EXCEPTION : (Values.JobState.Exception
+ , [Values.OperationalStatus.Error]
+ , 'Failed'),
+ jobs.YumJob.COMPLETED : (Values.JobState.Completed
+ , [ Values.OperationalStatus.OK
+ , Values.OperationalStatus.Completed]
+ , 'Finished successfully')
+ }[job.state]
+ except KeyError:
+ cmpi_logging.logger.error('unknown job state: %s' % job.state)
+ model['JobState'] = pywbem.CIMProperty('JobState',
+ type='uint16', value=None)
+ model['OperationalStatus'] = [Values.OperationalStatus.Unknown]
+ model['JobStatus'] = 'Unknown'
+ model["JobInParameters"] = make_method_params(
+ job, "__JobInParameters", True, False)
+ model["JobOutParameters"] = make_method_params(
+ job, "__JobOutParameters", False, True)
+ if 'method' in job.metadata:
+ model['MethodName'] = JOB_METHOD_NAMES[job.metadata["method"]]
+ else:
+ model["MethodName"] = pywbem.CIMProperty('MethodName',
+ type='string', value=None)
+ model['Name'] = job.metadata['name']
+ model['LocalOrUtcTime'] = Values.LocalOrUtcTime.UTC_Time
+ model['PercentComplete'] = pywbem.Uint16(
+ 100 if job.state == job.COMPLETED else (
+ 50 if job.state == job.RUNNING else
+ 0))
+ model['Priority'] = pywbem.Uint32(job.priority)
+ if job.started:
+ model['StartTime'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.started))
+ model['TimeBeforeRemoval'] = pywbem.CIMDateTime(timedelta(
+ seconds=job.time_before_removal))
+ model['TimeOfLastStateChange'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.last_change))
+ model['TimeSubmitted'] = pywbem.CIMDateTime(datetime.fromtimestamp(
+ job.created))
+
+@cmpi_logging.trace_function
+def job2model(job, class_name=None, keys_only=True, model=None):
+ """
+ Makes LMI_SoftwareJob out of job object or job id.
+
+ :param job: (``int`` | ``YumAsyncJob``) Job identifier.
+ In case of integer, caller should also provide class_name of resulting
+ CIM instance. Otherwise generic LMI_SoftwareJob will be returned.
+ :param class_name: (``str``) Determines CIM class name of resulting
+ instance. This should be given when ``job`` is an integer.
+ :param model: (``CIMInstance`` | ``CIMInstanceName``) If not None,
+ will be filled with properties, otherwise
+ a new instance of CIMInstance or CIMObjectPath is created.
+ :param keys_only: (``bool``) Says whether to fill only key properties.
+ Also if ``model`` is not given, it determines, whether to make
+ ``CIMInstanceName`` or ``CIMInstance``.
+ :rtype: (``CIMInstance`` | ``CIMInstanceName``)
+ """
+ if not isinstance(job, (int, long, jobs.YumAsyncJob)):
+ raise TypeError("job must be an instance of YumAsyncJob")
+ if isinstance(job, jobs.YumAsyncJob) and not job.async:
+ raise ValueError("job must be asynchronous")
+ if not keys_only and isinstance(job, (int, long)):
+ raise TypeError("job must be an instance of YumAsyncJob"
+ " filling non-key properties")
+
+ if class_name is None:
+ if model is not None:
+ class_name = model.classname
+ elif isinstance(job, jobs.YumJob):
+ class_name = job_class2cim_class_name(job.__class__)
+ else:
+ class_name = "LMI_SoftwareJob"
+ cmpi_logging.logger.warn(
+ "class_name not supplied for jobid=%d, using general"
+ " LMI_SoftwareJob as CIM class name")
+ if model is None:
+ model = pywbem.CIMInstanceName(class_name, namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance(class_name, path=model)
+
+ jobid = job.jobid if isinstance(job, jobs.YumAsyncJob) else job
+ model['InstanceID'] = 'LMI:%s:%d' % (class_name, jobid)
+ if isinstance(model, pywbem.CIMInstance):
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ if not keys_only:
+ _fill_nonkeys(job, model)
+ return model
+
+@cmpi_logging.trace_function
+def object_path2job(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "op must be an instance of CIMInstanceName")
+
+ if (not "InstanceID" in op or not op['InstanceID']):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing InstanceID key property.")
+ instid = op['InstanceID']
+ match = util.RE_INSTANCE_ID.match(instid)
+ if not match or match.group('clsname').lower() not in {
+ c.lower() for c in JOB_CLASS_NAMES}:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "InstanceID must start with one of {%s} prefixes."
+ " And end with positive integer." % (
+ ", ".join(("LMI:%s:" % cn) for cn in JOB_CLASS_NAMES)))
+
+ instid = int(match.group('id'))
+ try:
+ job = YumDB.get_instance().get_job(instid)
+ clsname = job_class2cim_class_name(job.__class__)
+ if ( clsname.lower() != op.classname.lower()
+ and op.classname.lower() != 'LMI_SoftwareJob'.lower()):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Classname \"%s\" does not belong to job with given id."
+ " \"%s\" is the correct one." % (op.classname, clsname))
+ return job
+ except errors.JobNotFound:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'No such job "%s".' % op['InstanceID'])
+
+@cmpi_logging.trace_function
+def modify_instance(instance):
+ """
+ This call modifies the job's parameters according to given instance.
+ """
+ job = object_path2job(instance.path)
+ ydb = YumDB.get_instance()
+ update_kwargs = {}
+ reschedule_kwargs = {}
+ # all modifiable properties
+ prop_name_map = {
+ "name" : "name",
+ "priority" : "priority",
+ "deleteoncompletion" : "delete_on_completion",
+ "timebeforeremoval" : "time_before_removal"
+ }
+ metadata_props = {"name"}
+ reschedule_props = {"delete_on_completion", "time_before_removal"}
+ for name, prop in instance.properties.items():
+ if prop is None:
+ cmpi_logging.logger.warn('property "%s" is None')
+ continue
+ name = name.lower()
+ try:
+ pname = prop_name_map[name]
+ if pname == "priority" and job.priority != prop.value:
+ cmpi_logging.logger.info(
+ 'changing priority of job %s to %d', job, prop.value)
+ job = ydb.set_job_priority(job.jobid, prop.value)
+ elif pname in reschedule_props:
+ if getattr(job, pname) == prop.value:
+ continue
+ if pname == "time_before_removal":
+ value = prop.value.timedelta.total_seconds()
+ else:
+ value = prop.value
+ reschedule_kwargs[pname] = value
+ else:
+ if pname in metadata_props:
+ if not 'metadata' in update_kwargs:
+ update_kwargs['metadata'] = {}
+ update_kwargs['metadata'][pname] = prop.value
+ else:
+ update_kwargs[pname] = prop.value
+ except KeyError:
+ if name == 'instanceid':
+ continue
+ cmpi_logging.logger.warn("skipping property %s: %s", name, prop)
+
+ if reschedule_kwargs:
+ for prop in ('delete_on_completion', 'time_before_removal'):
+ if prop not in reschedule_kwargs:
+ reschedule_kwargs[prop] = getattr(job, prop)
+ cmpi_logging.logger.info('rescheduling job %s to: %s',
+ job, ", ".join("%s=%s"%(k, v) for k, v in
+ reschedule_kwargs.items()))
+ job = ydb.reschedule_job(job.jobid, **reschedule_kwargs)
+
+ if update_kwargs:
+ cmpi_logging.logger.info('changing atributes of job %s to: %s',
+ job, ", ".join("%s=%s"%(k, v) for k, v in
+ update_kwargs.items()))
+ job = ydb.update_job(job.jobid, **update_kwargs)
+
+ return job2model(job, keys_only=False, model=instance)
+
+@cmpi_logging.trace_function
+def job2error(job):
+ """
+ @return instance of CIM_Error if job is in EXCEPTION state,
+ None otherwise
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be isntance of YumJob")
+ if job.state == job.EXCEPTION:
+ errortup = job.result_data
+ kwargs = {}
+ if issubclass(errortup[0],
+ (errors.RepositoryNotFound, errors.PackageNotFound)):
+ kwargs['status_code'] = Error.Values. \
+ CIMStatusCode.CIM_ERR_NOT_FOUND
+ if issubclass(errortup[0], errors.PackageNotFound):
+ kwargs['status_code_description'] = "Package not found"
+ else:
+ kwargs['status_code_description'] = "Repository not found"
+ elif issubclass(errortup[0], errors.PackageAlreadyInstalled):
+ kwargs['status_code'] = Error.Values. \
+ CIMStatusCode.CIM_ERR_ALREADY_EXISTS
+ kwargs['message'] = getattr(errortup[1], 'message',
+ str(errortup[1]))
+ value = Error.make_instance(**kwargs)
+ return value
diff --git a/src/software/lmi/software/core/MethodResult.py b/src/software/lmi/software/core/MethodResult.py
new file mode 100644
index 0000000..47eb7ca
--- /dev/null
+++ b/src/software/lmi/software/core/MethodResult.py
@@ -0,0 +1,88 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 class LMI_SoftwareMethodResult.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+from lmi.software.core import Job
+from lmi.software.core import InstMethodCall
+from lmi.software.yumdb import jobs, errors, YumDB
+
+@cmpi_logging.trace_function
+def object_path2jobid(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ if not isinstance(op, pywbem.CIMInstanceName):
+ raise TypeError("op must be a CIMInstanceName")
+ if not op["InstanceID"].lower().startswith('lmi:lmi_softwaremethodresult:'):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Missing 'LMI:LMI_SoftwareMethodResult:' prefix in InstanceID.")
+ instid = op['InstanceID'][len('LMI:LMI_SoftwareMethodResult:'):]
+ try:
+ instid = int(instid)
+ except ValueError:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Failed to parse InstanceID.")
+ return instid
+
+@cmpi_logging.trace_function
+def object_path2job(op):
+ """
+ @param op must contain precise InstanceID of job
+ """
+ instid = object_path2jobid(op)
+ try:
+ return YumDB.get_instance().get_job(instid)
+ except errors.JobNotFound:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "No such job.")
+
+@cmpi_logging.trace_function
+def job2model(job, keys_only=True, model=None):
+ """
+ This accepts job's id and produces corresponding result model.
+ """
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be an instance of YumJob")
+ if model is None:
+ model = pywbem.CIMInstanceName("LMI_SoftwareMethodResult",
+ namespace="root/cimv2")
+ if not keys_only:
+ model = pywbem.CIMInstance("LMI_SoftwareMethodResult", path=model)
+ model['InstanceID'] = "LMI:LMI_SoftwareMethodResult:"+str(job.jobid)
+ if not keys_only:
+ model.path['InstanceID'] = model['InstanceID'] #pylint: disable=E1103
+ method_name = Job.JOB_METHOD_NAMES[job.metadata['method']]
+ model['Caption'] = 'Result of method %s' % method_name
+ model['Description'] = (
+ 'Result of asynchronous job number %d created upon invocation'
+ " of %s's %s method." % (job.jobid,
+ "LMI_SoftwareInstallationService", method_name))
+ model['ElementName'] = 'MethodResult-%d' % job.jobid
+ model['PostCallIndication'] = pywbem.CIMProperty("PostCallIndication",
+ type="instance",
+ value=InstMethodCall.job2model(job, pre=False))
+ model['PreCallIndication'] = pywbem.CIMProperty("PreCallIndication",
+ type="instance",
+ value=InstMethodCall.job2model(job))
+ return model
+
diff --git a/src/software/lmi/software/core/SystemCollection.py b/src/software/lmi/software/core/SystemCollection.py
new file mode 100644
index 0000000..6596d40
--- /dev/null
+++ b/src/software/lmi/software/core/SystemCollection.py
@@ -0,0 +1,73 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 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 concerning SystemSoftwareCollection provider.
+"""
+
+import pywbem
+
+from lmi.common import cmpi_logging
+
+def get_path():
+ """@return instance name with prefilled properties"""
+ op = pywbem.CIMInstanceName(
+ classname="LMI_SystemSoftwareCollection",
+ namespace="root/cimv2")
+ op['InstanceID'] = "LMI:LMI_SystemSoftwareCollection"
+ return op
+
+@cmpi_logging.trace_function
+def check_path(env, collection, prop_name):
+ """
+ Checks instance name of SystemSoftwareCollection.
+ @param prop_name name of object name; used for error descriptions
+ """
+ if not isinstance(collection, pywbem.CIMInstanceName):
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "\"%s\" must be a CIMInstanceName" % prop_name)
+ our_collection = get_path()
+ ch = env.get_cimom_handle()
+ if collection.namespace != our_collection.namespace:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ 'Namespace of "%s" does not match "%s"' % (
+ prop_name, our_collection.namespace))
+ if not ch.is_subclass(our_collection.namespace,
+ sub=collection.classname,
+ super=our_collection.classname):
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "Class of \"%s\" must be a sublass of %s" % (
+ prop_name, our_collection.classname))
+ if not 'InstanceID' in collection:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "\"%s\" is missing InstanceID key property", prop_name)
+ if collection['InstanceID'] != our_collection['InstanceID']:
+ raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND,
+ "InstanceID of \"%s\" does not match \"%s\"" %
+ prop_name, our_collection['InstanceID'])
+ return True
+
+@cmpi_logging.trace_function
+def check_path_property(env, op, prop_name):
+ """
+ Checks, whether prop_name property of op object path is correct.
+ If not, an exception will be risen.
+ """
+ if not prop_name in op:
+ raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER,
+ "Missing %s key property!" % prop_name)
+ return check_path(env, op[prop_name], prop_name)
diff --git a/src/software/lmi/software/core/__init__.py b/src/software/lmi/software/core/__init__.py
new file mode 100644
index 0000000..a7ce6c9
--- /dev/null
+++ b/src/software/lmi/software/core/__init__.py
@@ -0,0 +1,84 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Core functionality of particular providers.
+Each provider having functionality useful to others has a submodule
+in this subpackage with the same name except for LMI_ prefix.
+"""
+
+import pywbem
+from lmi.common import cmpi_logging
+
+@cmpi_logging.trace_function
+def generate_references(
+ env,
+ object_name,
+ model,
+ filter_result_class_name,
+ filter_role,
+ filter_result_role,
+ keys_only,
+ handlers):
+ """
+ Function for generating references to object_name. It's supposed
+ to be used directly from references method of provider classes.
+ @param handlers is a list of tuples:
+ [ (role, class_name, handler), ... ]
+ where handler is a function, that will be called for matching
+ parameters, yielding instance(s/names). It accepts arguments:
+ env, object_name, model, keys_only.
+ """
+ if not isinstance(object_name, pywbem.CIMInstanceName):
+ raise TypeError("object_name must be a CIMInstanceName")
+ if not isinstance(model, pywbem.CIMInstance):
+ raise TypeError("model must be a CIMInstance")
+
+ ch = env.get_cimom_handle()
+ model.path.update(dict((t[0], None) for t in handlers))
+
+ try:
+ for i, (role, clsname, handler) in enumerate(handlers):
+ if ( ( filter_role
+ and filter_role.lower() != role.lower())
+ or not ch.is_subclass(object_name.namespace,
+ sub=object_name.classname,
+ super=clsname)):
+ continue
+ other_results = list((htuple[0], htuple[1]) for htuple in (
+ handlers[:i] + handlers[i+1:]))
+ for res_role, res_clsname in other_results:
+ if ( ( not filter_result_role
+ or filter_result_role.lower() == res_role.lower())
+ and (not filter_result_class_name or ch.is_subclass(
+ object_name.namespace,
+ sub=filter_result_class_name,
+ super=res_clsname))):
+ break
+ else:
+ continue
+ # matches filters
+ for obj in handler(env, object_name, model, keys_only):
+ yield obj
+ except pywbem.CIMError as exc:
+ if exc.args[0] != pywbem.CIM_ERR_NOT_FOUND:
+ raise
+
diff --git a/src/software/lmi/software/util/__init__.py b/src/software/lmi/software/util/__init__.py
new file mode 100644
index 0000000..d92fd7c
--- /dev/null
+++ b/src/software/lmi/software/util/__init__.py
@@ -0,0 +1,170 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""Common utilities for LMI_Software* providers
+"""
+
+import platform
+import re
+import signal
+
+RE_INSTANCE_ID = re.compile(r'LMI:(?P<clsname>[a-z][a-z_0-9]+):(?P<id>\d+)',
+ re.IGNORECASE)
+
+RE_EVRA = re.compile(
+ r'^(?P<epoch>\d+):(?P<version>[^-]+)-(?P<release>.+)\.(?P<arch>[^.]+)$')
+RE_NEVRA = re.compile(
+ r'^(?P<name>.+)-(?P<evra>(?P<epoch>\d+):(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?P<arch>[^.]+))$')
+RE_NEVRA_OPT_EPOCH = re.compile(
+ r'^(?P<name>.+)-(?P<evra>((?P<epoch>\d+):)?(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?P<arch>[^.]+))$')
+RE_ENVRA = re.compile(
+ r'^(?P<epoch>\d+):(?P<name>.+)-(?P<evra>(?P<version>[^-]+)'
+ r'-(?P<release>.+)\.(?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").lower() == "(none)":
+ epoch = "0"
+ return { "name" : match.group("name")
+ , "epoch" : epoch
+ , "version" : match.group("version")
+ , "release" : match.group("release")
+ , "arch" : match.group("arch")
+ }
+
+def make_nevra(name, epoch, version, release, 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, version, release, 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)
+
+def get_signal_name(signal_num):
+ """
+ @return name of signal for signal_num argument
+ """
+ if not isinstance(signal_num, (int, long)):
+ raise TypeError("signal_num must be an integer")
+ try:
+ return dict((v, k) for k, v in signal.__dict__.items())[signal_num]
+ except KeyError:
+ return "UNKNOWN_SIGNAL(%d)" % signal_num
+
diff --git a/src/software/lmi/software/yumdb/__init__.py b/src/software/lmi/software/yumdb/__init__.py
new file mode 100644
index 0000000..656b090
--- /dev/null
+++ b/src/software/lmi/software/yumdb/__init__.py
@@ -0,0 +1,677 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: 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 inspect
+import os
+import re
+import time
+from multiprocessing import Process, Queue #pylint: disable=W0404
+from pywbem.cim_provider2 import CIMProvider2
+import Queue as TQueue # T as threaded
+import threading
+import yum
+
+from lmi.common import cmpi_logging, singletonmixin
+from lmi.common.IndicationManager import IndicationManager
+from lmi.software.yumdb import jobs
+from lmi.software.yumdb import errors
+from lmi.software.yumdb.packageinfo import PackageInfo
+from lmi.software.yumdb.packagecheck import PackageFile
+from lmi.software.yumdb.packagecheck import PackageCheck
+from lmi.software.yumdb.process import YumWorker
+from lmi.software.yumdb.repository import Repository
+from lmi.software.yumdb.util import DispatchingFormatter
+from lmi.software.util import get_signal_name
+
+# Maximum time in seconds to wait for a job to accomplish.
+# If timeout expires, spawned process is checked (it might
+# be possibly killed) and is respawned in case it's dead.
+MAX_JOB_WAIT_TIME = 30
+
+# this may be used as an argument to YumWorker to setup logging
+YUM_WORKER_DEBUG_LOGGING_CONFIG = {
+ "version" : 1,
+ 'disable_existing_loggers' : True,
+ "formatters": {
+ # this is a message format for logging function/method calls
+ # it's manually set up in YumWorker's init method
+ "default": {
+ "()": DispatchingFormatter,
+ "formatters" : {
+ "lmi.software.yumdb.util.trace_function":
+ "%(asctime)s %(levelname)s:%(message)s"
+ },
+ "default" : "%(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",
+ },
+ },
+ "loggers" : {
+ "root": {
+ "level": "DEBUG",
+ "handlers" : ["file"]
+ },
+ "lmi.software.yumdb": {
+ "level" : "DEBUG",
+ "handlers" : ["file"],
+ "propagate" : False,
+ },
+ }
+}
+
+# *****************************************************************************
+# Utilities
+# *****************************************************************************
+def log_reply_error(job, reply):
+ """
+ Raises an exception in case of error occured in worker process
+ while processing job.
+ """
+ if isinstance(reply, (int, long)):
+ # asynchronous job
+ return
+ if not isinstance(reply, jobs.YumJob):
+ raise TypeError('expected instance of jobs.YumJob for reply, not "%s"' %
+ reply.__class__.__name__)
+ if reply.result == jobs.YumJob.RESULT_ERROR:
+ cmpi_logging.logger.error(
+ "YumDB: %s failed with error %s: %s",
+ job, reply.result_data[0].__name__, str(reply.result_data[1]))
+ cmpi_logging.logger.trace_warn(
+ "YumDB: %s exception traceback:\n%s%s: %s",
+ job, "".join(reply.result_data[2]),
+ reply.result_data[0].__name__, str(reply.result_data[1]))
+ reply.result_data[1].tb_printed = True
+ raise reply.result_data[1]
+ elif reply.result == jobs.YumJob.RESULT_TERMINATED:
+ cmpi_logging.logger.warn('YumDB: %s terminated', job)
+ else:
+ cmpi_logging.logger.debug('YumDB: %s completed with success', job)
+
+def _make_async_job(jobcls, *args, **kwargs):
+ """Creates asynchronous job, filling it wih some metadata."""
+ if not issubclass(jobcls, jobs.YumAsyncJob):
+ raise TypeError("jobcls must be a subclass of YumAsyncJob")
+ job = jobcls(*args, **kwargs)
+ if job.metadata is None:
+ job.metadata = {}
+ job.metadata['name'] = \
+ type(job).__name__[len('Yum'):] + ('-%d' % job.jobid)
+ return job
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def job_request(async=False):
+ """
+ Decorator factory for job entry points. They are YumDB methods.
+ All of them must return either job objects or jobid for asynchronous calls.
+ Job objects are processed by this decorator for caller to obtain only the
+ information he needs.
+
+ It wrapps them with logger wrapper and in case of asynchronous jobs,
+ it returns just the jobid.
+ """
+ def _decorator(method):
+ """
+ Decorator that just logs the method's call and returns job's result.
+ """
+ logged = cmpi_logging.trace_method(method)
+ def _new_func(self, *args, **kwargs):
+ """Wrapper for YumDB's method."""
+ return logged(self, *args, **kwargs).result_data
+ return _new_func
+
+ def _decorator_async(method):
+ """
+ Decorator for methods accepting async argument. In case of async=True,
+ the method returns jobid. Job's result is returned otherwise.
+ """
+ logged = cmpi_logging.trace_method(method)
+ def _new_func(self, *args, **kwargs):
+ """Wrapper for YumDB's method."""
+ callargs = inspect.getcallargs(method, self, *args, **kwargs)
+ result = logged(self, *args, **kwargs)
+ if callargs.get('async', False):
+ return result
+ else:
+ return result.result_data
+ return _new_func
+
+ return _decorator_async if async else _decorator
+
+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
+
+ # This serves to all code base as a global variable used to check,
+ # whether YumDB instance is running under cimom broker or under worker
+ # process. This is important for code used in callback functions passed
+ # to worker responsible for creating instances of ConcreteJob. This code
+ # must avoid using calls to YumDB while running under worker. This
+ #
+ # Worker process must set this to False before starting its event handling
+ # loop.
+ RUNNING_UNDER_CIMOM_PROCESS = True
+
+ @cmpi_logging.trace_method
+ def __init__(self, **kwargs): #pylint: disable=W0231
+ """
+ All arguments are passed to yum.YumBase constructor.
+ """
+ self._process = None
+ if kwargs is None:
+ kwargs = {}
+ self._yum_kwargs = kwargs
+
+ self._session_lock = threading.RLock()
+ self._session_level = 0
+
+ # used to guard access to _expected list and _process
+ self._reply_lock = threading.Lock()
+ # used to wait for job to be processed and received
+ self._reply_cond = threading.Condition(self._reply_lock)
+ # ids of all expected jobs -- those to be processed by YumWorker
+ self._expected = []
+ # {job_id : reply, ... }
+ self._replies = {}
+ cmpi_logging.logger.trace_info('YumDB: initialized')
+
+ # *************************************************************************
+ # Private methods
+ # *************************************************************************
+ @cmpi_logging.trace_method
+ def _handle_reply_timeout(self, job):
+ """
+ This is called when timeout occurs while waiting on downlink queue for
+ reply. Delay can be caused by worker process's early termination (bug).
+ This handler tries to recover from such an situation.
+ """
+ if not self._worker.is_alive():
+ if self._worker.exitcode < 0:
+ cmpi_logging.logger.error("[jobid=%d] worker"
+ " process(pid=%d) killed by signal %s", job.jobid,
+ self._worker.pid, get_signal_name(-self._process.exitcode))
+ else:
+ cmpi_logging.logger.error("[jobid=%d] worker"
+ " process(pid=%d) is dead - exit code: %d",
+ job.jobid, self._process.pid, self._worker.exitcode)
+ with self._reply_lock:
+ self._process = None
+ cmpi_logging.logger.error(
+ "[jobid=%d] starting new worker process", job.jobid)
+ self._expected = []
+ if not isinstance(job, jobs.YumBeginSession):
+ with self._session_lock:
+ if self._session_level > 0:
+ cmpi_logging.logger.info('restoring session '
+ 'level=%d', self._session_level)
+ new_session_job = jobs.YumBeginSession()
+ self._worker.uplink.put(new_session_job)
+ reply = self._worker.downlink.get()
+ log_reply_error(new_session_job, reply)
+ self._worker.uplink.put(job)
+ self._expected.append(job.jobid)
+ # other waiting processes need to resend their requests
+ self._reply_cond.notifyAll()
+ else:
+ cmpi_logging.logger.info("[jobid=%d] process is running,"
+ " waiting some more", job.jobid)
+
+ @cmpi_logging.trace_method
+ def _receive_reply(self, job):
+ """
+ Block on downlink queue to receive expected replies from worker
+ process. Only one thread can be executing this code at any time.
+
+ 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.
+
+ In case, that worker process terminated due to some error. Restart it
+ and resend all the job requests again.
+ """
+ while True:
+ cmpi_logging.logger.debug("[jobid=%d] blocking on downlink queue",
+ job.jobid)
+ try:
+ jobout = self._worker.downlink.get(
+ block=True, timeout=MAX_JOB_WAIT_TIME)
+ if jobout.jobid == job.jobid:
+ cmpi_logging.logger.debug(
+ "[jobid=%d] received desired reply", job.jobid)
+ with self._reply_lock:
+ self._expected.remove(job.jobid)
+ if len(self._expected):
+ self._reply_cond.notify()
+ return jobout
+ else:
+ cmpi_logging.logger.info("[jobid=%d] received reply"
+ " for another thread (jobid=%d)",
+ job.jobid, jobout.jobid)
+ with self._reply_lock:
+ self._replies[jobout.jobid] = jobout
+ self._reply_cond.notifyAll()
+ except TQueue.Empty:
+ cmpi_logging.logger.warn("[jobid=%d] wait for job reply timeout"
+ "(%d seconds) occured", job.jobid, MAX_JOB_WAIT_TIME)
+ self._handle_reply_timeout(job)
+
+ @cmpi_logging.trace_method
+ def _send_and_receive(self, job):
+ """
+ Sends a request to server and blocks until job is processed by
+ YumWorker and reply is received.
+
+ Only one thread can block on downlink channel to obtain reply. This
+ thread is the one, whose job appears as first in _expected list. Server
+ processes input jobs sequentially. That's why it's safe to presume,
+ that jobs are received in the same order as they were send. Thanks to
+ that we don't have to care about receiving replies for the other
+ waiting threads.
+
+ @return result of job
+ """
+ with self._reply_lock:
+ self._worker.uplink.put(job)
+ if getattr(job, 'async', False) is True:
+ return job.jobid
+ self._expected.append(job.jobid)
+ while True:
+ if job.jobid in self._replies:
+ cmpi_logging.logger.debug(
+ "[jobid=%d] desired reply already received",
+ job.jobid)
+ try:
+ self._expected.remove(job.jobid)
+ except ValueError:
+ cmpi_logging.logger.warn(
+ "[jobid=%d] reply not in expected list",
+ job.jobid)
+ return self._replies.pop(job.jobid)
+ elif job.jobid not in self._expected:
+ # process terminated, resending job
+ cmpi_logging.logger.warn("[jobid=%d] job removed"
+ " from expected list, sending request again", job.jobid)
+ self._worker.uplink.put(job)
+ self._expected.append(job.jobid)
+ elif job.jobid == self._expected[0]:
+ # now it's our turn to block on downlink
+ break
+ else: # another thread blocks on downlink -> let's sleep
+ cmpi_logging.logger.debug(
+ "[jobid=%d] another %d threads expecting reply,"
+ " suspending...", job.jobid, len(self._expected) - 1)
+ self._reply_cond.wait()
+ cmpi_logging.logger.debug(
+ "[jobid=%d] received reply, waking up", job.jobid)
+ return self._receive_reply(job)
+
+ 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", job)
+ reply = self._send_and_receive(job)
+ log_reply_error(job, reply)
+ cmpi_logging.logger.trace_verbose("YumDB: job %s done", 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 = Queue()
+ downlink = Queue()
+ self._process = YumWorker(uplink, downlink,
+ indication_manager=IndicationManager.get_instance(),
+ yum_kwargs=self._yum_kwargs)
+ #logging_config=YUM_WORKER_DEBUG_LOGGING_CONFIG)
+ self._process.start()
+ cmpi_logging.logger.trace_info(
+ "YumDB: YumWorker started with pid=%s", self._process.pid)
+ return self._process
+
+ # *************************************************************************
+ # Special methods
+ # *************************************************************************
+ def __del__(self):
+ """
+ Ensure, that YumWorker process is correctly shutted down.
+ """
+ self.clean_up()
+ singletonmixing.Singleton.__del__(self)
+
+ @cmpi_logging.trace_method
+ def __enter__(self):
+ with self._session_lock:
+ if self._session_level == 0:
+ self._do_job(jobs.YumBeginSession())
+ cmpi_logging.logger.trace_info('YumDB: new session started')
+ self._session_level += 1
+ cmpi_logging.logger.trace_info('YumDB: nested to session level=%d',
+ self._session_level)
+ return self
+
+ @cmpi_logging.trace_method
+ def __exit__(self, exc_type, exc_value, traceback):
+ with self._session_lock:
+ if self._session_level == 1:
+ self._do_job(jobs.YumEndSession())
+ cmpi_logging.logger.trace_info('YumDB: session ended')
+ cmpi_logging.logger.trace_info('YumDB: emerged from session'
+ ' level=%d', self._session_level)
+ self._session_level = max(self._session_level - 1, 0)
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ @cmpi_logging.trace_method
+ def clean_up(self):
+ """
+ Shut down the YumWorker process.
+ """
+ with self._reply_lock:
+ if self._process is not None:
+ cmpi_logging.logger.info('YumDB: terminating YumWorker')
+ self._process.uplink.put(None) # terminating command
+ self._process.join()
+ cmpi_logging.logger.info('YumDB: YumWorker terminated')
+ self._process = None
+ else:
+ cmpi_logging.logger.warn("YumDB: clean_up called, when process"
+ " not initialized!")
+
+ # *************************************************************************
+ # Jobs with simple results
+ # *************************************************************************
+ @job_request()
+ def get_package_list(self, kind,
+ allow_duplicates=False,
+ sort=False,
+ include_repos=None,
+ exclude_repos=None):
+ """
+ @param kind is one of: jobs.YumGetPackageList.SUPPORTED_KINDS
+ @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,
+ include_repos=include_repos, exclude_repos=exclude_repos))
+
+ @job_request()
+ def filter_packages(self, kind,
+ allow_duplicates=False,
+ sort=False,
+ include_repos=None,
+ exclude_repos=None,
+ **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,
+ include_repos=include_repos, exclude_repos=exclude_repos,
+ **filters))
+
+ @job_request()
+ def get_repository_list(self, kind):
+ """
+ @param kind is one of: jobs.YumGetRepositoryList.SUPPORTED_KINDS
+ @param allow_duplicates says, whether to list all found versions
+ of single package
+ @return [pkg1, pkg2, ...], pkgi is instance of yumdb.Repository
+ """
+ return self._do_job(jobs.YumGetRepositoryList(kind))
+
+ @job_request()
+ def filter_repositories(self, kind, **filters):
+ """
+ Similar to get_repository_list(), but applies filter on packages.
+ @see yumdb.jobs.YumFilterRepositories job for supported filter keys
+ """
+ return self._do_job(jobs.YumFilterRepositories(kind, **filters))
+
+ @job_request()
+ def set_repository_enabled(self, repoid, enable):
+ """
+ Enable or disable repository.
+ @param enable is a boolean
+ """
+ return self._do_job(jobs.YumSetRepositoryEnabled(repoid, enable))
+
+ # *************************************************************************
+ # Asynchronous jobs
+ # *************************************************************************
+ @job_request(async=True)
+ def install_package(self, pkg, async=False, force=False, **metadata):
+ """
+ Install package.
+ @param pkg is an instance of PackageInfo obtained with
+ get_package_list() or filter_packages() or a valid nevra as string.
+ Package must not be installed if force is False.
+ """
+ return self._do_job(_make_async_job(jobs.YumInstallPackage,
+ pkg, force=force, async=async, metadata=metadata))
+
+ @job_request(async=True)
+ def remove_package(self, pkg, async=False, **metadata):
+ """
+ @param pkg is an instance of PackageInfo obtained with
+ get_package_list() or filter_packages(), which must be installed
+ """
+ return self._do_job(_make_async_job(jobs.YumRemovePackage,
+ pkg, async=async, metadata=metadata))
+
+ @job_request(async=True)
+ def update_to_package(self, desired_pkg, async=False, **metadata):
+ """
+ @param desired_pkg is an instance of PackageInfo,
+ which must be available
+ """
+ return self._do_job(_make_async_job(jobs.YumUpdateToPackage,
+ desired_pkg, async=async, metadata=metadata))
+
+ @job_request(async=True)
+ def update_package(self, pkg,
+ async=False,
+ to_epoch=None,
+ to_version=None,
+ to_release=None,
+ force=False,
+ **metadata):
+ """
+ @param pkg is an instance of PackageInfo, which must be installed
+
+ The other parameters filter candidate available packages for update.
+ """
+ return self._do_job(_make_async_job(jobs.YumUpdatePackage,
+ pkg, async, to_epoch, to_version, to_release, force=force,
+ metadata=metadata))
+
+ @job_request(async=True)
+ def check_package(self, pkg, async=False, **metadata):
+ """
+ Return all necessary information from package database for package
+ verification.
+
+ :param pkg: (``PackageInfo``) An instance of PackageInfo
+ representing installed package or its nevra string.
+ :rtype: (``PackageCheck``)
+ """
+ return self._do_job(_make_async_job(jobs.YumCheckPackage,
+ pkg, async=async, metadata=metadata))
+
+ @job_request(async=True)
+ def check_package_file(self, pkg, file_name, async=False):
+ """
+ Return all necessary information from package database concerning
+ on particular file of package. If ``pkg`` does not contain
+ ``file_name``, ``FileNotFound`` error is raised.
+
+ :param pkg: (``PackageInfo``) An instance of PackageInfo
+ representing installed package or its nevra string.
+ :rtype: (``PackageFile``)
+ """
+ return self._do_job(_make_async_job(jobs.YumCheckPackageFile,
+ pkg, file_name, async=async))
+
+ @job_request(async=True)
+ def install_package_from_uri(self, uri,
+ async=False, update_only=False, force=False, **metadata):
+ """
+ Install package from uri.
+ @param uri is either remote url or local path.
+ """
+ return self._do_job(_make_async_job(jobs.YumInstallPackageFromURI,
+ uri, async, update_only, force=force, metadata=metadata))
+
+ # *************************************************************************
+ # Control of asynchronous jobs
+ # *************************************************************************
+ @job_request()
+ def get_job(self, jobid):
+ """
+ Return instance of ``YumJob`` with given ``jobid``.
+ """
+ return self._do_job(jobs.YumJobGet(jobid))
+
+ @job_request()
+ def get_job_list(self):
+ """
+ Return list of all asynchronous jobs.
+ """
+ return self._do_job(jobs.YumJobGetList())
+
+ @job_request()
+ def get_job_by_name(self, name):
+ """
+ Return asynchronous job filtered by its name.
+ """
+ return self._do_job(jobs.YumJobGetByName(name))
+
+ @job_request()
+ def set_job_priority(self, jobid, priority):
+ """
+ Change priority of asynchronous job. This will change
+ its order in queue, if it is still enqeueued.
+
+ Return object of job.
+ """
+ return self._do_job(jobs.YumJobSetPriority(jobid, priority))
+
+ @job_request()
+ def update_job(self, jobid, **kwargs):
+ """
+ Update metadata of job.
+
+ :param kwargs: (``dict``) Is a dictionary of job's property names
+ with mapped new values. Only keys given will be changed in
+ desired job.
+
+ **Note** that only keys, that do not affect job's priority or its
+ scheduling for deletion can be changed. See :ref:`YumJobUpdate`.
+ """
+ return self._do_job(jobs.YumJobUpdate(jobid, **kwargs))
+
+ @job_request()
+ def reschedule_job(self, jobid,
+ delete_on_completion, time_before_removal):
+ """
+ Change the scheduling of job for deletion.
+
+ :param delete_on_completion: (``bool``) Says, whether the job will
+ be scheduled for deletion at ``finished + time_before_removal``
+ time.
+ :param time_before_removal: (``int``) Number of seconds, after the job
+ is finished, it will be kept alive.
+ """
+ return self._do_job(jobs.YumJobReschedule(jobid,
+ delete_on_completion, time_before_removal))
+
+ @job_request()
+ def delete_job(self, jobid):
+ """
+ Delete job object. This can be called only on finished job.
+ """
+ return self._do_job(jobs.YumJobDelete(jobid))
+
+ @job_request()
+ def terminate_job(self, jobid):
+ """
+ Terminate job. This can be called only on *NEW* job.
+ """
+ return self._do_job(jobs.YumJobTerminate(jobid))
+
diff --git a/src/software/lmi/software/yumdb/errors.py b/src/software/lmi/software/yumdb/errors.py
new file mode 100644
index 0000000..44eb1c7
--- /dev/null
+++ b/src/software/lmi/software/yumdb/errors.py
@@ -0,0 +1,111 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Exceptions raisable by YumWorker.
+"""
+
+class YumDBError(Exception):
+ """Base class for all errors under yumdb package."""
+
+class DatabaseLockError(YumDBError):
+ """Raised, when the yum database can not be locked."""
+ pass
+
+class TransactionError(YumDBError):
+ """Base exception representing yum transaction processing error."""
+ pass
+class TransactionBuildFailed(TransactionError):
+ """Raised, when transaction building fails."""
+ pass
+class PackageAlreadyInstalled(TransactionError):
+ """Raised, when trying to install already installed package."""
+ def __init__(self, pkg):
+ TransactionError.__init__(self,
+ 'Package "%s" is already installed.' % pkg)
+class PackageOpenError(TransactionError):
+ """Raised, when trying to open package obtained from URI."""
+ def __init__(self, pkg, msg):
+ TransactionError.__init__(self,
+ 'Failed to open package "%s": %s' % (pkg, msg))
+class TransactionExecutionFailed(TransactionError):
+ """Raised, when YumBase.doTransaction() method fails."""
+ pass
+
+class PackageError(YumDBError):
+ """Generic exception for error concerning package handling."""
+ pass
+class PackageNotFound(PackageError):
+ """Raised, when requested package could not be found."""
+ pass
+class PackageNotInstalled(PackageError):
+ """Raised, when requested package is not installed for desired action."""
+ def __init__(self, pkg):
+ PackageError.__init__(self, 'Package "%s" is not installed.' % pkg)
+class FileNotFound(PackageError):
+ """
+ Raised, when requesting check on file that does not belong to
+ particular package.
+ """
+ pass
+
+class RepositoryError(YumDBError):
+ """Generic exception for error concerning repository handling."""
+ pass
+class RepositoryNotFound(RepositoryError):
+ """Raised, when requested repository cound not be found."""
+ def __init__(self, repoid):
+ RepositoryError.__init__(self, "No such repository: %s" % repoid)
+class RepositoryChangeError(RepositoryError):
+ """Raised, when modification of repository failed."""
+ pass
+
+class JobError(YumDBError):
+ """Generic exception for job handling."""
+ pass
+class UnknownJob(JobError):
+ """Raised, when no handler is available for given job on worker."""
+ pass
+class InvalidURI(JobError):
+ """Raised, when passed uri is not a valid one."""
+ def __init__(self, uri):
+ JobError.__init__(self, "Invalid uri: \"%s\"" % uri)
+class InvalidNevra(JobError):
+ """Raised when trying to instantiate job with invalid nevra string."""
+ pass
+class JobControlError(JobError):
+ """Generic exception for management of asynchronous jobs."""
+ pass
+class JobNotFound(JobControlError):
+ """Raised upon request for not existing asynchronous job."""
+ def __init__(self, target):
+ JobControlError.__init__(self, "job %s could not be found" % target)
+class InvalidJobState(JobControlError):
+ """
+ Raised when requested operation can not be executed on job in
+ its current state.
+ """
+ pass
+
+class IndicationError(YumDBError):
+ """Generic error for indication handling."""
+ pass
+
diff --git a/src/software/lmi/software/yumdb/jobmanager.py b/src/software/lmi/software/yumdb/jobmanager.py
new file mode 100644
index 0000000..a224905
--- /dev/null
+++ b/src/software/lmi/software/yumdb/jobmanager.py
@@ -0,0 +1,574 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+This is a module for ``JobManager`` which is a separate thread of
+``YumWorker`` process. It keeps a cache of asynchronous jobs and handles
+input and output queues.
+
+This module uses its own logging facilities because it runs in separeted
+process not having access to broker logging features.
+
+Before using ``JobManager``, module's variable ``JOB_TO_MODEL`` should
+be set to callable taking ``YumJob`` instance and returning its matching
+CIM abstraction instance.
+"""
+import heapq
+import inspect
+import logging
+import Queue
+import sys
+import threading
+import time
+import traceback
+
+from lmi.common.IndicationManager import IndicationManager
+from lmi.common.JobManager import JobManager as JM
+from lmi.software.yumdb import errors, jobs
+from lmi.software.yumdb.util import trace_function
+
+# This is a callable, which must be initialized before JobManager is used.
+# It should be a pointer to function, which takes a job and returns
+# corresponding CIM instance. It's used for sending indications.
+JOB_TO_MODEL = lambda job: None
+
+# Minimum time to keep asynchronous job in cache after completion. In seconds.
+MINIMUM_TIME_BEFORE_REMOVAL = 10
+
+# replacement for cmpi_logging.logger
+LOG = None
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+def job_handler(job_from_target=True):
+ """
+ Decorator for JobManager methods serving as handlers for control jobs.
+
+ Decorator locks the job_lock of manager's instance.
+ """
+ def _wrapper_jft(method):
+ """
+ It consumes "target" keyword argument (which is job's id) and makes
+ it an instance of YumJob. The method is then called with "job" argument
+ instead of "target".
+ """
+ logged = trace_function(method)
+
+ def _new_func(self, *args, **kwargs):
+ """Wrapper around method."""
+ if 'target' in kwargs:
+ kwargs['job'] = kwargs.pop('target')
+ callargs = inspect.getcallargs(method, self, *args, **kwargs)
+ target = callargs.pop('job')
+ with self._job_lock: #pylint: disable=W0212
+ if not target in self._async_jobs: #pylint: disable=W0212
+ raise errors.JobNotFound(target)
+ job = self._async_jobs[target] #pylint: disable=W0212
+ callargs['job'] = job
+ return logged(**callargs)
+ return _new_func
+
+ def _simple_wrapper(method):
+ """Just locks the job lock."""
+ def _new_func(self, *args, **kwargs):
+ """Wrapper around method."""
+ with self._job_lock: #pylint: disable=W0212
+ return method(self, *args, **kwargs)
+ return _new_func
+
+ if job_from_target:
+ return _wrapper_jft
+ else:
+ return _simple_wrapper
+
+class JobIndicationSender(object):
+ """
+ Makes creation and sending of indications easy. It keeps a reference
+ to job, which can be *snapshotted* for making CIM instance out of it.
+ These instances are then used to send indications via IndicationManager.
+
+ Typical usage::
+
+ sender = JobIndicationSender(im, job, [fltr_id1, fltr_id2])
+ ... # modify job
+ sender.snapshot()
+ sender.send()
+
+ **Note** that number of kept CIM instances won't exceed 2. First one
+ is created upon instantiation and the second one be calling
+ ``snapshot()``. Any successive call to ``snapshot()`` will overwrite
+ the second instance.
+ """
+
+ def __init__(self, indication_manager, job,
+ indications=JM.IND_JOB_CHANGED, new=None):
+ """
+ :param job (``YumJob``) Is job instance, which will be immediately
+ snapshoted as old instance and later as a new one.
+ :param indications (``list``) Can either be a list of indication ids
+ or a single indication id.
+ :param new (``YumJob``) A job instance stored as new.
+ """
+ if not isinstance(indication_manager, IndicationManager):
+ raise TypeError("indication_manager must be a subclass of"
+ " IndicationManager")
+ if not isinstance(job, jobs.YumJob):
+ raise TypeError("job must be an instance of YumJob")
+ if not new is None and not isinstance(new, jobs.YumJob):
+ raise TypeError("new must be an instance of YumJob")
+ self._indication_manager = indication_manager
+ self._job = job
+ self._old_instance = JOB_TO_MODEL(job)
+ if new is not None:
+ new = JOB_TO_MODEL(job)
+ self._new_instance = new
+ self._indications = set()
+ self.indication_ids = indications
+
+ @property
+ def job(self):
+ """
+ Return instance of ``YumJob``.
+ """
+ return self._job
+
+ @property
+ def indication_ids(self):
+ """
+ Return set of indication filter IDs.
+ """
+ return self._indications.copy()
+
+ @indication_ids.setter
+ def indication_ids(self, indication_ids):
+ """
+ Set the indication filter IDs.
+
+ :param indication_ids (``list``) Can be even single id.
+ """
+ if isinstance(indication_ids, basestring):
+ indication_ids = set([indication_ids])
+ self._indications = set(indication_ids)
+
+ @trace_function
+ def add_indication_ids(self, indication_ids):
+ """
+ Add filter IDs.
+ """
+ if isinstance(indication_ids, basestring):
+ indication_ids = set([indication_ids])
+ self._indications.update(indication_ids)
+
+ @trace_function
+ def snapshot(self):
+ """
+ Make a second CIM instance, overwriting previous one (not the first).
+ """
+ self._new_instance = JOB_TO_MODEL(self._job)
+
+ @trace_function
+ def send(self, make_snapshot=False):
+ """
+ Send all requested indications for given job.
+ """
+ if not self._indications:
+ raise errors.IndicationError(
+ "can not send any indication without id")
+ if make_snapshot:
+ self.snapshot()
+ if ( JM.IND_JOB_CHANGED in self._indications
+ and self._new_instance is None):
+ raise errors.IndicationError("no snapshot made for modified job")
+ for fltr_id in self._indications:
+ if fltr_id == JM.IND_JOB_CREATED:
+ LOG.debug("sending instance creation indication for job %s",
+ self._job)
+ self._indication_manager.send_instcreation(
+ self._new_instance if self._new_instance is not None
+ else self._old_instance,
+ fltr_id)
+ else:
+ LOG.debug("sending instance modification indication for job %s"
+ " with ID: %s", self._job, fltr_id)
+ self._indication_manager.send_instmodification(
+ self._old_instance, self._new_instance,
+ fltr_id)
+
+class JobManager(threading.Thread):
+ """
+ Separate thread for managing queue of jobs requested by client.
+ There are three kinds of jobs, that are handled differently:
+ * asynchronous - kept in _async_jobs dictionary until job is
+ deleted by request or it expires;
+ no reply is sent to client upon job's completion
+ * synchronous - reply is sent to client after job's completion;
+ no reference to the job is kept afterwards
+ * job control - they are not enqueued in _job_queue for YumWorker
+ to process, but are handled directly and in the FIFO order
+
+ Both asynchronous and synchronous jobs are enqueued in _job_queue
+ for YumWorker to obtain them. It's a priority queue sorting jobs by their
+ priority.
+ """
+ # enumeration of actions, that may be enqueued in calendar
+ ACTION_REMOVE = 0
+
+ ACTION_NAMES = ['remove']
+
+ def __init__(self, queue_in, queue_out, indication_manager):
+ threading.Thread.__init__(self, name="JobManager")
+ self._queue_in = queue_in
+ self._queue_out = queue_out
+ self._indication_manager = indication_manager
+ self._terminate = False
+
+ # (time, jobid, action)
+ self._calendar = []
+ # {jobid : job}
+ self._async_jobs = {}
+
+ # lock for critical access to _calendar, _async_jobs and _job_queue
+ self._job_lock = threading.RLock()
+ # priority queue of jobs that are processed by YumWorker
+ self._job_queue = []
+ # condition for YumWorker waiting on empty _job_queue
+ self._job_enqueued = threading.Condition(self._job_lock)
+
+ # *************************************************************************
+ # Private methods
+ # *************************************************************************
+ @trace_function
+ def _control_job(self, job):
+ """
+ Function dispatching job to handler for particular YumJob subclass.
+ """
+ try:
+ handler = {
+ # these are from YumDB client
+ jobs.YumJobGetList : self._handle_get_list,
+ jobs.YumJobGet : self._handle_get,
+ jobs.YumJobGetByName : self._handle_get_by_name,
+ jobs.YumJobSetPriority : self._handle_set_priority,
+ jobs.YumJobReschedule : self._handle_reschedule,
+ jobs.YumJobUpdate : self._handle_update,
+ jobs.YumJobDelete : self._handle_delete,
+ jobs.YumJobTerminate : self._handle_terminate,
+ }[job.__class__]
+ LOG.info("processing control job %s", str(job))
+ except KeyError:
+ raise errors.UnknownJob("No handler for job \"%s\"." %
+ job.__class__.__name__)
+ return handler(**job.job_kwargs)
+
+ @trace_function
+ def _enqueue_job(self, job):
+ """
+ Insert incoming job into _job_queue.
+ """
+ if isinstance(job, jobs.YumJobControl):
+ result = job.RESULT_SUCCESS
+ job.start()
+ try:
+ data = self._control_job(job)
+ except Exception: #pylint: disable=W0703
+ result = job.RESULT_ERROR
+ data = sys.exc_info()
+ data = (data[0], data[1], traceback.format_tb(data[2]))
+ LOG.exception("control job %s failed", job)
+ job.finish(result, data)
+ LOG.debug("sending reply for %s: (%s, %s)", job,
+ job.ResultNames[job.result], job.result_data)
+ self._queue_out.put(job)
+ else:
+ if job is None:
+ LOG.debug('received terminating command')
+ self._terminate = True
+ LOG.debug('job %s enqued for YumWorker to handle', job)
+ heapq.heappush(self._job_queue, job)
+ if getattr(job, 'async', False) is True:
+ ind = self._prepare_indication_for(job, JM.IND_JOB_CREATED)
+ self._async_jobs[job.jobid] = job
+ ind.send()
+ self._job_enqueued.notify()
+
+ @trace_function
+ def _schedule_event(self, after, jobid, action):
+ """
+ Enqueue event into calendar. Event consists of time, jobid and
+ action.
+ """
+ schedule_at = time.time() + after
+ for (sched, jid, act) in self._calendar:
+ if jid == jobid and act == action:
+ if sched <= schedule_at: # same event already scheduled
+ return
+ # schedule it for early time
+ LOG.debug('rescheduling action %s on job %d to take place'
+ ' after %d seconds (instead of %d)',
+ self.ACTION_NAMES[action], jid, after,
+ sched - schedule_at + after)
+ self._calendar.remove((sched, jid, act))
+ self._calendar.append((schedule_at, jid, act))
+ heapq.heapify(self._calendar)
+ return
+ LOG.debug('scheduling action %s on job %d to take place after '
+ ' %d seconds', self.ACTION_NAMES[action], jobid, after)
+ heapq.heappush(self._calendar, (schedule_at, jobid, action))
+
+ @trace_function
+ def _run_event(self, jobid, action):
+ """
+ Process event from calendar.
+ """
+ if action == self.ACTION_REMOVE:
+ with self._job_lock:
+ del self._async_jobs[jobid]
+ else:
+ msg = "unsupported action: %s" % action
+ raise ValueError(msg)
+
+ @trace_function
+ def _prepare_indication_for(self, job, *args, **kwargs):
+ """
+ Return instance of ``JobIndicationSender``.
+ """
+ return JobIndicationSender(self._indication_manager, job,
+ *args, **kwargs)
+
+ # *************************************************************************
+ # Job handlers
+ # *************************************************************************
+ @job_handler()
+ def _handle_get(self, job): #pylint: disable=R0201
+ """@return job object"""
+ return job
+
+ @job_handler(False)
+ def _handle_get_list(self):
+ """@return list of all asynchronous jobs"""
+ with self._job_lock:
+ return sorted(self._async_jobs.values())
+
+ @job_handler(False)
+ def _handle_get_by_name(self, target):
+ """@return job object filtered by name"""
+ for job in self._async_jobs.values():
+ if 'name' in job.metadata and target == job.metadata['name']:
+ return job
+ raise errors.JobNotFound(target)
+
+ @job_handler()
+ def _handle_set_priority(self, job, new_priority):
+ """
+ Modify job's priority and updates its position in queue.
+ @return modified job object
+ """
+ if not isinstance(new_priority, (int, long)):
+ raise TypeError('priority must be an integer')
+ if job.priority != new_priority:
+ ind = self._prepare_indication_for(job)
+ job.update(priority=new_priority)
+ if job in self._job_queue:
+ heapq.heapify(self._job_queue)
+ ind.send(True)
+ return job
+
+ @job_handler()
+ def _handle_reschedule(self, job,
+ delete_on_completion,
+ time_before_removal):
+ """
+ Changes job's schedule for its deletion.
+ """
+ if ( job.delete_on_completion == delete_on_completion
+ and job.time_before_removal == time_before_removal):
+ return
+ if job.finished and job.delete_on_completion:
+ for i, event in enumerate(self._calendar):
+ if event[1] == job.jobid and event[2] == self.ACTION_REMOVE:
+ del self._calendar[i]
+ heapq.heapify(self._calendar)
+ break
+ ind = self._prepare_indication_for(job)
+ if delete_on_completion:
+ schedule_at = time_before_removal
+ if job.finished:
+ schedule_at = job.finished + schedule_at - time.time()
+ self._schedule_event(schedule_at, job.jobid, self.ACTION_REMOVE)
+ job.delete_on_completion = delete_on_completion
+ job.time_before_removal = time_before_removal
+ ind.send(True)
+ return job
+
+ @job_handler()
+ def _handle_update(self, job, data): #pylint: disable=R0201
+ """
+ Updates any job metadata.
+ """
+ ind = self._prepare_indication_for(job)
+ job.update(**data)
+ ind.send(True)
+ return job
+
+ @job_handler()
+ def _handle_delete(self, job):
+ """
+ Deletes finished asynchronous job.
+ """
+ if not job.finished:
+ raise errors.InvalidJobState(
+ 'can not delete unfinished job "%s"' % job)
+ try:
+ self._job_queue.remove(job)
+ heapq.heapify(self._job_queue)
+ LOG.debug('job "%s" removed from queue', job)
+ except ValueError:
+ LOG.debug('job "%s" not started and not enqueued', job)
+ del self._async_jobs[job.jobid]
+ return job
+
+ @job_handler()
+ def _handle_terminate(self, job):
+ """
+ Terminates not started job.
+ """
+ if job.started and not job.finished:
+ raise errors.InvalidJobState('can not kill running job "%s"' % job)
+ if job.finished:
+ raise errors.InvalidJobState('job "%s" already finished' % job)
+ self._job_queue.remove(job)
+ heapq.heapify(self._job_queue)
+ ind = self._prepare_indication_for(job)
+ job.finish(result=job.RESULT_TERMINATED)
+ ind.send(True)
+ LOG.info('terminated not started job "%s"', job)
+ return job
+
+ # *************************************************************************
+ # Public properties
+ # *************************************************************************
+ @property
+ def queue_in(self):
+ """Incoming queue for YumJob instances."""
+ return self._queue_in
+
+ @property
+ def queue_out(self):
+ """Output queue for results."""
+ return self._queue_out
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ @trace_function
+ def finish_job(self, job, result, result_data):
+ """
+ This should be called for any job by YumWorker after the job is
+ processed.
+
+ If the job is synchronous, reply is send at once. Otherwise the result
+ is stored for later client's query in the job itself.
+ """
+ with self._job_lock:
+ if job.state != job.RUNNING:
+ raise errors.InvalidJobState(
+ 'can not finish not started job "%s"' % job)
+ if getattr(job, 'async', False):
+ ind = self._prepare_indication_for(job,
+ (JM.IND_JOB_CHANGED, JM.IND_JOB_PERCENT_UPDATED))
+ job.finish(result, result_data)
+ if getattr(job, 'async', False):
+ if job.delete_on_completion:
+ schedule_at = max( job.time_before_removal
+ , MINIMUM_TIME_BEFORE_REMOVAL)
+ self._schedule_event(schedule_at, job.jobid,
+ self.ACTION_REMOVE)
+ if result == job.RESULT_SUCCESS:
+ ind.add_indication_ids(JM.IND_JOB_SUCCEEDED)
+ elif result == job.RESULT_ERROR:
+ ind.add_indication_ids(JM.IND_JOB_FAILED)
+ ind.send(True)
+ else:
+ LOG.debug("sending reply for %s: (%s, %s)", job,
+ job.ResultNames[job.result], job.result_data)
+ self._queue_out.put(job)
+ return job
+
+ @trace_function
+ def get_job(self, block=True, timeout=None):
+ """
+ Method supposed to be used only by YumWorker. It pops the first job
+ from _job_queue, starts it and returns it.
+ """
+ start = time.time()
+ with self._job_lock:
+ if len(self._job_queue) == 0 and not block:
+ raise Queue.Empty
+ while len(self._job_queue) == 0:
+ if timeout:
+ LOG.debug('waiting for job for %s seconds' % timeout)
+ self._job_enqueued.wait(timeout)
+ if len(self._job_queue) == 0:
+ now = time.time()
+ if timeout > now - start:
+ raise Queue.Empty
+ job = heapq.heappop(self._job_queue)
+ if job is not None:
+ if getattr(job, "async", False):
+ ind = self._prepare_indication_for(job,
+ (JM.IND_JOB_CHANGED, JM.IND_JOB_PERCENT_UPDATED))
+ job.start()
+ ind.send(True)
+ else:
+ job.start()
+ return job
+
+ def run(self):
+ """The entry point of thread."""
+ global LOG #pylint: disable=W0603
+ LOG = logging.getLogger(__name__)
+ LOG.info("%s thread started", self.name)
+
+ while self._terminate is False:
+ try:
+ timeout = None
+ with self._job_lock:
+ if len(self._calendar) > 0:
+ timeout = self._calendar[0][0] - time.time()
+ LOG.debug('waiting on input queue for job%s',
+ (' with timeout %s' % timeout) if timeout else '')
+ job = self._queue_in.get(timeout=timeout)
+ with self._job_lock:
+ self._enqueue_job(job)
+ while not self._queue_in.empty():
+ # this won't throw
+ self._enqueue_job(self._queue_in.get_nowait())
+
+ except Queue.Empty:
+ with self._job_lock:
+ while ( len(self._calendar)
+ and self._calendar[0][0] < time.time()):
+ _, jobid, action = heapq.heappop(self._calendar)
+ LOG.info('running action %s on job(id=%d)',
+ self.ACTION_NAMES[action], jobid)
+ self._run_event(jobid, action)
+ LOG.info('%s thread terminating', self.name)
+
diff --git a/src/software/lmi/software/yumdb/jobs.py b/src/software/lmi/software/yumdb/jobs.py
new file mode 100644
index 0000000..346ff17
--- /dev/null
+++ b/src/software/lmi/software/yumdb/jobs.py
@@ -0,0 +1,668 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Define job classes representing kinds of jobs of worker process.
+"""
+
+import os
+import threading
+import time
+import yum
+
+from lmi.software import util
+from lmi.software.yumdb import errors
+from lmi.software.yumdb.packageinfo import PackageInfo
+from lmi.software.yumdb.repository import Repository
+
+DEFAULT_JOB_PRIORITY = 10
+# in seconds
+DEFAULT_TIME_BEFORE_REMOVAL = 60 * 5
+
+class YumJob(object): #pylint: disable=R0903
+ """
+ Base class for any job, that is processable by YumWorker process.
+ It contains jobid attribute, that must be unique for
+ each job, it's counted from zero a incremented after each creation.
+
+ metadata attribute typically contain:
+ name - name of job, that is modifiable by user
+ method - identificator of method, that lead to creation of job
+ """
+ __slots__ = ( 'jobid', 'created', 'started', 'finished', 'last_change'
+ , 'priority', 'result', 'result_data')
+
+ # 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
+
+ # job state enumeration
+ NEW, RUNNING, COMPLETED, TERMINATED, EXCEPTION = range(5)
+ # job result enumeration
+ RESULT_SUCCESS, RESULT_TERMINATED, RESULT_ERROR = range(3)
+
+ ResultNames = ("success", "terminated", "error")
+
+ @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
+
+ @classmethod
+ def handle_ignore_job_props(cls):
+ """
+ @return set of job properties, that does not count as job's handler
+ arguments - job handler does not care fore metadata, jobid, priority,
+ etc...
+ """
+ return set(YumJob.__slots__)
+
+ def __init__(self, priority=10):
+ if not isinstance(priority, (int, long)):
+ raise TypeError("priority must be integer")
+ self.jobid = self._get_job_id()
+ self.started = None
+ self.finished = None
+ self.priority = priority
+ self.created = time.time()
+ self.last_change = self.created
+ self.result = None
+ self.result_data = None
+
+ @property
+ def state(self):
+ """
+ @return integer representing job's state
+ """
+ if not self.started:
+ return self.NEW
+ if not self.finished:
+ return self.RUNNING
+ if self.result == self.RESULT_ERROR:
+ return self.EXCEPTION
+ if self.result == self.RESULT_TERMINATED:
+ return self.TERMINATED
+ return self.COMPLETED
+
+ @property
+ def job_kwargs(self):
+ """
+ Jobs are in worker handled in handlers specific for each subclass.
+ These handlers are methods of worker. They accepts concrete arguments
+ that can be obtained from job by invoking 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
+ and not slot in cls.handle_ignore_job_props()):
+ kwargs[slot] = getattr(self, slot)
+ cls = cls.__bases__[0]
+ for prop in YumJob.__slots__:
+ kwargs.pop(prop, None)
+ return kwargs
+
+ def start(self):
+ """Modify the state of job to RUNNING."""
+ if self.started:
+ raise errors.InvalidJobState("can not start already started job")
+ self.started = time.time()
+ self.last_change = self.started
+
+ def finish(self, result, data=None):
+ """
+ Modify the state of job to one of {COMPLETED, EXCEPTION, TERMINATED}.
+ Depending on result parameter.
+ """
+ if not self.started and result != self.RESULT_TERMINATED:
+ raise errors.InvalidJobState("can not finish not started job")
+ self.finished = time.time()
+ if result == self.RESULT_TERMINATED:
+ self.started = self.finished
+ self.result = result
+ self.result_data = data
+ self.last_change = self.finished
+
+ def update(self, **kwargs):
+ """Change job's properties."""
+ change = False
+ for key, value in kwargs.items():
+ if getattr(self, key) != value:
+ setattr(self, key, value)
+ change = True
+ if change is True:
+ self.last_change = time.time()
+
+ def __eq__(self, other):
+ return self.__class__ is other.__class__ and self.jobid == other.jobid
+
+ def __ne__(self, other):
+ return ( self.__class__ is not other.__class__
+ or self.jobid != other.jobid)
+
+ def __lt__(self, other):
+ """
+ JobControl jobs have the highest priority.
+ """
+ return ( ( isinstance(self, YumJobControl)
+ and not isinstance(other, YumJobControl))
+ or ( self.priority < other.priority
+ or ( self.priority == other.priority
+ and ( self.jobid < other.jobid
+ or ( self.jobid == other.jobid
+ and (self.created < other.created))))))
+
+ def __cmp__(self, other):
+ if ( isinstance(self, YumJobControl)
+ and not isinstance(other, YumJobControl)):
+ return -1
+ if ( not isinstance(self, YumJobControl)
+ and isinstance(other, YumJobControl)):
+ return 1
+ if self.priority < other.priority:
+ return -1
+ if self.priority > other.priority:
+ return 1
+ if self.jobid < other.jobid:
+ return -1
+ if self.jobid > other.jobid:
+ return 1
+ if self.created < other.created:
+ return -1
+ if self.created > other.created:
+ return 1
+ return 0
+
+ def __str__(self):
+ return "%s(id=%d,p=%d)" % (
+ self.__class__.__name__, self.jobid, self.priority)
+
+ def __getstate__(self):
+ ret = self.job_kwargs
+ for prop in self.handle_ignore_job_props():
+ ret[prop] = getattr(self, prop)
+ return ret
+
+ def __setstate__(self, state):
+ for k, value in state.items():
+ setattr(self, k, value)
+
+class YumAsyncJob(YumJob): #pylint: disable=R0903
+ """
+ Base class for jobs, that support asynchronnous execution.
+ No reply is sent upon job completition or error. The results are
+ kept on server.
+ """
+ __slots__ = ( 'async'
+ , 'delete_on_completion'
+ , 'time_before_removal'
+ , 'metadata')
+
+ @classmethod
+ def handle_ignore_job_props(cls):
+ return YumJob.handle_ignore_job_props().union(YumAsyncJob.__slots__)
+
+ def __init__(self, priority=10, async=False, metadata=None):
+ YumJob.__init__(self, priority)
+ self.async = bool(async)
+ self.delete_on_completion = True
+ self.time_before_removal = DEFAULT_TIME_BEFORE_REMOVAL
+ if metadata is None and self.async is True:
+ metadata = {}
+ self.metadata = metadata
+
+ def __str__(self):
+ return "%s(id=%d,p=%d%s%s)" % (
+ self.__class__.__name__, self.jobid,
+ self.priority,
+ ',async' if self.async else '',
+ (',name="%s"'%self.metadata['name'])
+ if self.metadata and 'name' in self.metadata else '')
+
+ def update(self, **kwargs):
+ if 'metadata' in kwargs:
+ self.metadata.update(kwargs.pop('metadata'))
+ return YumJob.update(self, **kwargs)
+
+# *****************************************************************************
+# Job control funtions
+# *****************************************************************************
+class YumJobControl(YumJob): #pylint: disable=R0903
+ """Base class for any job used for asynchronous jobs management."""
+ pass
+
+class YumJobGetList(YumJobControl): #pylint: disable=R0903
+ """Request for obtaining list of all asynchronous jobs."""
+ pass
+
+class YumJobOnJob(YumJobControl):
+ """
+ Base class for any control job acting upon particular asynchronous job.
+ """
+ __slots__ = ('target', )
+ def __init__(self, target):
+ YumJobControl.__init__(self)
+ if not isinstance(target, (int, long)):
+ raise TypeError("target must be an integer")
+ self.target = target
+
+class YumJobGet(YumJobOnJob): #pylint: disable=R0903
+ """Get job object by its id."""
+ pass
+
+class YumJobGetByName(YumJobOnJob): #pylint: disable=R0903
+ """Get job object by its name property."""
+ def __init__(self, name):
+ YumJobOnJob.__init__(self, -1)
+ self.target = name
+
+class YumJobSetPriority(YumJobOnJob): #pylint: disable=R0903
+ """Change priority of job."""
+ __slots__ = ('new_priority', )
+
+ def __init__(self, target, priority):
+ YumJobOnJob.__init__(self, target)
+ self.new_priority = priority
+
+class YumJobUpdate(YumJobOnJob): #pylint: disable=R0903
+ """
+ .. _YumJobUpdate:
+
+ Update job's metadata. There are some forbidden properties, that
+ can not be changed in this way. Those are all affecting job's priority
+ and its scheduling for deletion. Plus any that store job's state.
+ All forbidden properties are listed in ``FORBIDDEN_PROPERTIES``.
+ """
+ __slots__ = ('data', )
+ FORBIDDEN_PROPERTIES = (
+ 'async', 'jobid', 'created', 'started', 'priority', 'finished',
+ 'delete_on_completion', 'time_before_removal', 'last_change')
+
+ def __init__(self, target, **kwargs):
+ YumJobOnJob.__init__(self, target)
+ assert not set.intersection(
+ set(YumJobUpdate.FORBIDDEN_PROPERTIES), set(kwargs))
+ self.data = kwargs
+
+class YumJobReschedule(YumJobOnJob): #pylint: disable=R0903
+ """Change the schedule of job's deletion."""
+ __slots__ = ('delete_on_completion', 'time_before_removal')
+ def __init__(self, target, delete_on_completion, time_before_removal):
+ YumJobOnJob.__init__(self, target)
+ if not isinstance(time_before_removal, (int, long, float)):
+ raise TypeError("time_before_removal must be float")
+ self.delete_on_completion = bool(delete_on_completion)
+ self.time_before_removal = time_before_removal
+
+class YumJobDelete(YumJobOnJob): #pylint: disable=R0903
+ """Delete job - can only be called on finished job."""
+ pass
+
+class YumJobTerminate(YumJobOnJob): #pylint: disable=R0903
+ """
+ Can only be called on not yet started job.
+ Running job can not be terminated.
+ """
+ pass
+
+# *****************************************************************************
+# Yum API functions
+# *****************************************************************************
+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
+ * installed lists all installed packages; more packages with
+ the same name can be installed varying in their architecture
+ * avail_notinst lists all available, not installed packages;
+ allow_duplicates must be True to include older packages (but still
+ available)
+ * avail_reinst lists all installed packages, that are available;
+ package can be installed, but not available anymore due to updates
+ of repository, where only the newest packages are kept
+ * available lists a union of avail_notinst and avail_reinst
+ * all lists union of installed and avail_notinst
+
+ allow_duplicates - whether multiple packages can be present
+ in result for single (name, arch) of package differing
+ in their version
+
+ sort - whether to sort packages by nevra
+
+ include_repos - either a string passable to RepoStorage.enableRepo()
+ or a list of repository names, that will be temporared enabled before
+ listing packages; this is applied after disabling of repositories
+
+ exclude_repos - either a string passable to RepoStorage.disableRepo()
+ or a list of repository names, that will be temporared disabled before
+ listing packages; this is applied before enabling of repositories
+
+ Worker replies with [pkg1, pkg2, ...].
+ """
+ __slots__ = ('kind', 'allow_duplicates', 'sort', 'include_repos',
+ 'exclude_repos')
+
+ SUPPORTED_KINDS = ( 'installed', 'available', 'avail_reinst'
+ , 'avail_notinst', 'all')
+
+ def __init__(self, kind, allow_duplicates, sort=False,
+ include_repos=None, exclude_repos=None):
+ 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))
+ for arg in ('include_repos', 'exclude_repos'):
+ val = locals()[arg]
+ if ( not val is None
+ and not isinstance(arg, (tuple, list, basestring))):
+ raise TypeError("expected list or string for %s" % arg)
+ self.kind = kind
+ self.allow_duplicates = bool(allow_duplicates)
+ self.sort = bool(sort)
+ self.include_repos = include_repos
+ self.exclude_repos = exclude_repos
+
+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', 'repoid')
+
+ def __init__(self, kind, allow_duplicates,
+ sort=False, include_repos=None, exclude_repos=None,
+ name=None, epoch=None, version=None,
+ release=None, arch=None,
+ nevra=None, evra=None,
+ envra=None,
+ repoid=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,
+ include_repos=include_repos, exclude_repos=exclude_repos)
+ 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
+ self.repoid = repoid
+
+class YumSpecificPackageJob(YumAsyncJob): #pylint: disable=R0903
+ """
+ Abstract job taking instance of yumdb.PackageInfo as argument or
+ package's nevra.
+ Arguments:
+ pkg - plays different role depending on job subclass;
+ can also be a nevra
+ """
+ __slots__ = ('pkg', )
+ def __init__(self, pkg, async=False, metadata=None):
+ if isinstance(pkg, basestring):
+ if not util.RE_NEVRA_OPT_EPOCH.match(pkg):
+ raise errors.InvalidNevra('not a valid nevra "%s"' % pkg)
+ elif not isinstance(pkg, PackageInfo):
+ raise TypeError("pkg must be either string or instance"
+ " of PackageInfo")
+ YumAsyncJob.__init__(self, async=async, metadata=metadata)
+ self.pkg = pkg
+
+class YumInstallPackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Job requesting installation of specific package.
+ pkg argument should be available.
+ Arguments:
+ pkg - same as in YumSpecificPackageJob
+ force is a boolean saying:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
+
+ Worker replies with new instance of package.
+ """
+ __slots__ = ('force', )
+ def __init__(self, pkg, async=False, force=False, metadata=None):
+ YumSpecificPackageJob.__init__(
+ self, pkg, async=async, metadata=metadata)
+ self.force = bool(force)
+
+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
+ force is a boolean, that has meaning only when update_only is False:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
+
+ The arguments more given, the more complete filter of candidates.
+
+ Worker replies with new instance of package.
+ """
+ __slots__ = ('to_epoch', 'to_version', 'to_release', 'force')
+
+ def __init__(self, pkg, async=False,
+ to_epoch=None, to_version=None, to_release=None, force=False,
+ metadata=None):
+ if not isinstance(pkg, PackageInfo):
+ raise TypeError("pkg must be instance of yumdb.PackageInfo")
+ YumSpecificPackageJob.__init__(
+ self, pkg, async=async, metadata=metadata)
+ self.to_epoch = to_epoch
+ self.to_version = to_version
+ self.to_release = to_release
+ self.force = bool(force)
+
+class YumCheckPackage(YumSpecificPackageJob): #pylint: disable=R0903
+ """
+ Request verification information for instaled package and its files.
+
+ Arguments:
+ pkg - either instance of PackageInfo or nevra string.
+ In latter case it will be replaced for YumWorker with instance
+ of PackageInfo.
+
+ Worker replies with ``(pkg_info, pkg_check)``.
+ where:
+ ``pkg_info`` - is instance of PackageInfo
+ ``pkg_check`` - new instance of yumdb.PackageCheck
+ """
+ def __init__(self, pkg, async=False, metadata=None):
+ YumSpecificPackageJob.__init__(self, pkg, async=async,
+ metadata=metadata)
+ if isinstance(pkg, PackageInfo) and not pkg.installed:
+ raise ValueError("package must be installed to check it")
+
+class YumCheckPackageFile(YumCheckPackage): #pylint: disable=R0903
+ """
+ Request verification information for particular file of installed
+ package.
+
+ Worker replies with ``(pkg_info, pkg_check)``.
+ where:
+ ``pkg_info`` - is instance of PackageInfo
+ ``pkg_check`` - new instance of yumdb.PackageCheck containing only
+ requested file.
+ """
+ __slots__ = ('file_name', )
+ def __init__(self, pkg, file_name, *args, **kwargs):
+ YumCheckPackage.__init__(self, pkg, *args, **kwargs)
+ if not isinstance(file_name, basestring):
+ raise TypeError("file_name must be string")
+ self.file_name = file_name
+
+class YumInstallPackageFromURI(YumAsyncJob): #pylint: disable=R0903
+ """
+ Job requesting installation of specific package from URI.
+ Arguments:
+ uri is either a path to rpm package on local filesystem or url
+ of rpm stored on remote host
+ update_only is a boolean:
+ True -> install the package only if the older version is installed
+ False -> install the package if it's not already installed
+ force is a boolean, that has meaning only when update_only is False:
+ True -> reinstall the package if it's already installed
+ False -> fail if the package is already installed
+
+ Worker replies with new instance of package.
+ """
+ __slots__ = ('uri', 'update_only', "force")
+ def __init__(self, uri, async=False, update_only=False, force=False,
+ metadata=None):
+ if not isinstance(uri, basestring):
+ raise TypeError("uri must be a string")
+ if uri.startswith('file://'):
+ uri = uri[len('file://'):]
+ if not yum.misc.re_remote_url(uri) and not os.path.exists(uri):
+ raise errors.InvalidURI(uri)
+ YumAsyncJob.__init__(self, async=async, metadata=metadata)
+ self.uri = uri
+ self.update_only = bool(update_only)
+ self.force = bool(force)
+
+class YumGetRepositoryList(YumJob): #pylint: disable=R0903
+ """
+ Job requesing a list of repositories.
+ Arguments:
+ kind - supported values are in SUPPORTED_KINDS tuple
+
+ Worker replies with [repo1, repo2, ...].
+ """
+ __slots__ = ('kind', )
+
+ SUPPORTED_KINDS = ('all', 'enabled', 'disabled')
+
+ def __init__(self, kind):
+ 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
+
+class YumFilterRepositories(YumGetRepositoryList): #pylint: disable=R0903
+ """
+ Job similar to YumGetRepositoryList, but allowing to specify
+ filter on packages.
+ Arguments (plus those in YumGetRepositoryList):
+ name, gpg_check, repo_gpg_check
+
+ Some of those are redundant, but filtering is optimized for
+ speed, so supplying all of them won't affect performance.
+
+ Worker replies with [repo1, repo2, ...].
+ """
+ __slots__ = ('repoid', 'gpg_check', 'repo_gpg_check')
+
+ def __init__(self, kind,
+ repoid=None, gpg_check=None, repo_gpg_check=None):
+ YumGetRepositoryList.__init__(self, kind)
+ self.repoid = repoid
+ self.gpg_check = None if gpg_check is None else bool(gpg_check)
+ self.repo_gpg_check = (
+ None if repo_gpg_check is None else bool(repo_gpg_check))
+
+class YumSpecificRepositoryJob(YumJob): #pylint: disable=R0903
+ """
+ Abstract job taking instance of yumdb.Repository as argument.
+ Arguments:
+ repo - (``Repository`` or ``str``) plays different role depending
+ on job subclass
+ """
+ __slots__ = ('repo', )
+ def __init__(self, repo):
+ if not isinstance(repo, (Repository, basestring)):
+ raise TypeError("repoid must be either instance of"
+ " yumdb.Repository or string")
+ YumJob.__init__(self)
+ self.repo = repo
+
+class YumSetRepositoryEnabled(YumSpecificRepositoryJob):#pylint: disable=R0903
+ """
+ Job allowing to enable or disable repository.
+ Arguments:
+ enable - (``boolean``) representing next state
+ """
+ __slots__ = ('enable', )
+ def __init__(self, repo, enable):
+ YumSpecificRepositoryJob.__init__(self, repo)
+ self.enable = bool(enable)
+
diff --git a/src/software/lmi/software/yumdb/packagecheck.py b/src/software/lmi/software/yumdb/packagecheck.py
new file mode 100644
index 0000000..e66078c
--- /dev/null
+++ b/src/software/lmi/software/yumdb/packagecheck.py
@@ -0,0 +1,235 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module with definition of RPM package check class.
+"""
+
+from collections import OrderedDict
+from datetime import datetime
+import grp
+import logging
+import os
+import pwd
+import rpm
+import yum
+
+from lmi.software.yumdb import errors
+
+CHECKSUMTYPE_STR2NUM = dict((val.lower(), k) for (k, val) in
+ yum.constants.RPM_CHECKSUM_TYPES.items())
+
+( FILE_TYPE_UNKNOWN
+, FILE_TYPE_FILE
+, FILE_TYPE_DIRECTORY
+, FILE_TYPE_SYMLINK
+, FILE_TYPE_FIFO
+, FILE_TYPE_CHARACTER_DEVICE
+, FILE_TYPE_BLOCK_DEVICE
+) = range(7)
+
+FILE_TYPE_NAMES = ( 'unknown', 'file', 'directory', 'symlink', 'fifo'
+ , 'character device', 'block device')
+
+class PackageFile(object):
+ """
+ Metadata related to particular file on filesystem belonging to RPM package.
+ Data contained here are from RPM database.
+
+ Attributes:
+ ``path`` - (``str``) Absolute path of file.
+ ``file_type`` - (``int``) One of ``FILE_TYPE_*`` identifiers above.
+ ``uid`` - (``int``) User ID.
+ ``gid`` - (``int``) Group ID.
+ ``mode`` - (``int``) Raw file mode.
+ ``device`` - (``int``) Device number.
+ ``mtime`` - (``int``) Last modification time in seconds.
+ ``size`` - (``long``) File size as a number of bytes.
+ ``link_target`` - (``str``) Link target of symlink. None if ``file_type``
+ is not symlink.
+ ``checksum`` - (``str``) Checksum as string in hexadecimal format.
+ None if file is not a regular file.
+ """
+ __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):
+ if not isinstance(file_type, basestring):
+ raise TypeError("file_type must be a string")
+ 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
+ try:
+ self.file_type = FILE_TYPE_NAMES.index(file_type.lower())
+ except ValueError:
+ logging.getLogger(__name__).error('unrecognized file type "%s" for'
+ ' file "%s"', file_type, path)
+ self.file_type = FILE_TYPE_NAMES[FILE_TYPE_UNKNOWN]
+ self.uid = uid
+ self.gid = gid
+ self.mode = mode
+ self.device = device
+ self.mtime = mtime
+ self.size = size
+ self.link_target = (link_target
+ if self.file_type == FILE_TYPE_SYMLINK else None)
+ self.checksum = checksum if self.file_type == FILE_TYPE_FILE else None
+
+ @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__ = ("objid", "file_checksum_type", "files")
+
+ def __init__(self, objid, file_checksum_type, files=None):
+ """
+ @param objid 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.objid = objid
+ 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, file_name=None):
+ """
+ Create instance of PackageCheck from instance of
+ yum.packages._RPMVerifyPackage.
+
+ :param file_name: (``str``) If not None, causes result to have just
+ one instance of ``PackageFile`` matching this file_name.
+ If it's not found in the package, ``FileNotFound`` will be raised.
+ :rtype (``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:
+ if file_name is not None and file_name != vpf.filename:
+ continue
+ 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]
+ )
+ if file_name is not None:
+ break
+ if file_name is not None and len(files) < 1:
+ raise errors.FileNotFound('File "%s" not found in package "%s".' % (
+ file_name, pkg.nevra))
+ return res
+
diff --git a/src/software/lmi/software/yumdb/packageinfo.py b/src/software/lmi/software/yumdb/packageinfo.py
new file mode 100644
index 0000000..7a40d19
--- /dev/null
+++ b/src/software/lmi/software/yumdb/packageinfo.py
@@ -0,0 +1,181 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding an abstraction for RPM package.
+"""
+
+from datetime import datetime
+import yum
+
+from lmi.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 "objid" is provided.
+ """
+ __slots__ = (
+ "objid",
+ "name", "epoch", "version", "release", "architecture",
+ 'summary', 'description', 'license', 'group', 'vendor',
+ "repoid", 'size',
+ 'installed', # boolean
+ 'install_time' # datetime instance
+ )
+
+ def __init__(self, objid, name, epoch, version, release, arch, **kwargs):
+ """
+ @param objid is an in of original yum package object, which is used
+ by server for subsequent operations on this package requested by client
+ """
+ self.objid = objid
+ 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.repoid = kwargs.pop("repoid", 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', 'repoid'))
+
+ # *************************************************************************
+ # 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 __eq__(self, other):
+ return ( self.name == other.name
+ and self.version == other.version
+ and self.release == other.release
+ and self.arch == other.arch
+ and self.epoch == other.epoch
+ and ( (self.repoid is None or other.repoid is None)
+ or (self.repoid == other.repoid)))
+
+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',
+ 'repoid'))
+ 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/lmi/software/yumdb/process.py b/src/software/lmi/software/yumdb/process.py
new file mode 100644
index 0000000..85abe61
--- /dev/null
+++ b/src/software/lmi/software/yumdb/process.py
@@ -0,0 +1,914 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding the code of separate process accessing the YUM API.
+"""
+
+import errno
+from itertools import chain
+import logging
+from multiprocessing import Process
+import os
+import Queue as TQueue # T as threaded
+import sys
+import time
+import traceback
+from urlgrabber.grabber import default_grabber
+import weakref
+import yum
+
+from lmi.software import util
+from lmi.software.yumdb import errors
+from lmi.software.yumdb import jobs
+from lmi.software.yumdb import packageinfo
+from lmi.software.yumdb import packagecheck
+from lmi.software.yumdb import repository
+from lmi.software.yumdb.jobmanager import JobManager
+from lmi.software.yumdb.util import trace_function, setup_logging
+
+# *****************************************************************************
+# Constants
+# *****************************************************************************
+# interval in seconds
+FREE_DATABASE_TIMEOUT = 60
+LOCK_WAIT_INTERVAL = 0.5
+RPMDB_PATH = '/var/lib/rpm/Packages'
+LOG = None
+
+# *****************************************************************************
+# Utilities
+# ****************************************************************************
+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)
+
+ match = None
+ if "nevra" in filters:
+ match = util.RE_NEVRA.match(filters["nevra"])
+ elif "envra" in filters:
+ match = util.RE_ENVRA.match(filters["envra"])
+ if match is not None:
+ for attr in ("name", "epoch", "version", "release", "arch"):
+ match_attr = attr
+ filters[attr] = match.group(match_attr)
+ filters.pop('nevra', None)
+ filters.pop('envra', None)
+ elif "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", "repoid", "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
+
+class RepoFilterSetter(object):
+ """
+ A context manager, that will set a repository filter lasting
+ as long as the object itself.
+ """
+ def __init__(self, yum_base, include_repos=None, exclude_repos=None):
+ if not isinstance(yum_base, yum.YumBase):
+ raise TypeError("yum_base must be a YumBase instance")
+ self._yum_base = yum_base
+ self._include = include_repos
+ self._exclude = exclude_repos
+ # after __enter__ this will be dictionary containing (
+ # repoid, enabled) pairs
+ self._prev_states = None
+
+ def __enter__(self):
+ self._prev_states = { r.id: r.enabled
+ for r in self._yum_base.repos.repos.values()}
+ if isinstance(self._exclude, (list, tuple, set)):
+ exclude = ",".join(self._exclude)
+ else:
+ exclude = self._exclude
+ # set of repositories, that were affected
+ repos = set()
+ if exclude:
+ repos.update(self._yum_base.repos.disableRepo(exclude))
+ LOG.info('disabling repositories: [%s]', ", ".join(repos))
+ if isinstance(self._include, (list, tuple, set)):
+ include = ",".join(self._include)
+ else:
+ include = self._include
+ if include:
+ affected = self._yum_base.repos.enableRepo(include)
+ LOG.info('enabling repositories: [%s]', ", ".join(affected))
+ repos.update(affected)
+ for repoid, prev_enabled in self._prev_states.items():
+ if ( repoid not in repos
+ or ( bool(prev_enabled)
+ is bool(self._yum_base.repos.getRepo(repoid).enabled))):
+ # keep only manipulated repositories
+ del self._prev_states[repoid]
+ if len(self._prev_states):
+ for repoid in (r for r, v in self._prev_states.items() if v):
+ self._yum_base.pkgSack.sacks.pop(repoid, None)
+ self._yum_base.repos.populateSack()
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ # restore previous repository states
+ if len(self._prev_states):
+ LOG.info('restoring repositories: [%s]',
+ ", ".join(self._prev_states.keys()))
+ for repoid, enabled in self._prev_states.items():
+ repo = self._yum_base.repos.getRepo(repoid)
+ if enabled:
+ repo.enable()
+ else:
+ repo.disable()
+ for repoid in (r for r, v in self._prev_states.items() if not v):
+ self._yum_base.pkgSack.sacks.pop(repoid, None)
+ self._yum_base.repos.populateSack()
+
+# *****************************************************************************
+# Decorators
+# *****************************************************************************
+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.
+ """
+ logged = trace_function(method)
+ def _wrapper(self, *args, **kwargs):
+ """
+ Wrapper for the job handler method.
+ """
+ created_session = False
+ self._init_database() #pylint: disable=W0212
+ if self._session_level == 0: #pylint: disable=W0212
+ self._session_level = 1 #pylint: disable=W0212
+ created_session = True
+ self._lock_database() #pylint: disable=W0212
+ try:
+ LOG.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 = logged(self, *args, **kwargs)
+ LOG.debug("job handler %s finished", method.__name__)
+ return result
+ finally:
+ if created_session is True: #pylint: disable=W0212
+ 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.
+
+ It spawns a second thread for managing asynchronous jobs and queue
+ of incoming jobs. It's an instance of JobManager.
+ """
+
+ def __init__(self,
+ queue_in,
+ queue_out,
+ indication_manager,
+ yum_kwargs=None,
+ logging_config=None):
+ Process.__init__(self, name="YumWorker")
+ self._jobmgr = JobManager(queue_in, queue_out, indication_manager)
+ self._session_level = 0
+ self._session_ended = False
+
+ if yum_kwargs is None:
+ yum_kwargs = {}
+
+ self._yum_kwargs = yum_kwargs
+ self._yum_base = None
+
+ self._pkg_cache = None
+ # contains (repoid, time_stamp_of_config_file)
+ # plus (/repos/dir, ...) for each repo config directory
+ self._repodir_mtimes = {}
+ 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:
+ LOG.info("creating YumBase with kwargs=(%s)",
+ ", ".join(( "%s=%s"%(k, str(v))
+ for k, v in self._yum_kwargs.items())))
+ self._yum_base = yum.YumBase(**self._yum_kwargs)
+
+ @trace_function
+ def _free_database(self):
+ """
+ Release the yum base object to safe memory.
+ """
+ LOG.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:
+ LOG.info("trying to lock database - session level %d",
+ self._session_level)
+ self._yum_base.doLock()
+ LOG.info("successfully locked up")
+ break
+ except yum.Errors.LockError as exc:
+ LOG.warn("failed to lock")
+ if exc.errno in (errno.EPERM, errno.EACCES, errno.ENOSPC):
+ LOG.error("can't create lock file")
+ raise errors.DatabaseLockError("Can't create lock file.")
+ LOG.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:
+ LOG.info("unlocking database")
+ self._yum_base.closeRpmDB()
+ self._yum_base.doUnlock()
+
+ @trace_function
+ def _get_job(self):
+ """
+ Get job from JobManager thread.
+ If no job comes for long time, free database to save memory.
+ """
+ while True:
+ if self._session_ended and self._session_level == 0:
+ try:
+ return self._jobmgr.get_job(timeout=FREE_DATABASE_TIMEOUT)
+ except TQueue.Empty:
+ self._free_database()
+ self._session_ended = False
+ else:
+ return self._jobmgr.get_job()
+
+ @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:
+ LOG.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.objid] = 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:
+ LOG.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")
+ LOG.debug("looking up yum package %s with id=%d",
+ pkg, pkg.objid)
+ try:
+ result = self._pkg_cache[pkg.objid]
+ LOG.debug("lookup successful")
+ except KeyError:
+ LOG.warn("lookup of package %s with id=%d failed, trying"
+ " to query database", pkg, pkg.objid)
+ 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:
+ LOG.warn("package %s not found", pkg)
+ raise errors.PackageNotFound(
+ "package %s could not be found" % pkg)
+ result = result[0]
+ return result
+
+ @trace_function
+ def _clear_repository_cache(self):
+ """
+ Clears the repository cache and their configuration directory
+ last modification times.
+ """
+ if self._yum_base is not None:
+ for repoid in self._yum_base.repos.repos.keys():
+ self._yum_base.repos.delete(repoid)
+ del self._yum_base.repos
+ del self._yum_base.pkgSack
+ self._repodir_mtimes.clear()
+
+ @trace_function
+ def _check_repository_configs(self):
+ """
+ Checks whether repository information is up to date with configuration
+ files by comparing timestamps. If not, repository cache will be
+ released.
+ """
+ dirty = False
+ if self._repodir_mtimes:
+ for repodir in self._yum_base.conf.reposdir:
+ if ( os.path.exists(repodir)
+ and ( not repodir in self._repodir_mtimes
+ or ( os.stat(repodir).st_mtime
+ > self._repodir_mtimes[repodir]))):
+ LOG.info("repository config dir %s changed", repodir)
+ dirty = True
+ break
+ if not dirty:
+ for repo in self._yum_base.repos.repos.values():
+ filename = repo.repofile
+ if ( not os.path.exists(filename)
+ or ( int(os.stat(filename).st_mtime)
+ > repo.repo_config_age)):
+ LOG.info('config file of repository "%s" changed',
+ repo.id)
+ dirty = True
+ break
+ if dirty is True:
+ LOG.info("repository cache is dirty, cleaning up ...")
+ self._clear_repository_cache()
+ self._yum_base.getReposFromConfig()
+ if dirty is True or not self._repodir_mtimes:
+ self._update_repodir_mtimes()
+
+ @trace_function
+ def _update_repodir_mtimes(self):
+ """
+ Updates the last modification times of repo configuration directories.
+ """
+ assert self._yum_base is not None
+ for repodir in self._yum_base.conf.reposdir:
+ if os.path.exists(repodir):
+ self._repodir_mtimes[repodir] = os.stat(repodir).st_mtime
+
+ @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,
+ jobs.YumCheckPackageFile : self._handle_check_package_file,
+ jobs.YumInstallPackageFromURI : \
+ self._handle_install_package_from_uri,
+ jobs.YumGetRepositoryList : \
+ self._handle_get_repository_list,
+ jobs.YumFilterRepositories : self._handle_filter_repositories,
+ jobs.YumSetRepositoryEnabled : \
+ self._handle_set_repository_enabled
+ }[job.__class__]
+ LOG.info("processing job %s(id=%d)",
+ job.__class__.__name__, job.jobid)
+ except KeyError:
+ LOG.error("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
+ """
+ LOG.info("building transaction %s", name)
+ (code, msgs) = self._yum_base.buildTransaction()
+ if code == 1:
+ LOG.error("building transaction %s failed: %s",
+ name, "\n".join(msgs))
+ raise errors.TransactionBuildFailed(
+ "Failed to build \"%s\" transaction: %s" % (
+ name, "\n".join(msgs)))
+ LOG.info("processing transaction %s", name)
+ self._yum_base.processTransaction()
+ self._yum_base.closeRpmDB()
+
+ @trace_function
+ def _main_loop(self):
+ """
+ This is a main loop called from run(). Jobs are handled here.
+ 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.
+ """
+ while True:
+ job = self._get_job()
+ if job is not None: # not a terminate command
+ result = jobs.YumJob.RESULT_SUCCESS
+ try:
+ data = self._do_work(job)
+ except Exception: #pylint: disable=W0703
+ result = jobs.YumJob.RESULT_ERROR
+ # (type, value, traceback)
+ data = sys.exc_info()
+ # traceback is not pickable - replace it with formatted
+ # text
+ data = (data[0], data[1], traceback.format_tb(data[2]))
+ LOG.exception("job %s failed", job)
+ self._jobmgr.finish_job(job, result, data)
+ if job is None:
+ LOG.info("waiting for %s to finish", self._jobmgr.name)
+ self._jobmgr.join()
+ break
+
+ @trace_function
+ def _handle_begin_session(self):
+ """
+ Handler for session begin job.
+ """
+ self._session_level += 1
+ LOG.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.
+ """
+ LOG.info("ending session level %d", self._session_level)
+ self._session_level = max(self._session_level - 1, 0)
+ if self._session_level == 0:
+ self._unlock_database()
+ self._session_ended = True
+
+ @_needs_database
+ def _handle_get_package_list(self, kind, allow_duplicates, sort,
+ include_repos=None, exclude_repos=None, transform=True):
+ """
+ Handler for listing packages job.
+ @param transform says, whether to return just a package abstractions
+ or original ones
+ @return [pkg1, pkg2, ...]
+ """
+ if kind == 'avail_notinst':
+ what = 'available'
+ elif kind == 'available':
+ what = 'all'
+ elif kind == 'avail_reinst':
+ what = 'all'
+ else:
+ what = kind
+ with RepoFilterSetter(self._yum_base, include_repos, exclude_repos):
+ LOG.debug("calling YumBase.doPackageLists(%s, showdups=%s)",
+ what, allow_duplicates)
+ pkglist = self._yum_base.doPackageLists(what,
+ showdups=allow_duplicates)
+ if kind == 'all':
+ result = pkglist.available + pkglist.installed
+ elif kind == 'available':
+ result = pkglist.available + pkglist.reinstall_available
+ elif kind == 'avail_reinst':
+ result = pkglist.reinstall_available
+ else: # get installed or available
+ result = getattr(pkglist, what)
+ if sort is True:
+ result.sort()
+ LOG.debug("returning %s packages", len(result))
+ return self._cache_packages(result, transform=transform)
+
+ @_needs_database
+ def _handle_filter_packages(self, kind, allow_duplicates, sort,
+ include_repos=None, exclude_repos=None,
+ transform=True, **filters):
+ """
+ Handler for filtering packages job.
+ @return [pkg1, pkg2, ...]
+ """
+ pkglist = self._handle_get_package_list(kind, allow_duplicates, False,
+ include_repos=include_repos, exclude_repos=exclude_repos,
+ transform=False)
+ matches = _get_package_filter_function(filters)
+ result = [p for p in pkglist if matches(p)]
+ if sort is True:
+ result.sort()
+ LOG.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, force=False):
+ """
+ Handler for package installation job.
+ @return installed package instance
+ """
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages(
+ 'available' if force else 'avail_notinst',
+ allow_duplicates=False, sort=True,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ elif len(pkgs) > 1:
+ LOG.warn('multiple packages matches nevra "%s": [%s]',
+ pkg, ", ".join(p.nevra for p in pkgs))
+ pkg_desired = pkgs[-1]
+ else:
+ pkg_desired = self._lookup_package(pkg)
+ if isinstance(pkg_desired, yum.rpmsack.RPMInstalledPackage):
+ if force is False:
+ raise errors.PackageAlreadyInstalled(pkg)
+ action = "reinstall"
+ else:
+ action = "install"
+ getattr(self._yum_base, action)(pkg_desired)
+ self._run_transaction(action)
+ 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.
+ """
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg = pkgs[-1]
+ else:
+ 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
+ """
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('available',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg_desired = pkgs[-1]
+ else:
+ 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,
+ _force=False):
+ """
+ Handler for package update job.
+ @return updated package instance
+ """
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ pkg = pkgs[-1]
+ else:
+ pkg = self._lookup_package(pkg)
+ if not isinstance(pkg, yum.rpmsack.RPMInstalledPackage):
+ raise errors.PackageNotInstalled(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, file_name=None):
+ """
+ @return PackageCheck instance for requested package
+ """
+ if isinstance(pkg, basestring):
+ pkgs = self._handle_filter_packages('installed',
+ allow_duplicates=False, sort=False,
+ transform=False, nevra=pkg)
+ if len(pkgs) < 1:
+ raise errors.PackageNotFound('No available package matches'
+ ' nevra "%s".' % pkg)
+ rpm = pkgs[-1]
+ pkg = self._transform_packages((rpm, ), cache_packages=False)[0]
+ else:
+ rpm = self._lookup_package(pkg)
+ if not isinstance(rpm, yum.rpmsack.RPMInstalledPackage):
+ raise errors.PackageNotInstalled(rpm)
+ vpkg = yum.packages._RPMVerifyPackage(rpm, rpm.hdr.fiFromHeader(),
+ packagecheck.pkg_checksum_type(rpm), [], True)
+ return (pkg, packagecheck.make_package_check_from_db(vpkg,
+ file_name=file_name))
+
+ @_needs_database
+ def _handle_check_package_file(self, pkg, file_name):
+ """
+ @return PackageCheck instance for requested package containing
+ just one PackageFile instance for given ``file_name``.
+ """
+ return self._handle_check_package(pkg, file_name)
+
+ @_needs_database
+ def _handle_install_package_from_uri(self, uri,
+ update_only=False, force=False):
+ """
+ @return installed PackageInfo instance
+ """
+ try:
+ pkg = yum.packages.YumUrlPackage(self,
+ ts=self._yum_base.rpmdb.readOnlyTS(), url=uri,
+ ua=default_grabber)
+ except yum.Errors.MiscError as exc:
+ raise errors.PackageOpenError(uri, str(exc))
+ installed = self._handle_filter_packages("installed", False, False,
+ nevra=util.pkg2nevra(pkg, with_epoch="ALWAYS"))
+ if installed and force is False:
+ raise errors.PackageAlreadyInstalled(pkg)
+ kwargs = { 'po' : pkg }
+ if installed:
+ action = 'reinstallLocal'
+ else:
+ action = 'installLocal'
+ kwargs = { 'updateonly' : update_only }
+ getattr(self._yum_base, action)(uri, **kwargs)
+ self._run_transaction('installLocal')
+ installed = self._handle_filter_packages("installed", False, False,
+ nevra=util.pkg2nevra(pkg, with_epoch="ALWAYS"))
+ if len(installed) < 1:
+ raise errors.TransactionExecutionFailed(
+ "Failed to install desired package %s." % pkg)
+ return installed[0]
+
+ @_needs_database
+ def _handle_get_repository_list(self, kind, transform=True):
+ """
+ @return list of yumdb.Repository instances
+ """
+ self._check_repository_configs()
+ if kind == 'enabled':
+ repos = sorted(self._yum_base.repos.listEnabled())
+ else:
+ repos = self._yum_base.repos.repos.values()
+ if kind == 'disabled':
+ repos = [repo for repo in repos if not repo.enabled]
+ repos.sort()
+ if transform:
+ repos = [repository.make_repository_from_db(r) for r in repos]
+ LOG.debug("returning %d repositories from %s",
+ len(repos), kind)
+ return repos
+
+ @_needs_database
+ def _handle_filter_repositories(self, kind, **filters):
+ """
+ @return list of yumdb.Repository instances -- filtered
+ """
+ filters = dict((k, v) for k, v in filters.items() if v is not None)
+ if 'repoid' in filters:
+ self._check_repository_configs()
+ try:
+ repo = repository.make_repository_from_db(
+ self._yum_base.repos.getRepo(filters["repoid"]))
+ if ( (kind == "enabled" and not repo.enabled)
+ or (kind == "disabled" and repo.enabled)):
+ LOG.warn(
+ 'no such repository with id="%s"matching filters',
+ filters['repoid'])
+ return []
+ LOG.debug(
+ "exactly one repository matching filters found")
+ return [repo]
+ except (KeyError, yum.Errors.RepoError):
+ LOG.warn('repository with id="%s" could not be found',
+ filters['repoid'])
+ raise errors.RepositoryNotFound(filters['repoid'])
+
+ repos = self._handle_get_repository_list(kind, transform=False)
+ result = []
+ for repo in repos:
+ # do the filtering and safe transformed repo into result
+ for prop, value in filters.items():
+ if repository.get_prop_from_yum_repo(repo, prop) != value:
+ # did not pass the filter
+ break
+ else: # all properties passed
+ result.append(repository.make_repository_from_db(repo))
+ LOG.debug("found %d repositories matching", len(result))
+ return result
+
+ @_needs_database
+ def _handle_set_repository_enabled(self, repo, enable):
+ """
+ @return previous enabled state
+ """
+ self._check_repository_configs()
+ if isinstance(repo, repository.Repository):
+ repoid = repo.repoid
+ else:
+ repoid = repo
+ try:
+ repo = self._yum_base.repos.getRepo(repoid)
+ except (KeyError, yum.Errors.RepoError):
+ raise errors.RepositoryNotFound(repoid)
+ res = repo.enabled
+ try:
+ if enable ^ res:
+ if enable is True:
+ LOG.info("enabling repository %s" % repo)
+ repo.enable()
+ else:
+ LOG.info("disabling repository %s" % repo)
+ repo.disable()
+ try:
+ yum.config.writeRawRepoFile(repo, only=["enabled"])
+ except Exception as exc:
+ raise errors.RepositoryChangeError(
+ 'failed to modify repository "%s": %s - %s' % (
+ repo, exc.__class__.__name__, str(exc)))
+ else:
+ LOG.info("no change for repo %s", repo)
+ except yum.Errors.RepoError as exc:
+ raise errors.RepositoryChangeError(
+ 'failed to modify repository "%s": %s' % (repo, str(exc)))
+ return res
+
+ # *************************************************************************
+ # Public properties
+ # *************************************************************************
+ @property
+ def uplink(self):
+ """
+ @return input queue for jobs
+ """
+ return self._jobmgr.queue_in
+
+ @property
+ def downlink(self):
+ """
+ @return output queue for job results
+ """
+ return self._jobmgr.queue_out
+
+ # *************************************************************************
+ # Public methods
+ # *************************************************************************
+ def run(self):
+ """
+ Thread's entry point. After initial setup it calls _main_loop().
+ """
+ if self._logging_config is not None:
+ setup_logging(self._logging_config)
+ global LOG
+ LOG = logging.getLogger(__name__)
+ LOG.info("running as pid=%d", self.pid)
+ self._jobmgr.start()
+ LOG.info("started %s as thread %s",
+ self._jobmgr.name, self._jobmgr.ident)
+ self._pkg_cache = weakref.WeakValueDictionary()
+
+ # This allows the code, that can be run both from broker and
+ # YumWorker, to check, whether it's called by this process.
+ from lmi.software.yumdb import YumDB
+ YumDB.RUNNING_UNDER_CIMOM_PROCESS = False
+
+ self._main_loop()
+ LOG.info("terminating")
+
diff --git a/src/software/lmi/software/yumdb/repository.py b/src/software/lmi/software/yumdb/repository.py
new file mode 100644
index 0000000..758ae21
--- /dev/null
+++ b/src/software/lmi/software/yumdb/repository.py
@@ -0,0 +1,202 @@
+# -*- encoding: utf-8 -*-
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+"""
+Module holding an abstraction for YUM repository.
+"""
+
+from datetime import datetime
+import logging
+import yum
+import yum.Errors
+
+# maps names of Repository properties to their corresponding property
+# names in YumRepository object
+PROPERTY_NAME_MAP = {
+ "repoid" : "id",
+ "base_urls" : "baseurl",
+ "config_file" : "repofile",
+ "cost" : "cost",
+ "enabled" : "enabled",
+ "gpg_check" : "gpgcheck",
+ "last_edit" : "repo_config_age",
+ "mirror_list" : "mirrorlist",
+ "mirror_urls" : "mirrorurls",
+ "name" : "name",
+ "pkg_dir" : "pkgdir",
+ "ready" : "ready",
+ "repo_gpg_check" : "repo_gpgcheck",
+ "timeout" : "timeout"
+}
+
+def get_prop_from_yum_repo(repo, prop_name):
+ """
+ @param prop_name is one Repository properties
+ @return value of property from object of YumRepository
+ """
+ if not isinstance(repo, yum.yumRepo.YumRepository):
+ raise TypeError("repo must be in instance of yum.yumRepo.YumRepository")
+ if prop_name in PROPERTY_NAME_MAP:
+ val = getattr(repo, PROPERTY_NAME_MAP[prop_name])
+ if prop_name == "last_edit":
+ val = datetime.fromtimestamp(val)
+ elif prop_name == "mirror_urls" and not repo.mirrorlist:
+ val = None
+ elif prop_name == "ready":
+ val = val()
+ elif prop_name in {"arch", "basearch", "releasever"}:
+ val = repo.yumvar[prop_name]
+ elif prop_name in {"revision", "last_update"}:
+ if repo.enabled and repo.repoXML:
+ md = repo.repoXML
+ if prop_name == "last_update":
+ val = datetime.fromtimestamp(md.timestamp)
+ elif prop_name == "revision":
+ val = long(md.revision)
+ else:
+ val = getattr(repo.repoXML, prop_name)
+ else:
+ val = None
+ elif prop_name == "pkg_count":
+ # this needs populated sack: ydb.repos.populateSack(repo.id)
+ val = len(repo.sack)
+ else:
+ raise ValueError('Unknown repository property: "%s"' % prop_name)
+ return val
+
+class Repository(object):
+ """
+ Container for repository metadata. It represents yum repository.
+ It's supposed to be passed from YumWorker to YumDB client and
+ vice-versa.
+ """
+ __slots__ = (
+ "objid", # [int] id of python object on server process
+ "repoid", # [string] repository id name
+ # (name of config file)
+
+ "arch", # [string] architecture of packages
+ "basearch", # [string] base system architecture
+ "base_urls", # [list] base urls as strings
+ #"cache",
+ #"cache_dir",
+ "name", # [string] repository descriptive name
+ "config_file", # [string] file path to corresponding
+ # config file
+ "cost", # [int] cost of repository
+ "enabled", # [boolean] whether repo is enabled
+ "gpg_check", # [boolean] whether to check gpg signature
+ #"metadata_expire", # how long are metadata considered valid
+ "last_edit", # datetime of last config modification
+ "last_update", # datetime of last change of repository
+ # on server
+ "mirror_list", # url of mirrorlist, or None
+ "mirror_urls", # list of urls obtained from mirrorlist or None
+ #"persist_dir", #
+ #"pkg_count", # number of packages in directory
+ "pkg_dir", # directory with packages
+ #"proxy", # boolean saying whether this is a proxy
+ "ready", # boolean saying, whether it's ready for use
+ "releasever", # version of targeted distribution
+ "repo_gpg_check", # [boolean] whether to check gpg
+ # signarues of data
+ "revision",
+ "timeout", # timeout for requests
+ )
+
+ def __init__(self, objid, repoid, arch, basearch, base_urls,
+ config_file, cost, enabled, gpg_check, last_edit, last_update,
+ name, pkg_dir, ready, releasever, repo_gpg_check, revision,
+ timeout, mirror_list=None, mirror_urls=None):
+ for arg in ('last_edit', 'last_update'):
+ if ( locals()[arg] is not None
+ and not isinstance(locals()[arg], datetime)):
+ raise TypeError("%s must be an instance of datetime" % arg)
+ if not isinstance(timeout, float):
+ raise TypeError("timeout must be a float")
+ for arg in ('cost', 'revision'):
+ if ( locals()[arg] is not None
+ and not isinstance(locals()[arg], (int, long))):
+ raise TypeError("%s must be an integer" % arg)
+ self.objid = objid
+ self.repoid = repoid
+ self.arch = arch
+ self.basearch = basearch
+ self.base_urls = list(base_urls)
+ self.config_file = config_file
+ self.cost = cost
+ self.enabled = bool(enabled)
+ self.gpg_check = bool(gpg_check)
+ self.last_edit = last_edit
+ self.last_update = last_update
+ self.mirror_list = "" if not mirror_list else mirror_list
+ self.mirror_urls = [] if not mirror_urls else list(mirror_urls)
+ self.name = name
+ #self.pkg_count = pkg_count
+ self.pkg_dir = pkg_dir
+ self.ready = bool(ready)
+ self.releasever = releasever
+ self.repo_gpg_check = bool(repo_gpg_check)
+ self.revision = revision
+ self.timeout = timeout
+
+ def __str__(self):
+ return self.repoid
+
+ 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_repository_from_db(repo):
+ """
+ Create instance of Repository from instance of yum.yumRepo.YumRepository
+ @return instance of Repository
+ """
+ if not isinstance(repo, yum.yumRepo.YumRepository):
+ raise TypeError("repo must be in instance of yum.yumRepo.YumRepository")
+ metadata = {}
+ for prop_name in Repository.__slots__[1:]:
+ try:
+ metadata[prop_name] = get_prop_from_yum_repo(repo, prop_name)
+ except yum.Errors.RepoError as exc:
+ # some properties can cause error (like requesting ready)
+ logging.getLogger(__name__).warn(
+ 'failed to get property "%s" of repo "%s": %s' % (
+ prop_name, repo.name, exc))
+ if prop_name == "ready":
+ metadata[prop_name] = False
+ continue
+
+ res = Repository(id(repo), **metadata)
+ return res
+
diff --git a/src/software/lmi/software/yumdb/util.py b/src/software/lmi/software/yumdb/util.py
new file mode 100644
index 0000000..18fcf6b
--- /dev/null
+++ b/src/software/lmi/software/yumdb/util.py
@@ -0,0 +1,163 @@
+# Software Management Providers
+#
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Authors: Michal Minar <miminar@redhat.com>
+#
+
+"""
+Common utilities meant to be used only be ``yumdb`` subpackage.
+"""
+
+from itertools import chain
+import inspect
+import logging
+import os
+
+from lmi.common import cmpi_logging
+
+class DispatchingFormatter:
+ """
+ Formatter class for logging module. It allows to predefine different
+ format string for paricular module names.
+ """
+ def __init__(self, formatters, default):
+ """
+ *format* in parameters description can be either ``string`` or
+ another formatter object.
+
+ :param formatters (``dict``) Mapping of module names to *format*.
+ :param default Default *format*.
+ """
+ for k, formatter in formatters.items():
+ if isinstance(formatter, basestring):
+ formatters[k] = logging.Formatter(formatter)
+ self._formatters = formatters
+ if isinstance(default, basestring):
+ default = logging.Formatter(default)
+ self._default_formatter = default
+
+ def format(self, record):
+ """
+ Interface for logging module.
+ """
+ formatter = self._formatters.get(record.name, self._default_formatter)
+ return formatter.format(record)
+
+# *****************************************************************************
+# 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)
+
+ logger = logging.getLogger(__name__+'.trace_function')
+ module = func.__module__.split('.')[-1]
+ lineno = inspect.currentframe().f_back.f_lineno
+
+ def _wrapper(self, *args, **kwargs):
+ """
+ Wrapper for function or method, that does the logging.
+ """
+ if logger.isEnabledFor(logging.DEBUG):
+ frm = inspect.currentframe()
+ logargs = {
+ "caller_file" : os.path.basename(os.path.splitext(
+ frm.f_back.f_code.co_filename)[0]),
+ "caller_lineno" : frm.f_back.f_lineno,
+ "module" : module,
+ "func" : func.__name__,
+ "lineno" : lineno,
+ "action" : "entering",
+ "args" : ", ".join(chain(
+ (_print_value(a) for a in args),
+ ( "%s=%s"%(k, _print_value(v))
+ for k, v in kwargs.items())))
+ }
+
+ if not logargs["args"]:
+ logargs["args"] = ""
+ else:
+ logargs["args"] = " with args=(%s)" % logargs["args"]
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d%(args)s" , logargs)
+ try:
+ result = func(self, *args, **kwargs)
+ if logger.isEnabledFor(logging.DEBUG):
+ logargs["action"] = "exiting"
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d", logargs)
+ except Exception as exc:
+ if logger.isEnabledFor(logging.DEBUG):
+ logargs['action'] = 'exiting'
+ logargs['error'] = str(exc)
+ logger.debug("%(caller_file)s:%(caller_lineno)d - %(action)s"
+ " %(module)s:%(func)s:%(lineno)d with error: %(error)s",
+ logargs)
+ raise
+ return result
+
+ return _wrapper
+
+def setup_logging(config):
+ """
+ This is meant to be used by ``YumWorker`` process to setup logging
+ independent of what providers are using. Unfortunately ``YumWorker``
+ can not use the same facilities as the rest of program, because
+ logging is done through *broker*.
+ """
+ try:
+ logging.config.dictConfig(config)
+ cmpi_logging.logger = logging.getLogger('lmi.software.yumdb')
+ except Exception: #pylint: disable=W0703
+ # logging is not set up but client expects us to work
+ # all messages are dumped to /dev/null
+ logging.config.dictConfig({
+ 'version' : 1,
+ 'disable_existing_loggers' : True,
+ 'handlers': {
+ 'null' : {
+ 'class': 'logging.handlers.FileHandler',
+ 'level': 'CRITICAL',
+ 'filename': '/dev/null'
+ }
+ },
+ 'loggers' : {
+ 'root' : {
+ 'level': 'CRITICAL',
+ 'handlers' : ['null'],
+ 'propagate' : False
+ }
+ }
+ })
+
+