From 3c719d73efc1902edfc79d0d6de6b14670addd26 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Wed, 3 Oct 2012 11:32:02 +0200 Subject: update/delete methods done on YumInstalledPackage enumerating YumPackage now yields all available packages - removed update, delete and check_integrity methods - added Install method - SoftwareElementState now equals to: 1 ("Installable") - for not installed and available package 2 ("Executable") - for installed package enumerating YumInstalledPackage now yields all installed packages - added update, delete and check_integrity methods --- src/yum/providers/LMI_YumInstalledPackage.py | 358 ++++++++++++++++++++------- src/yum/providers/LMI_YumPackage.py | 294 +++++++--------------- src/yum/providers/util/common.py | 165 +++++++----- src/yum/test/test_common.sh | 15 +- src/yum/test/yum_package.sh | 84 +++++-- 5 files changed, 526 insertions(+), 390 deletions(-) (limited to 'src') diff --git a/src/yum/providers/LMI_YumInstalledPackage.py b/src/yum/providers/LMI_YumInstalledPackage.py index cb432d2..37a3afe 100644 --- a/src/yum/providers/LMI_YumInstalledPackage.py +++ b/src/yum/providers/LMI_YumInstalledPackage.py @@ -1,3 +1,4 @@ +# -*- encoding: utf-8 -*- # Software Management Providers # # Copyright (C) 2012 Red Hat, Inc. All rights reserved. @@ -21,17 +22,27 @@ Instruments the CIM class LMI_YumInstalledPackage """ +import itertools import pywbem import socket from pywbem.cim_provider2 import CIMProvider2 -from LMI_YumPackage import LMI_YumPackage +from LMI_YumPackage import LMI_YumPackage, pkg2model +from util.common import * + +def get_computer_system_op(): + return pywbem.CIMInstanceName( + classname='Linux_ComputerSystem', + keybindings={ + "CreationClassName": "Linux_ComputerSystem" + , "Name" : socket.gethostname() }, + namespace="root/cimv2") class LMI_YumInstalledPackage(CIMProvider2): - """Instrument the CIM class LMI_YumInstalledPackage + """Instrument the CIM class LMI_YumInstalledPackage The InstalledSoftwareElement association allows the identification of the ComputerSystem on which a particular SoftwareElement is installed. - + """ def __init__ (self, env): @@ -44,50 +55,51 @@ class LMI_YumInstalledPackage(CIMProvider2): 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 + 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 + 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. + 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 + 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 + CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ - + logger = env.get_logger() logger.log_debug('Entering %s.get_instance()' \ % self.__class__.__name__) - - - # TODO fetch system resource matching the following keys: - # model['System'] - # model['Software'] + if model['System'] != get_computer_system_op(): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Unknown \"System\"") + with YumDB.getInstance(env): + pkg = object_path2pkg(env, model['Software']) + model['Software'] = pkg2model(pkg, env, 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. + 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. + 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. @@ -99,59 +111,52 @@ class LMI_YumInstalledPackage(CIMProvider2): 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. + # we set property values on the model. model.path.update({'System': None, 'Software': None}) - + yum_package = LMI_YumPackage(env) yum_package_path = pywbem.CIMInstanceName("LMI_YumPackage", namespace=model.path.namespace, host=model.path.host) - yum_package_model = pywbem.CIMInstance(classname="LMI_YumPackage", - path=yum_package_path) - for iname in yum_package.enum_instances( - env, yum_package_model, True): - model['System'] = pywbem.CIMInstanceName( - classname='Linux_ComputerSystem', - keybindings={ - "CreationClassName": "Linux_ComputerSystem" - , "Name" : socket.gethostname() }, - host=model.path.host, - namespace=model.path.namespace) - model['Software'] = iname.path - if keys_only: - yield model - else: - try: - yield self.get_instance(env, model) - except pywbem.CIMError, (num, msg): - if num not in (pywbem.CIM_ERR_NOT_FOUND, - pywbem.CIM_ERR_ACCESS_DENIED): - raise + model['System'] = get_computer_system_op() + with YumDB.getInstance(env) as yb: + for rpm in yb.rpmdb: + iname = pkg2model(rpm, env, 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 + 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. + 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 + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) - CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only + 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 + 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) @@ -162,7 +167,8 @@ class LMI_YumInstalledPackage(CIMProvider2): logger.log_debug('Entering %s.set_instance()' \ % self.__class__.__name__) # TODO create or modify the instance - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement + # Remove to implement + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) return instance def delete_instance(self, env, instance_name): @@ -170,66 +176,75 @@ class LMI_YumInstalledPackage(CIMProvider2): Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) - instance_name -- A pywbem.CIMInstanceName specifying the instance + 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 + 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 + 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 + CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) - """ + """ logger = env.get_logger() logger.log_debug('Entering %s.delete_instance()' \ % self.__class__.__name__) - # TODO delete the resource - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement - + if instance_name['System'] != get_computer_system_op(): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Unknown \"System\"") + + with YumDB.getInstance(env) as yb: + pkg = 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. + 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 + 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 + 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 + 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 + 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, + The following diagram may be helpful in understanding the role, result_role, and result_class_name parameters. +------------------------+ +-------------------+ | object_name.classname | | result_class_name | @@ -247,7 +262,7 @@ class LMI_YumInstalledPackage(CIMProvider2): CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE - CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_FAILED (some other unspecified error occurred) @@ -258,9 +273,9 @@ class LMI_YumInstalledPackage(CIMProvider2): % self.__class__.__name__) ch = env.get_cimom_handle() - # If you want to get references for free, implemented in terms + # 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, + if ch.is_subclass(object_name.namespace, sub=object_name.classname, super='LMI_ComputerSystem') or \ ch.is_subclass(object_name.namespace, @@ -269,10 +284,173 @@ class LMI_YumInstalledPackage(CIMProvider2): return self.simple_refs(env, object_name, model, result_class_name, role, result_role, keys_only) -## end of class LMI_YumInstalledPackageProvider - + def cim_method_checkintegrity(self, env, object_name): + """Implements LMI_YumPackage.CheckIntegrity() + + Verify existence and attributes of files installed from RPM + package. If all files installed exist and their attributes + matches, method returns "Pass". "Not passed" is returned when + arbitrary file differs in its attributes. And "Error" if + verification could not be done. + + Keyword arguments: + env -- Provider Environment (pycimmb.ProviderEnvironment) + object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName + specifying the object on which the method CheckIntegrity() + should be invoked. + + Returns a two-tuple containing the return value ( + type pywbem.Uint32 self.Values.CheckIntegrity) + and a list of CIMParameter objects representing the output parameters + + Output parameters: + Failed -- (type REF (pywbem.CIMInstanceName( + classname='LMI_YumFileCheck', ...)) + Array of references to LMI_YumFileCheck, that did not pass + verification. + + Possible Errors: + CIM_ERR_ACCESS_DENIED + CIM_ERR_INVALID_PARAMETER (including missing, duplicate, + unrecognized or otherwise incorrect parameters) + CIM_ERR_NOT_FOUND (the target CIM Class or instance does not + exist in the specified namespace) + CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor + the invocation request) + CIM_ERR_FAILED (some other unspecified error occurred) + + """ + + logger = env.get_logger() + logger.log_debug('Entering %s.cim_method_checkintegrity()' \ + % self.__class__.__name__) + + # TODO do something + raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented + out_params = [] + #out_params+= [pywbem.CIMParameter('failed', type='reference', + # value=[pywbem.CIMInstanceName(classname='LMI_YumFileCheck', ...),])] # TODO + #rval = # TODO (type pywbem.Uint32 self.Values.CheckIntegrity) + return (rval, out_params) + + def cim_method_update(self, env, object_name, + param_epoch=None, + param_release=None, + param_version=None): + """Implements LMI_YumInstalledPackage.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_YumInstalledPackage', ...)) + 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 object_name['System'] != get_computer_system_op(): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Unknown \"System\"") + + with YumDB.getInstance(env) as yb: + orig = 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(pkg, env, True))] + if orig.evra == pkg.evra: + logger.log_info('already up to date') + return (self.Values.Update.Already_newest, out_params) + + yb.update(update_to=True, + name=pkg.name, + epoch=pkg.epoch, + version=pkg.version, + release=pkg.release) + yb.buildTransaction() + yb.processTransaction() + logger.log_info('package {} updated to: {}'.format( + orig.name, pkg.evra)) + + return (self.Values.Update.Successful_installation, out_params) + + class Values(object): + class 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_YumInstalledPackage + ## get_providers() for associating CIM Class Name to python provider class name - -def get_providers(env): - lmi_yuminstalledpackage_prov = LMI_YumInstalledPackage(env) - return {'LMI_YumInstalledPackage': lmi_yuminstalledpackage_prov} + +def get_providers(env): + lmi_yuminstalledpackage_prov = LMI_YumInstalledPackage(env) + return {'LMI_YumInstalledPackage': lmi_yuminstalledpackage_prov} diff --git a/src/yum/providers/LMI_YumPackage.py b/src/yum/providers/LMI_YumPackage.py index f1441b0..5a9750b 100644 --- a/src/yum/providers/LMI_YumPackage.py +++ b/src/yum/providers/LMI_YumPackage.py @@ -1,3 +1,4 @@ +# -*- encoding: utf-8 -*- # Software Management Providers # # Copyright (C) 2012 Red Hat, Inc. All rights reserved. @@ -70,8 +71,9 @@ class LMI_YumPackage(CIMProvider2): logger.log_debug('Entering %s.get_instance()' \ % self.__class__.__name__) - pkg = YumPackage.object_path2pkg(env, model.path) - return pkg2model(pkg, env, keys_only=False, model=model) + with YumDB.getInstance(env): + pkg = object_path2pkg(env, model.path, 'all') + return pkg2model(pkg, env, keys_only=False, model=model) def enum_instances(self, env, model, keys_only): """Enumerate instances. @@ -108,7 +110,11 @@ class LMI_YumPackage(CIMProvider2): 'SoftwareElementID': None}) with YumDB.getInstance(env) as yb: - for pkg in yb.rpmdb: + # get all packages + pl = yb.doPackageLists('all', showdups=True) + pl = itertools.chain(pl.installed, pl.available) + # NOTE: available ∩ installed = ∅ + for pkg in sorted(pl, key=lambda a:a.evra): yield pkg2model(pkg, env, keys_only, model) def set_instance(self, env, instance, modify_existing): @@ -141,84 +147,9 @@ class LMI_YumPackage(CIMProvider2): logger = env.get_logger() logger.log_debug('Entering %s.set_instance()' \ % self.__class__.__name__) - - _has_key = lambda k: k in instance.properties or ( - instance.path is not None and k in instance.path) - def _get_key(k): - v = instance.properties.get(k, None) - if isinstance(v, pywbem.CIMProperty): return v.value - if v is not None: return v - logger.log_error('missing key "{}" in inst.props'.format(k)) - return instance.path[k] if k in instance.path else None - - # parse and check arguments - if modify_existing is True: - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "MofifyInstance is not supported") - match_props = {} # args for match_pkg - installed_kwargs = {} # args for rpmdb.installed - if instance['SoftwareElementID']: - m = re_nevra.match(instance['SoftwareElementID']) - if not m: - raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, - "SoftwareElementID could not be parsed.") - match_props['nevra'] = instance['SoftwareElementID'] - match_props['name'] = m.group('name') - for a in ('name', 'epoch', 'ver', 'rel', 'arch'): - installed_kwargs[a] = m.group(a) - else: - for matchattr, instattr in ( - ('name', 'name'), ('epoch', 'epoch'), ('version', 'ver'), - ('release', 'rel'), ('arch', 'arch')): - if _has_key(matchattr) and _get_key(matchattr): - match_props[matchattr] = installed_kwargs[instattr] = \ - _get_key(matchattr) - - if not match_props: - raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, - "Too few key values given (give at least a Name).") - if not 'name' in match_props and not 'nevra' in match_props: - raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, - "Missing either Name or SoftwareElementID property.") - - with YumDB.getInstance(env) as yb: - # check already installed - if yb.rpmdb.installed(**installed_kwargs): - raise pywbem.CIMError(pywbem.CIM_ERR_ALREADY_EXISTS, - "Package is already installed.") - - # get available packages - pl = yb.doPackageLists('available', showdups=True) - exact, matched, unmatched = yum.packages.parsePackages( - pl.available, [match_props['name']]) - exact_orig = exact - exact = sorted( [ p for p in exact if match_pkg(p, **match_props) ] - , key=lambda a: a.evra) - if len(exact) == 0: - logger.log_error('could not find any package for query: {}' - ' in list: {}' - .format(match_props, [p.nevra for p in exact_orig])) - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "No matching package found.") - if len(exact) > 1: - logger.log_info('found multiple matching packages' - ' for query: {}'.format(match_props)) - pkg = exact[-1] # select highest version - else: - logger.log_debug('exact match found for query: {}' - .format(match_props)) - pkg = exact[0] - - logger.log_info('installing package {}'.format(pkg.nevra)) - # install - yb.install(pkg) - yb.buildTransaction() - yb.processTransaction() - logger.log_info('package installed'.format(pkg.nevra)) - - # return instance - instance = pkg2model(pkg, env, True, instance) - return self.get_instance(env, instance) + # TODO create or modify the instance + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) # Remove to implement + return instance def delete_instance(self, env, instance_name): """Delete an instance. @@ -246,92 +177,29 @@ class LMI_YumPackage(CIMProvider2): logger.log_debug('Entering %s.delete_instance()' \ % self.__class__.__name__) - with YumDB.getInstance(env) as yb: - pkg = YumPackage.object_path2pkg(env, instance_name) - logger.log_info('removing package "%s"' % pkg.nevra) - yb.remove(pkg) - yb.buildTransaction() - yb.processTransaction() - logger.log_info('package "%s" removed' % pkg.nevra) - - def cim_method_checkintegrity(self, env, object_name): - """Implements LMI_YumPackage.CheckIntegrity() - - Verify existence and attributes of files installed from RPM - package. If all files installed exist and their attributes - matches, method returns "Pass". "Not passed" is returned when - arbitrary file differs in its attributes. And "Error" if - verification could not be done. - - Keyword arguments: - env -- Provider Environment (pycimmb.ProviderEnvironment) - object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName - specifying the object on which the method CheckIntegrity() - should be invoked. - - Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.CheckIntegrity) - and a list of CIMParameter objects representing the output parameters - - Output parameters: - Failed -- (type REF (pywbem.CIMInstanceName(classname='LMI_YumFileCheck', ...)) - Array of references to LMI_YumFileCheck, that did not pass - verification. - + # TODO delete the resource + # Remove to implement + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED) - 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__) + def cim_method_install(self, env, object_name): + """Implements LMI_YumPackage.Install() - # TODO do something - raise pywbem.CIMError(pywbem.CIM_ERR_METHOD_NOT_AVAILABLE) # Remove to implemented - out_params = [] - #out_params+= [pywbem.CIMParameter('failed', type='reference', - # value=[pywbem.CIMInstanceName(classname='LMI_YumFileCheck', ...),])] # TODO - #rval = # TODO (type pywbem.Uint32 self.Values.CheckIntegrity) - return (rval, out_params) - - def cim_method_update(self, env, object_name, - param_epoch=None, - param_release=None, - param_version=None): - """Implements LMI_YumPackage.Update() - - Updates the package to latest version. When any of "version" or - "release" argument is given, install only matching available - package. Otherwise try to update to newest package available. + Will install available package. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName specifying the object on which the method Update() should be invoked. - 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) + + Returns a two-tuple containing the return value (type pywbem.Uint32 self.Values.Install) and a list of CIMParameter objects representing the output parameters Output parameters: - Installed -- (type REF (pywbem.CIMInstanceName(classname='LMI_YumPackage', ...)) + Installed -- (type REF (pywbem.CIMInstanceName( + classname='LMI_YumInstalledPackage', ...)) The reference to newly installed package, if installation was - successful. Otherwise reference to current package. + successful. Null otherwise. Possible Errors: CIM_ERR_ACCESS_DENIED @@ -344,60 +212,73 @@ class LMI_YumPackage(CIMProvider2): CIM_ERR_FAILED (some other unspecified error occurred) """ - logger = env.get_logger() - logger.log_debug('Entering %s.cim_method_update()' \ + logger.log_debug('Entering %s.cim_method_install()' \ % self.__class__.__name__) - with YumDB.getInstance(env) as yb: - orig = YumPackage.object_path2pkg(env, object_name) - - evr_str = [] - for name, param in ( - ('epoch', param_epoch), - ('version', param_version), - ('release', param_release)): - evr_str.append("%s(%s)"%(name,param)) - if len(evr_str): - evr_str = "specific "+'-'.join(evr_str) - else: - evr_str = "the newest version-release" + # parse and check arguments + match_props = {} # args for match_pkg + if object_name['SoftwareElementID']: + m = re_nevra.match(object_name['SoftwareElementID']) + if not m: + raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, + "SoftwareElementID could not be parsed.") + match_props['nevra'] = object_name['SoftwareElementID'] + match_props['name'] = m.group('name') + else: + for matchattr, instattr in ( + ('name', 'name'), ('epoch', 'epoch'), ('version', 'ver'), + ('release', 'rel'), ('arch', 'arch')): + if object_name.get(matchattr, None): + match_props[matchattr] = object_name[matchattr] - logger.log_info('trying to update to %s of package \"%s\"' % - (evr_str, object_name["Name"])) + if not match_props: + raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, + "Too few key values given (give at least a Name).") + if not 'name' in match_props and not 'nevra' in match_props: + raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, + "Missing either Name or SoftwareElementID property.") + with YumDB.getInstance(env) as yb: + # get available packages pl = yb.doPackageLists('all', showdups=True) - exact, matched, unmatched = yum.packages.parsePackages( + exact,_,_ = yum.packages.parsePackages( itertools.chain(pl.available, pl.installed), - [orig.name]) - exact = sorted(exact, key=lambda a:a.evra) - - try: - pkg = [p for p in exact - if (not param_epoch or param_epoch == p.epoch) - and (not param_version or param_version == p.ver) - and (not param_release or param_release == p.rel)] [-1] - except IndexError: - logger.log_error( - 'could not find any matching available package') - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND) - out_params = [pywbem.CIMParameter('Installed', type='reference', - value=pkg2model(pkg, env, True))] - if orig.evra == pkg.evra: - logger.log_info('already up to date') - return (self.Values.Update.Already_newest, out_params) - - yb.update(update_to=True, - name=pkg.name, - epoch=pkg.epoch, - version=pkg.version, - release=pkg.release) + [match_props['name']]) + exact = yum.misc.unique(exact) + exact_orig = exact + exact = sorted( [ p for p in exact if match_pkg(p, **match_props) ] + , key=lambda a: a.evra) + if len(exact) == 0: + logger.log_error('could not find any package for query: {}' + ' in list: {}' + .format(match_props, [p.nevra for p in exact_orig])) + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "No matching package found.") + out_params = [ pywbem.CIMParameter('Installed', type='reference') ] + for pkg in exact: # check, whether package is already installed + if yb.rpmdb.installed(po=pkg): + out_params[0].value = pkg2model(pkg, env, True) + return (self.Values.Install.Already_installed, out_params) + if len(exact) > 1: # should not happen + logger.log_info('found multiple matching packages' + ' for query: {}'.format(match_props)) + pkg = exact[-1] # select highest version + else: + logger.log_debug('exact match found for query: {}' + .format(match_props)) + pkg = exact[0] + + logger.log_info('installing package {}'.format(pkg.nevra)) + # install + yb.install(pkg) yb.buildTransaction() yb.processTransaction() - logger.log_info('package {} updated to: {}'.format( - orig.name, pkg.evra)) + logger.log_info('package installed'.format(pkg.nevra)) - return (self.Values.Update.Successful_installation, out_params) + # return object_name + out_params[0].value = pkg2model(pkg, env, True, object_name) + return (self.Values.Install.Successful_installation, out_params) class Values(object): class DetailedStatus(object): @@ -549,11 +430,6 @@ class LMI_YumPackage(CIMProvider2): Oracle_Enterprise_Linux_64_bit = pywbem.Uint16(109) eComStation_32_bitx = pywbem.Uint16(110) - class Update(object): - Already_newest = pywbem.Uint16(0) - Successful_installation = pywbem.Uint16(1) - Failed = pywbem.Uint16(2) - class CommunicationStatus(object): Unknown = pywbem.Uint16(0) Not_Available = pywbem.Uint16(1) @@ -622,12 +498,12 @@ class LMI_YumPackage(CIMProvider2): # DMTF_Reserved = .. # Vendor_Reserved = 0x8000.. - class CheckIntegrity(object): - Pass = pywbem.Uint32(0) - Not_passed = pywbem.Uint32(1) - Error = pywbem.Uint32(2) + class Install(object): + Already_installed = pywbem.Uint32(0) + Successful_installation = pywbem.Uint32(1) + Failed = pywbem.Uint32(2) -## end of class LMI_YumPackageProvider +## end of class LMI_YumPackage ## get_providers() for associating CIM Class Name to python provider class name diff --git a/src/yum/providers/util/common.py b/src/yum/providers/util/common.py index 1519a9a..7d918a4 100644 --- a/src/yum/providers/util/common.py +++ b/src/yum/providers/util/common.py @@ -1,3 +1,4 @@ +# -*- encoding: utf-8 -*- # Software Management Providers # # Copyright (C) 2012 Red Hat, Inc. All rights reserved. @@ -22,6 +23,7 @@ import collections from datetime import datetime import grp import hashlib +import itertools import os import platform import pwd @@ -48,7 +50,8 @@ class YumDB(singletonmixin.Singleton): if not isinstance(env, pycimmb.ProviderEnvironment): raise TypeError("env must be instance of" " pycimmb.ProviderEnvironment") - self._yum = yum.YumBase(*args, **kwargs) + self._yum_args = (args, kwargs) + self._yum = None self._db_mtime = 0 self._lock_cnt = 0 self.env = env @@ -70,15 +73,15 @@ class YumDB(singletonmixin.Singleton): def __enter__(self): self._lock_cnt += 1 if self._lock_cnt < 2: + self._yum = yum.YumBase(*self._yum_args[0], **self._yum_args[1]) if not self.is_locked() and self.is_dirty(): self.update_db() self._yum.doLock() return self def __exit__(self, exc_type, exc_value, traceback): - if self._lock_cnt > 0: - self._yum.closeRpmDB() - self._yum.doUnlock() + if self._lock_cnt == 1: + del self._yum self._lock_cnt -= 1 def __getattr__(self, name): @@ -165,6 +168,63 @@ def match_pkg(pkg, **kwargs): return False return True +def object_path2pkg(env, op, package_list='installed'): + """ + @param package_list one of {'installed', 'all', 'available'} + says, where to look for given package + """ + if not isinstance(op, pywbem.CIMInstanceName): + raise TypeError("op must be an instance of CIMInstanceName") + if not isinstance(package_list, basestring): + raise TypeError("package_list must be a string") + if not package_list in ('installed', 'all', 'available'): + raise ValueError('unsupported package list "%s"'%package_list) + + tos = get_target_operating_system()[0] + if ( not op['Name'] or not op['SoftwareElementID'] + or not op['SoftwareElementID'].startswith(op['Name']) + or op['SoftwareElementID'].find(op['Version']) == -1): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.") +# if op['SoftwareElementState'] not in ("2", 2): +# raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, +# "Only \"Executable\" software element state supported") + if not check_target_operating_system(op['TargetOperatingSystem']): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Wrong target operating system.") + if not op['Name'] or not op['Version']: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + 'Both "Name" and "Version" must be given') + m = re_nevra.match(op['SoftwareElementID']) + if not m: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Wrong SotwareElementID. Expected valid nevra" + " (name-epoch:version-release.arch).") + if op['Version'] != m.group('ver'): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Version does not match version part in SoftwareElementID.") + kwargs = dict((k, m.group(k)) for k in + ('name', 'epoch', 'ver', 'rel', 'arch')) + with YumDB.getInstance(env) as yb: + if ( package_list == 'installed' + and not yb.rpmdb.installed(**kwargs)): + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "Package not installed") + + pl = yb.doPackageLists(package_list, + showdups=package_list != 'installed') + if package_list != 'all': + pl = getattr(pl, package_list) + else: + # NOTE: available ∩ installed = ∅ + pl = itertools.chain(pl.available, pl.installed) + exact,_,_ = yum.packages.parsePackages(pl, [op['Name']]) + for pkg in yum.misc.unique(exact): + if pkg.evra == m.group('evra'): break + else: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + "No matching package found") + return pkg + class YumPackage: """ Just a namespace for common function related to YumPackage provider. @@ -527,50 +587,6 @@ class YumPackage: res = 0 return pywbem.Uint16(res) - @staticmethod - def object_path2pkg(env, op): - if not isinstance(op, pywbem.CIMInstanceName): - raise TypeError("op must be an instance of CIMInstanceName") - tos = get_target_operating_system()[0] - if ( not op['Name'] or not op['SoftwareElementID'] - or not op['SoftwareElementID'].startswith(op['Name']) - or op['SoftwareElementID'].find(op['Version']) == -1): - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Wrong keys.") - if op['SoftwareElementState'] not in ("2", 2): - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "Only \"Executable\" software element state supported") - if not check_target_operating_system(op['TargetOperatingSystem']): - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "Wrong target operating system.") - if not op['Name'] or not op['Version']: - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - 'Both "Name" and "Version" must be given') - m = re_nevra.match(op['SoftwareElementID']) - if not m: - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "Wrong SotwareElementID. Expected valid nevra" - " (name-epoch:version-release.arch).") - if op['Version'] != m.group('ver'): - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "Version does not match version part in SoftwareElementID.") - kwargs = dict((k, m.group(k)) for k in - ('name', 'epoch', 'ver', 'rel', 'arch')) - with YumDB.getInstance(env) as yb: - if not yb.rpmdb.installed(**kwargs): - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "Package not installed") - - pl = yb.doPackageLists('installed') - exact, matched, unmatched = yum.packages.parsePackages( - pl.installed, [op['Name']]) - exact = yum.misc.unique(exact) - for pkg in exact: - if pkg.evra == m.group('evra'): break - else: - raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, - "No matching package installed") - return pkg - @staticmethod def pkg2model_wrapper(namespace, classname): """ @@ -594,26 +610,39 @@ class YumPackage: model = pywbem.CIMInstanceName(classname, namespace=namespace) if not keys_only: model = pywbem.CIMInstance(classname, path=model) - model['Name'] = pkg.name - model['SoftwareElementID'] = pkg.nevra - model['SoftwareElementState'] = pywbem.Uint16(2) - model['TargetOperatingSystem'] = tos - model['Version'] = pkg.version - if not keys_only: - model['Caption'] = pkg.summary - model['Description'] = pkg.description - model['InstallDate'] = pywbem.CIMDateTime( - datetime.fromtimestamp(pkg.installtime)) - if pkg.vendor: - model['Manufacturer'] = pkg.vendor - model['Release'] = pkg.release - model['Epoch'] = pywbem.Uint16(pkg.epoch) - model["Architecture"] = YumPackage.parse_arch(pkg.arch, env) - model['License'] = YumPackage.parse_license(pkg.license, env) - model['LicenseString']= pkg.license - model['Group'] = YumPackage.parse_group(pkg.group, env) - model['GroupString'] = pkg.group - model['Size'] = pywbem.Uint64(pkg.size) + if isinstance(model, pywbem.CIMInstance): + def _set_key(k, v): + model[k] = v + model.path[k] = v + else: + _set_key = model.__setitem__ + with YumDB.getInstance(env): + _set_key('Name', pkg.name) + _set_key('SoftwareElementID', pkg.nevra) + _set_key('SoftwareElementState', pywbem.Uint16(2 + if isinstance(pkg, yum.rpmsack.RPMInstalledPackage) + else 1)) + _set_key('TargetOperatingSystem', tos) + _set_key('Version', pkg.version) + if not keys_only: + model['Caption'] = pkg.summary + model['Description'] = pkg.description + if isinstance(pkg, yum.rpmsack.RPMInstalledPackage): + model['InstallDate'] = pywbem.CIMDateTime( + datetime.fromtimestamp(pkg.installtime)) + if pkg.vendor: + model['Manufacturer'] = pkg.vendor + model['Release'] = pkg.release + model['Epoch'] = pywbem.Uint16(pkg.epoch) + model["Architecture"] = YumPackage.parse_arch( + pkg.arch, env) + model['License'] = YumPackage.parse_license( + pkg.license, env) + model['LicenseString']= pkg.license + model['Group'] = YumPackage.parse_group( + pkg.group, env) + model['GroupString'] = pkg.group + model['Size'] = pywbem.Uint64(pkg.size) return model return pkg2model diff --git a/src/yum/test/test_common.sh b/src/yum/test/test_common.sh index 25d931f..1e5cd98 100644 --- a/src/yum/test/test_common.sh +++ b/src/yum/test/test_common.sh @@ -6,8 +6,7 @@ if [ -z $CLASS_NAME ]; then fi -url="http://localhost:5988/root/cimv2" -op="${url}:${cls}" +url="http://localhost:5988" fedora_release=`sed 's/Fedora release\s*\([0-9]\+\).*/\1/' \ /etc/fedora-release` @@ -18,6 +17,18 @@ function make_nevra() { printf "%s-%s:%s-%s.%s" $@ } +function make_op() { + local class_name keys + read class_name keys <<< "$@" + [ -n "$keys" ] && keys=".$keys" + printf "root/cimv2:$class_name$keys" +} + +computer_system_keys="CreationClassName=\"Linux_ComputerSystem\"" +computer_system_keys+=",Name=$(hostname)" +computer_system_op=`make_op Linux_ComputerSystem "$computer_system_keys"` +op=`make_op ${CLASS_NAME}` + function make_rpm_name() { local name epoch ver rel arch; if [ $# -ge 5 ]; then diff --git a/src/yum/test/yum_package.sh b/src/yum/test/yum_package.sh index c13cbe4..5d81955 100755 --- a/src/yum/test/yum_package.sh +++ b/src/yum/test/yum_package.sh @@ -4,6 +4,7 @@ dir=`dirname $0` [ -z "$dir" ] && dir=. CLASS_NAME="LMI_YumPackage" +. $dir/test_common.sh declare -A pkg1 pkg2 if [[ "$fedora_release" = 16 ]]; then @@ -84,10 +85,22 @@ test_install() { fi local keys=`make_inst_keys ${nevra_arr[@]}` - local url="$op.${keys}" - echo "creating instance of $CLASS_NAME: $url" - wbemerr ci "$url" "${keys},Release=\"$rel\"" - + local url="$url/$op.${keys}" + + echo "trying to get instance" + if ! wbemerr gi "$url" |& \ + grep -q '\