diff options
Diffstat (limited to 'src/software/openlmi/software/LMI_SoftwareInstalledPackage.py')
-rw-r--r-- | src/software/openlmi/software/LMI_SoftwareInstalledPackage.py | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py b/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py new file mode 100644 index 0000000..51d15b6 --- /dev/null +++ b/src/software/openlmi/software/LMI_SoftwareInstalledPackage.py @@ -0,0 +1,509 @@ +# -*- encoding: utf-8 -*- +# Software Management Providers +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Michal Minar <miminar@redhat.com> +# + +"""Python Provider for LMI_SoftwareInstalledPackage + +Instruments the CIM class LMI_SoftwareInstalledPackage + +""" + +import itertools +import pywbem +from pywbem.cim_provider2 import CIMProvider2 +from openlmi.software.LMI_SoftwarePackage import pkg2model, LMI_SoftwarePackage +from openlmi.software.LMI_SoftwareFileCheck import filecheck2model +from openlmi.software.util.common import * + +class LMI_SoftwareInstalledPackage(CIMProvider2): + """Instrument the CIM class LMI_SoftwareInstalledPackage + + The InstalledSoftwareElement association allows the identification of + the ComputerSystem on which a particular SoftwareElement is installed. + + """ + + def __init__ (self, env): + logger = env.get_logger() + logger.log_debug('Initializing provider %s from %s' \ + % (self.__class__.__name__, __file__)) + + def get_instance(self, env, model): + """Return an instance. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + model -- A template of the pywbem.CIMInstance to be returned. The + key properties are set on this instance to correspond to the + instanceName that was requested. The properties of the model + are already filtered according to the PropertyList from the + request. Only properties present in the model need to be + given values. If you prefer, you can set all of the + values, and the instance will be filtered for you. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized + or otherwise incorrect parameters) + CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM + Instance does not exist in the specified namespace) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.get_instance()' \ + % self.__class__.__name__) + + if not "Software" in model: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing Software property.") + if not "System" in model: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing System property.") + check_computer_system_op(env, model['System']) + with YumDB.getInstance(env): + pkg = SoftwarePackage.object_path2pkg(env, model['Software']) + model['Software'] = pkg2model(env, pkg, True) + return model + + def enum_instances(self, env, model, keys_only): + """Enumerate instances. + + The WBEM operations EnumerateInstances and EnumerateInstanceNames + are both mapped to this method. + This method is a python generator + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + model -- A template of the pywbem.CIMInstances to be generated. + The properties of the model are already filtered according to + the PropertyList from the request. Only properties present in + the model need to be given values. If you prefer, you can + always set all of the values, and the instance will be filtered + for you. + keys_only -- A boolean. True if only the key properties should be + set on the generated instances. + + Possible Errors: + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.enum_instances()' \ + % self.__class__.__name__) + + # Prime model.path with knowledge of the keys, so key values on + # the CIMInstanceName (model.path) will automatically be set when + # we set property values on the model. + model.path.update({'System': None, 'Software': None}) + + yum_package_path = pywbem.CIMInstanceName("LMI_SoftwarePackage", + namespace=model.path.namespace, + host=model.path.host) + model['System'] = get_computer_system_op() + with YumDB.getInstance(env) as yb: + for rpm in yb.rpmdb: + iname = pkg2model(env, rpm, True, yum_package_path) + model['Software'] = iname + if keys_only: + yield model + else: + try: + yield self.get_instance(env, model) + except pywbem.CIMError, (num, msg): + if num not in (pywbem.CIM_ERR_NOT_FOUND, + pywbem.CIM_ERR_ACCESS_DENIED): + raise + + def set_instance(self, env, instance, modify_existing): + """Return a newly created or modified instance. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + instance -- The new pywbem.CIMInstance. If modifying an existing + instance, the properties on this instance have been filtered by + the PropertyList from the request. + modify_existing -- True if ModifyInstance, False if CreateInstance + + Return the new instance. The keys must be set on the new instance. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_NOT_SUPPORTED + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized + or otherwise incorrect parameters) + CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only + valid if modify_existing is False, indicating that the operation + was CreateInstance) + CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid + if modify_existing is True, indicating that the operation + was ModifyInstance) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.set_instance()' \ + % self.__class__.__name__) + + # parse and check arguments + if modify_existing is True: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, + "MofifyInstance is not supported") + + if not "Software" in instance: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing Software property.") + if not "System" in instance: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing System property.") + check_computer_system_op(env, instance['System']) + + with YumDB.getInstance(env) as yb: + pkg = SoftwarePackage.object_path2pkg_search(env, instance['Software']) + if isinstance(pkg, yum.rpmsack.RPMInstalledPackage): + raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS, + "Package is already installed.") + + logger.log_info('installing package {}'.format(pkg.nevra)) + # install + yb.install(pkg) + yb.buildTransaction() + yb.processTransaction() + logger.log_info('package installed'.format(pkg.nevra)) + + # return instance + pkg_iname = pkg2model(env, pkg, True) + pkg_iname["SoftwareElementState"] = \ + LMI_SoftwarePackage.Values.SoftwareElementState.Executable + instance["Software"] = pkg_iname + return LMI_SoftwareInstalledPackage(env).get_instance(env, instance) + + def delete_instance(self, env, instance_name): + """Delete an instance. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + instance_name -- A pywbem.CIMInstanceName specifying the instance + to delete. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_NOT_SUPPORTED + CIM_ERR_INVALID_NAMESPACE + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized + or otherwise incorrect parameters) + CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified + namespace) + CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM + Instance does not exist in the specified namespace) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.delete_instance()' \ + % self.__class__.__name__) + + if not "Software" in instance_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing Software property.") + if not "System" in instance_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing System property.") + check_computer_system_op(env, instance_name['System']) + with YumDB.getInstance(env) as yb: + pkg = SoftwarePackage.object_path2pkg(env, instance_name["Software"]) + logger.log_info('removing package "%s"' % pkg.nevra) + yb.remove(pkg) + yb.buildTransaction() + yb.processTransaction() + logger.log_info('package "%s" removed' % pkg.nevra) + + def references(self, env, object_name, model, result_class_name, role, + result_role, keys_only): + """Instrument Associations. + + All four association-related operations (Associators, AssociatorNames, + References, ReferenceNames) are mapped to this method. + This method is a python generator + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + object_name -- A pywbem.CIMInstanceName that defines the source + CIM Object whose associated Objects are to be returned. + model -- A template pywbem.CIMInstance to serve as a model + of the objects to be returned. Only properties present on this + model need to be set. + result_class_name -- If not empty, this string acts as a filter on + the returned set of Instances by mandating that each returned + Instances MUST represent an association between object_name + and an Instance of a Class whose name matches this parameter + or a subclass. + role -- If not empty, MUST be a valid Property name. It acts as a + filter on the returned set of Instances by mandating that each + returned Instance MUST refer to object_name via a Property + whose name matches the value of this parameter. + result_role -- If not empty, MUST be a valid Property name. It acts + as a filter on the returned set of Instances by mandating that + each returned Instance MUST represent associations of + object_name to other Instances, where the other Instances play + the specified result_role in the association (i.e. the + name of the Property in the Association Class that refers to + the Object related to object_name MUST match the value of this + parameter). + keys_only -- A boolean. True if only the key properties should be + set on the generated instances. + + The following diagram may be helpful in understanding the role, + result_role, and result_class_name parameters. + +------------------------+ +-------------------+ + | object_name.classname | | result_class_name | + | ~~~~~~~~~~~~~~~~~~~~~ | | ~~~~~~~~~~~~~~~~~ | + +------------------------+ +-------------------+ + | +-----------------------------------+ | + | | [Association] model.classname | | + | object_name | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | + +--------------+ object_name.classname REF role | | + (CIMInstanceName) | result_class_name REF result_role +------+ + | |(CIMInstanceName) + +-----------------------------------+ + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_NOT_SUPPORTED + CIM_ERR_INVALID_NAMESPACE + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized + or otherwise incorrect parameters) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.references()' \ + % self.__class__.__name__) + ch = env.get_cimom_handle() + + # If you want to get references for free, implemented in terms + # of enum_instances, just leave the code below unaltered. + if ch.is_subclass(object_name.namespace, + sub=object_name.classname, + super='CIM_ComputerSystem') or \ + ch.is_subclass(object_name.namespace, + sub=object_name.classname, + super='LMI_SoftwarePackage'): + return self.simple_refs(env, object_name, model, + result_class_name, role, result_role, keys_only) + + def cim_method_checkintegrity(self, env, object_name): + """Implements LMI_SoftwarePackage.CheckIntegrity() + + Verify existence and attributes of files installed from RPM + package. If all files installed exist and their attributes + matches, method returns "Pass". "Not passed" is returned when + arbitrary file differs in its attributes. And "Error" if + verification could not be done. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName + specifying the object on which the method CheckIntegrity() + should be invoked. + + Returns a two-tuple containing the return value ( + type pywbem.Uint32 self.Values.CheckIntegrity) + and a list of CIMParameter objects representing the output parameters + + Output parameters: + Failed -- (type REF (pywbem.CIMInstanceName( + classname='LMI_SoftwareFileCheck', ...)) + Array of references to LMI_SoftwareFileCheck, that did not pass + verification. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, + unrecognized or otherwise incorrect parameters) + CIM_ERR_NOT_FOUND (the target CIM Class or instance does not + exist in the specified namespace) + CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor + the invocation request) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.cim_method_checkintegrity()' \ + % self.__class__.__name__) + + failed = [] + if not "Software" in object_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing Software property.") + if not "System" in object_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing System property.") + with YumDB.getInstance(env) as yb: + pkg = SoftwarePackage.object_path2pkg(env, object_name['Software']) + csum = SoftwareFileCheck.pkg_checksum_type(pkg) + vpkg = yum.packages._RPMVerifyPackage( + pkg, pkg.hdr.fiFromHeader(), csum, [], True) + for vpf in vpkg: + fc = SoftwareFileCheck.test_file(env, csum, vpf) + if SoftwareFileCheck.filecheck_passed(fc): continue + failed.append(filecheck2model( + vpkg, vpf.filename, env, keys_only=True, fc=fc)) + out_params = [ pywbem.CIMParameter('Failed', type='reference', + value=failed) ] + return ( getattr(self.Values.CheckIntegrity, + 'Pass' if len(failed) == 0 else 'Not_passed') + , out_params ) + + def cim_method_update(self, env, object_name, + param_epoch=None, + param_release=None, + param_version=None): + """Implements LMI_SoftwareInstalledPackage.Update() + + Updates the package to latest version. When any of "version" or + "release" argument is given, install only matching available + package. Otherwise try to update to newest package available. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName + specifying the object on which the method Update() + should be invoked. + param_release -- The input parameter release (type unicode) + Specify particular release of package to update to. Update to + newest, when empty + param_version -- The input parameter version (type unicode) + Specify particular version of package to update to. Update to + newest, when empty + + Returns a two-tuple containing the return value ( + type pywbem.Uint16 self.Values.Update) + and a list of CIMParameter objects representing the output parameters + + Output parameters: + Installed -- (type REF (pywbem.CIMInstanceName( + classname='LMI_SoftwareInstalledPackage', ...)) + The reference to newly installed package, if installation was + successful. Otherwise reference to current package. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, + unrecognized or otherwise incorrect parameters) + CIM_ERR_NOT_FOUND (the target CIM Class or instance does not + exist in the specified namespace) + CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor + the invocation request) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.cim_method_update()' \ + % self.__class__.__name__) + + if not "Software" in object_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing Software property.") + if not "System" in object_name: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Missing System property.") + check_computer_system_op(env, object_name['System']) + + with YumDB.getInstance(env) as yb: + orig = SoftwarePackage.object_path2pkg(env, object_name['Software']) + + evr_str = [] + for name, param in ( + ('epoch', param_epoch), + ('version', param_version), + ('release', param_release)): + evr_str.append("%s(%s)"%(name,param)) + if len(evr_str): + evr_str = "specific "+'-'.join(evr_str) + else: + evr_str = "the newest version-release" + + logger.log_info('trying to update to %s of package \"%s\"' % + (evr_str, object_name["Software"]["Name"])) + + pl = yb.doPackageLists('all', showdups=True) + exact,_,_ = yum.packages.parsePackages( + itertools.chain(pl.available, pl.installed), + [orig.name]) + # NOTE: available ∩ installed = ∅ + exact = sorted(exact, key=lambda a:a.evra) + + try: + pkg = [ p for p in exact + if (not param_epoch or param_epoch == p.epoch) + and (not param_version or param_version == p.ver) + and (not param_release or param_release == p.rel) ] [-1] + except IndexError: + logger.log_error( + 'could not find any matching available package') + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND) + out_params = [pywbem.CIMParameter('Installed', type='reference', + value=pkg2model(env, pkg, True))] + if orig.evra == pkg.evra: + logger.log_info('already up to date') + return (self.Values.Update.Already_newest, out_params) + + yb.update(update_to=True, + name=pkg.name, + epoch=pkg.epoch, + version=pkg.version, + release=pkg.release) + yb.buildTransaction() + yb.processTransaction() + logger.log_info('package {} updated to: {}'.format( + orig.name, pkg.evra)) + + out_params[0].value["SoftwareElementState"] = \ + LMI_SoftwarePackage.Values.SoftwareElementState.Executable + + return (self.Values.Update.Successful_installation, out_params) + + class Values(object): + class Update(object): + Already_newest = pywbem.Uint16(0) + Successful_installation = pywbem.Uint16(1) + Failed = pywbem.Uint16(2) + + class CheckIntegrity(object): + Pass = pywbem.Uint32(0) + Not_passed = pywbem.Uint32(1) + Error = pywbem.Uint32(2) + +## end of class LMI_SoftwareInstalledPackage + +## get_providers() for associating CIM Class Name to python provider class name + +def get_providers(env): + lmi_softwareinstalledpackage_prov = LMI_SoftwareInstalledPackage(env) + return {'LMI_SoftwareInstalledPackage': lmi_softwareinstalledpackage_prov} |