From d5ac71ea513deffe78fb48e4f29ef69102f74cdb Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Wed, 3 Jul 2013 13:04:08 +0200 Subject: clean up YumDB instance only if it's instantiated using global variable to signal, whether running under broker process --- .../openlmi/software/core/InstallationJob.py | 154 +++++++++++++++++---- src/software/openlmi/software/yumdb/__init__.py | 10 ++ src/software/openlmi/software/yumdb/process.py | 9 +- 3 files changed, 139 insertions(+), 34 deletions(-) diff --git a/src/software/openlmi/software/core/InstallationJob.py b/src/software/openlmi/software/core/InstallationJob.py index e90ad1b..0a3d44a 100644 --- a/src/software/openlmi/software/core/InstallationJob.py +++ b/src/software/openlmi/software/core/InstallationJob.py @@ -17,7 +17,8 @@ # along with this program. If not, see . """ -Just a common functionality related to SoftwareInstallationJob. +Just a common functionality related to SoftwareInstallationJob +and SoftwareVerificationJob. """ from datetime import datetime, timedelta @@ -25,10 +26,16 @@ import pywbem import time from openlmi.common import cmpi_logging +from openlmi.software import util from openlmi.software.core import Error from openlmi.software.yumdb import errors, jobs from openlmi.software.yumdb import YumDB +JOB_CLASS_NAMES = ( + "LMI_SoftwareInstallationJob", + "LMI_SoftwareVerificationJob" +) + JOB_DESCRIPTIONS = { jobs.YumInstallPackage : 'Software package installation job %(jobid)d for "%(pkg)s".', @@ -38,23 +45,27 @@ JOB_DESCRIPTIONS = { 'Software package update job %(jobid)d for "%(pkg)s".', jobs.YumUpdatePackage : 'Software package update job %(jobid)d for "%(pkg)s".', - jobs.YumCheckPackage : - 'Software package check 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) = range(3) +, 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") + "InstallFromByteStream", + "VerifyInstalledIdentity") class Values(object): class DetailedStatus(object): @@ -397,6 +408,46 @@ class Values(object): 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 openlmi.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): """ @@ -422,12 +473,32 @@ def make_method_params(job, class_name, include_input, include_output): if include_input and "input_params" in job.metadata: for (name, value) in job.metadata["input_params"].items(): inst[name] = value - if include_output and "output_params" in job.metadata: - # overwrite any input parameter - for (name, value) in job.metadata["output_params"].iteritems(): - 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): """ @@ -506,11 +577,22 @@ def _fill_nonkeys(job, model): job.created)) @cmpi_logging.trace_function -def job2model(job, keys_only=True, model=None): +def job2model(job, class_name=None, keys_only=True, model=None): """ - @param job can either be jobs.YumAsyncJob or integer - @param model if not None, will be filled with data, otherwise - a new instance of CIMInstance or CIMObjectPath is created + 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") @@ -520,15 +602,23 @@ def job2model(job, keys_only=True, model=None): 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("LMI_SoftwareInstallationJob", - namespace="root/cimv2") + model = pywbem.CIMInstanceName(class_name, namespace="root/cimv2") if not keys_only: - model = pywbem.CIMInstance("LMI_SoftwareInstallationJob", - path=model) + model = pywbem.CIMInstance(class_name, path=model) jobid = job.jobid if isinstance(job, jobs.YumAsyncJob) else job - model['InstanceID'] = 'LMI:LMI_SoftwareInstallationJob:%d' % jobid + 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: @@ -545,19 +635,27 @@ def object_path2job(op): "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_softwareinstallationjob:"): raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, - "InstanceID must start with LMI:LMI_SoftwareInstallationJob:" - " prefix.") - try: - instid = int(instid[len("LMI:LMI_SoftwareInstallationJob:"):]) - except ValueError: + "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, - 'Invalid InstanceID "%s"' % instid) + "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: - return YumDB.get_instance().get_job(instid) + 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']) diff --git a/src/software/openlmi/software/yumdb/__init__.py b/src/software/openlmi/software/yumdb/__init__.py index b913bfa..71fc1c5 100644 --- a/src/software/openlmi/software/yumdb/__init__.py +++ b/src/software/openlmi/software/yumdb/__init__.py @@ -205,6 +205,16 @@ class YumDB(singletonmixin.Singleton): # 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 """ diff --git a/src/software/openlmi/software/yumdb/process.py b/src/software/openlmi/software/yumdb/process.py index c4171ec..56227f3 100644 --- a/src/software/openlmi/software/yumdb/process.py +++ b/src/software/openlmi/software/yumdb/process.py @@ -898,13 +898,10 @@ class YumWorker(Process): self._jobmgr.name, self._jobmgr.ident) self._pkg_cache = weakref.WeakValueDictionary() - # We've been started by YumDB, but in this process we don't need - # the instance object - let's delete it. - # This allows the code, that can be run both from broker and YumWorker - # process, to check it. When run under broker, the - # YumDB.isInstantiated() returns True. + # This allows the code, that can be run both from broker and + # YumWorker, to check, whether it's called by this process. from openlmi.software.yumdb import YumDB - YumDB._forget_class_instance_reference_for_testing() + YumDB.RUNNING_UNDER_CIMOM_PROCESS = False self._main_loop() LOG.info("terminating") -- cgit