diff options
Diffstat (limited to 'src/software/openlmi/software/core/InstallationJob.py')
-rw-r--r-- | src/software/openlmi/software/core/InstallationJob.py | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/src/software/openlmi/software/core/InstallationJob.py b/src/software/openlmi/software/core/InstallationJob.py new file mode 100644 index 0000000..b082331 --- /dev/null +++ b/src/software/openlmi/software/core/InstallationJob.py @@ -0,0 +1,579 @@ +# -*- encoding: utf-8 -*- +# Software Management Providers +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +""" +Just a common functionality related to SoftwareInstallationJob. +""" + +from datetime import datetime, timedelta +import pywbem +import time + +from openlmi.common import cmpi_logging +from openlmi.software.yumdb import errors, jobs +from openlmi.software.yumdb import YumDB + +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.YumCheckPackage : + 'Software package check job %(jobid)d for "%(pkg)s".', + jobs.YumInstallPackageFromURI : + 'Software package installation job %(jobid)d from uri: "%(uri)s".', +} + +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' + } + +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' + if 'method_name' in job.metadata: + model['MethodName'] = job.metadata['method_name'] + else: + model["MethodName"] = pywbem.CIMProperty('MethodName', + type='string', value=None) + model['Name'] = job.metadata['name'] + 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, 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 + """ + 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 model is None: + model = pywbem.CIMInstanceName("LMI_SoftwareInstallationJob", + namespace="root/cimv2") + if not keys_only: + model = pywbem.CIMInstance("LMI_SoftwareInstallationJob", + path=model) + + jobid = job.jobid if isinstance(job, jobs.YumAsyncJob) else job + model['InstanceID'] = 'LMI:SoftwareInstallationJob:%d' % 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, "Wrong keys.") + instid = op['InstanceID'] + if not instid.lower().startswith("lmi:softwareinstallationjob:"): + raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, + "InstanceID must start with LMI:SoftwareInstallationJob: prefix.") + try: + instid = int(instid[len("LMI:SoftwareInstallationJob:"):]) + except ValueError: + raise pywbem.CIMError(pywbem.CIM_ERR_INVALID_PARAMETER, + 'Invalid InstanceID "%s"' % instid) + try: + return YumDB.get_instance().get_job(instid) + except errors.JobNotFound: + raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, + 'Not 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) + |