# -*- 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 . """ 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)