def create_jobs(interval_choice):
        """
        create the jobs based on publish status
        All jobs will be sorted agaist with publish.priority
        """
        job = None
        log = None
        jobs = []
        job_batch_id = interval_choice.job_batch_id

        check_job = Job(
            id=-1, batch_id="CK" +
            job_batch_id) if interval_choice == Realtime.instance() else None
        up_to_date = False

        for p in Publish.objects.filter(interval=interval_choice.name,
                                        waiting=0).order_by('priority'):
            if not p.publish_status.publish_enabled:
                #publish is disabled, ignore
                continue

            if interval_choice == Realtime.instance():
                #Realtime publish, check whether input is up to date.
                up_to_date = True
                for i in p.inputs:
                    if not i.is_up_to_date(check_job, False):
                        #input is not up to date, clear importing info to avoid double check for foreign table.
                        up_to_date = False
                        break
                if up_to_date:
                    continue

            p.waiting = models.F("waiting") + 1
            job = Job(batch_id=job_batch_id,
                      publish=p,
                      state=Waiting.instance().name,
                      previous_state=None,
                      message=None,
                      created=timezone.now(),
                      launched=None,
                      finished=None,
                      job_type=interval_choice.name)

            #add log
            log = JobLog(
                state="Create",
                outcome="Create",
                message="Create by {0} cron job".format(interval_choice),
                next_state=job.state,
                start_time=timezone.now(),
                end_time=timezone.now())

            jobs.append((p, job, log))

        with transaction.atomic():
            for p, job, log in jobs:
                p.save(update_fields=['waiting'])
                job.save()
                log.job_id = job.id
                log.save()
    def create_jobs(interval_choice):
        """
        create the jobs based on publish status
        All jobs will be sorted agaist with publish.priority
        """
        job = None
        log = None
        jobs = []
        job_batch_id = interval_choice.job_batch_id

        check_job = Job(id=-1,batch_id="CK" + job_batch_id) if interval_choice == Realtime.instance() else None
        up_to_date = False
        
        for p in Publish.objects.filter(interval = interval_choice.name, waiting = 0).order_by('priority'):
            if not p.publish_status.publish_enabled:
                #publish is disabled, ignore
                continue

            if interval_choice == Realtime.instance():
                #Realtime publish, check whether input is up to date.
                up_to_date = True
                for i in p.inputs:
                    if not i.is_up_to_date(check_job,False):
                        #input is not up to date, clear importing info to avoid double check for foreign table.
                        up_to_date = False
                        break
                if up_to_date:
                    continue

            p.waiting = models.F("waiting") + 1
            job = Job(
                        batch_id = job_batch_id,
                        publish = p,
                        state = Waiting.instance().name,
                        previous_state = None,
                        message = None,
                        created = timezone.now(),
                        launched = None,
                        finished = None,
                        job_type = interval_choice.name
                    )

            #add log
            log = JobLog(
                        state = "Create",
                        outcome = "Create",
                        message = "Create by {0} cron job".format(interval_choice),
                        next_state = job.state,
                        start_time = timezone.now(),
                        end_time = timezone.now())

            jobs.append((p,job,log))

        with transaction.atomic():
            for p,job,log in jobs:
                p.save(update_fields=['waiting'])
                job.save()
                log.job_id = job.id
                log.save()
Exemple #3
0
    def _create_job(publish,
                    job_interval=JobInterval.Manually,
                    job_batch_id=None):
        """
        manually create a job
        return (true,'OK'), if create a job, otherwise return (False,message)
        """
        if not publish.publish_status.publish_enabled:
            #publish is disabled, ignore
            return (False, "Disabled")

        job = None
        job = Job.objects.filter(publish=publish,
                                 state=Waiting.instance().name).first()
        if job > 0:
            #already have one waiting harvest job, create another is meanless.
            return (True, job.pk)

        if not job_batch_id:
            job_batch_id = job_interval.job_batch_id()

        with transaction.atomic():
            if publish.waiting > 0:
                #already have one waiting harvest job, create another is meanless.
                return

            publish.waiting = models.F("waiting") + 1
            job = Job(batch_id=job_batch_id,
                      publish=publish,
                      state=Waiting.instance().name,
                      previous_state=None,
                      message=None,
                      created=timezone.now(),
                      launched=None,
                      finished=None,
                      job_type=job_interval.name)
            publish.save(update_fields=['waiting'])
            job.save()
            #add log
            log = JobLog(
                job_id=job.id,
                state="Create",
                outcome="Create",
                message="Created by custodian" if job_interval
                == JobInterval.Manually else "Created by other application",
                next_state=job.state,
                start_time=timezone.now(),
                end_time=timezone.now())
            log.save()

        return (True, job.id)
    def _create_job(publish,job_interval=JobInterval.Manually,job_batch_id=None):
        """
        manually create a job
        return (true,'OK'), if create a job, otherwise return (False,message)
        """
        if not publish.publish_status.publish_enabled:
            #publish is disabled, ignore
            return (False,"Disabled")

        job = None
        job = Job.objects.filter(publish=publish,state=Waiting.instance().name).first()
        if job > 0:
            #already have one waiting harvest job, create another is meanless.
            return (True,job.pk)

        if not job_batch_id:
            job_batch_id = job_interval.job_batch_id()

        with transaction.atomic():
            if publish.waiting > 0:
                #already have one waiting harvest job, create another is meanless.
                return;

            publish.waiting = models.F("waiting") + 1
            job = Job(
                        batch_id = job_batch_id,
                        publish = publish,
                        state = Waiting.instance().name,
                        previous_state = None,
                        message = None,
                        created = timezone.now(),
                        launched = None,
                        finished = None,
                        job_type = job_interval.name
                    )
            publish.save(update_fields=['waiting'])
            job.save()
            #add log
            log = JobLog(
                        job_id = job.id,
                        state = "Create",
                        outcome = "Create",
                        message = "Created by custodian" if job_interval == JobInterval.Manually else "Created by other application",
                        next_state = job.state,
                        start_time = timezone.now(),
                        end_time = timezone.now())
            log.save()

        return (True,job.id)
    def run(job, first_run=True, step=False):
        current_state = JobState.get_jobstate(job.state)

        previous_state = None
        if job.previous_state:
            previous_state = JobState.get_jobstate(job.previous_state)

        log = None
        while True:
            start_time = timezone.now()
            logger.debug(
                "job(id={0},name={1}) begins to execute state ({2})".format(
                    job.id, job.publish.name, job.state))
            if current_state.is_end_state:
                #current job is already finished.
                logger.debug(
                    "job(id={0},name={1},state={2}) is finished".format(
                        job.id, job.publish.name, job.state))
                return
            elif job.user_action and (
                (job.user_action == JobStateOutcome.cancelled_by_custodian
                 and current_state.cancellable) or
                (job.user_action != JobStateOutcome.cancelled_by_custodian)):
                #have a pending user action.
                if not current_state.outcome_cls.is_manual_outcome(
                        job.user_action):
                    #a invalid user action
                    next_state = current_state
                    state_result = (
                        JobStateOutcome.internal_error,
                        "The action '{0}' is not a valid user action.".format(
                            job.user_action))
                else:
                    try:
                        next_state = current_state.next_state(job.user_action)
                        state_result = (job.user_action, job.user_action)
                        #user action is processed, clear it
                        job.user_action = None
                    except:
                        #can not apply the user action on the current state, stay in the same state
                        next_state = current_state
                        state_result = (
                            JobStateOutcome.internal_error,
                            "The action '{0}' can not apply on the current state({1})."
                            .format(job.user_action, current_state.name))
            elif current_state.is_interactive_state:
                #job is at interactive state, but no user action is requested, return and wait user action.
                return
            elif current_state.is_error_state:
                #wait the configured interval before continue
                try:
                    if not first_run and job.last_execution_end_time and timezone.now(
                    ) < job.last_execution_end_time + timedelta(
                            seconds=BorgConfiguration.RETRY_INTERVAL):
                        #early than the next execution time. can not run this time
                        return
                    else:
                        state_result = current_state.execute(
                            job, previous_state)
                except:
                    #can not find the last execution time. run it
                    state_result = current_state.execute(job, previous_state)
                next_state = current_state.next_state(state_result[0])
            else:
                try:
                    #normal state without pending user action
                    state_result = current_state.execute(job, previous_state)
                    if not state_result:
                        raise Exception(
                            "The outcome of state '{0}' is null.".format(
                                current_state.name))
                except:
                    #run into a unexpected exception
                    state_result = (JobStateOutcome.internal_error,
                                    JobState.get_exception_message())

                try:
                    next_state = current_state.next_state(state_result[0])
                except:
                    #can not find a transition from the current state with the state_result, stay at the current state.
                    next_state = current_state
                    if state_result[1]:
                        state_result = (
                            state_result[0],
                            "{0}\n=======================\n{1}".format(
                                JobState.get_exception_message(),
                                state_result[1]))
                    else:
                        state_result = (state_result[0],
                                        JobState.get_exception_message())
            end_time = timezone.now()

            logger.debug(
                "job(id={0},name={1}) ends to execute state ({2}) with result ({3});"
                .format(job.id, job.publish.name, job.state, state_result))

            #set the retry times
            if current_state == next_state:
                #stay at the same state, runs into some exception
                job.retry_times += 1
            elif current_state.is_interactive_state:
                #current state is interactive state.
                job.retry_times = 0
            elif current_state.is_error_state:
                #current state is a error state
                if next_state.is_error_state:
                    #stay at the same state, run into a exception
                    job.retry_times += 1
                elif isinstance(next_state, current_state._normal_state):
                    #a normal transition from current state to its associated normal state
                    pass
                else:
                    #a transition from current state to other normal state, reset retry_times.
                    job.retry_times = 0
            elif next_state.is_end_state:
                #reach the end state or a volative state
                job.retry_times = 0
            else:
                #current state is a normal state
                if not next_state.is_error_state:
                    #a successful transition from a normal state to another normal state.
                    job.retry_times = 0
                else:
                    #a failed transition from a normal state to a failed state
                    job.retry_times += 1

            log = JobLog(job_id=job.id,
                         state=job.state,
                         outcome=state_result[0],
                         message=state_result[1],
                         next_state=next_state,
                         start_time=start_time,
                         end_time=end_time)

            if current_state.is_error_state and not current_state.is_interactive_state and not job.user_action and isinstance(
                    next_state, current_state._normal_state):
                #normal transition from error state to normal state, ignore the log
                log = None
            elif next_state.is_error_state or current_state == next_state:
                #failed,
                last_log = JobLog.objects.filter(
                    job_id=job.id).order_by("-pk").first()
                if last_log and last_log.state == job.state and last_log.outcome == state_result[
                        0] and last_log.message == state_result[
                            1] and last_log.next_state == next_state.name:
                    #same execption occur
                    last_log.start_time = start_time
                    last_log.end_time = end_time
                    log = last_log

            if current_state != next_state:
                #job move to a new state, change the job state
                job.previous_state = job.state
                job.state = next_state.name
            else:
                #some bad thing happens,job stays at the same state, leave the job's state untouched
                pass

            job.last_execution_end_time = timezone.now()
            job.message = state_result[1]
            job.save(update_fields=[
                'previous_state', 'state', 'message', 'retry_times',
                'last_execution_end_time', 'user_action'
            ])
            if log:
                log.save()

            #import ipdb; ipdb.set_trace()
            if current_state == next_state:
                #stay in the same state, stop execution
                logger.debug(
                    "job(id={0},name={1},state={2}) stay in the same state.".
                    format(job.id, job.publish.name, job.state))
                return
            elif next_state.is_error_state:
                return
            elif step:
                return

            #set previous_state to the current state, current_state to the next_state
            previous_state = current_state
            current_state = next_state
    def run(job,first_run=True,step=False):
        current_state = JobState.get_jobstate(job.state)

        previous_state = None
        if job.previous_state:
            previous_state = JobState.get_jobstate(job.previous_state)

        log = None
        while True:
            start_time = timezone.now()
            logger.debug("job(id={0},name={1}) begins to execute state ({2})".format(job.id,job.publish.name,job.state))
            if current_state.is_end_state:
                #current job is already finished.
                logger.debug("job(id={0},name={1},state={2}) is finished".format(job.id,job.publish.name,job.state))
                return
            elif job.user_action and ((job.user_action == JobStateOutcome.cancelled_by_custodian and current_state.cancellable) or (job.user_action != JobStateOutcome.cancelled_by_custodian)):
                #have a pending user action.
                if not current_state.outcome_cls.is_manual_outcome(job.user_action):
                    #a invalid user action
                    next_state = current_state
                    state_result = (JobStateOutcome.internal_error, "The action '{0}' is not a valid user action.".format(job.user_action))
                else:
                    try:
                        next_state = current_state.next_state(job.user_action)
                        state_result = (job.user_action,job.user_action)
                        #user action is processed, clear it
                        job.user_action = None
                    except:
                        #can not apply the user action on the current state, stay in the same state
                        next_state = current_state
                        state_result = (JobStateOutcome.internal_error, "The action '{0}' can not apply on the current state({1}).".format(job.user_action,current_state.name))
            elif current_state.is_interactive_state:
                #job is at interactive state, but no user action is requested, return and wait user action.
                return
            elif current_state.is_error_state:
                #wait the configured interval before continue
                try:
                    if not first_run and job.last_execution_end_time and timezone.now() < job.last_execution_end_time + timedelta(seconds=BorgConfiguration.RETRY_INTERVAL):
                        #early than the next execution time. can not run this time
                        return
                    else:
                        state_result = current_state.execute(job,previous_state)
                except:
                    #can not find the last execution time. run it
                    state_result = current_state.execute(job,previous_state)
                next_state = current_state.next_state(state_result[0])
            else:
                try:
                    #normal state without pending user action
                    state_result = current_state.execute(job,previous_state)
                    if not state_result:
                        raise Exception("The outcome of state '{0}' is null.".format(current_state.name))
                except:
                    #run into a unexpected exception
                    state_result = (JobStateOutcome.internal_error, JobState.get_exception_message())

                try:
                    next_state = current_state.next_state(state_result[0])
                except:
                    #can not find a transition from the current state with the state_result, stay at the current state.
                    next_state = current_state
                    if state_result[1]:
                        state_result = (state_result[0],"{0}\n=======================\n{1}".format(JobState.get_exception_message(),state_result[1]))
                    else:
                        state_result = (state_result[0],JobState.get_exception_message())
            end_time = timezone.now()

            logger.debug("job(id={0},name={1}) ends to execute state ({2}) with result ({3});".format(job.id,job.publish.name,job.state,state_result))

            #set the retry times
            if current_state == next_state:
                #stay at the same state, runs into some exception
                job.retry_times += 1
            elif current_state.is_interactive_state:
                #current state is interactive state.
                job.retry_times = 0
            elif current_state.is_error_state:
                #current state is a error state
                if next_state.is_error_state:
                    #stay at the same state, run into a exception
                    job.retry_times += 1
                elif isinstance(next_state,current_state._normal_state):
                    #a normal transition from current state to its associated normal state
                    pass
                else:
                    #a transition from current state to other normal state, reset retry_times.
                    job.retry_times = 0
            elif next_state.is_end_state :
                #reach the end state or a volative state
                job.retry_times = 0
            else:
                #current state is a normal state
                if not next_state.is_error_state:
                    #a successful transition from a normal state to another normal state.
                    job.retry_times = 0
                else:
                    #a failed transition from a normal state to a failed state
                    job.retry_times += 1

            log = JobLog(
                        job_id = job.id,
                        state = job.state,
                        outcome = state_result[0],
                        message = state_result[1],
                        next_state = next_state,
                        start_time = start_time,
                        end_time = end_time)

            if current_state.is_error_state and not current_state.is_interactive_state and not job.user_action and isinstance(next_state,current_state._normal_state):
                #normal transition from error state to normal state, ignore the log
                log = None
            elif next_state.is_error_state or current_state == next_state:
                #failed,
                last_log = JobLog.objects.filter(job_id = job.id).order_by("-pk").first()
                if last_log and last_log.state == job.state and last_log.outcome == state_result[0] and last_log.message == state_result[1] and last_log.next_state == next_state.name:
                    #same execption occur
                    last_log.start_time = start_time
                    last_log.end_time = end_time
                    log = last_log

            if current_state != next_state:
                #job move to a new state, change the job state
                job.previous_state = job.state
                job.state = next_state.name
            else:
                #some bad thing happens,job stays at the same state, leave the job's state untouched
                pass

            job.last_execution_end_time = timezone.now()
            job.message = state_result[1]
            job.save(update_fields=['previous_state','state','message','retry_times','last_execution_end_time','user_action'])
            if log:
                log.save()

            #import ipdb; ipdb.set_trace()
            if current_state == next_state:
                #stay in the same state, stop execution
                logger.debug("job(id={0},name={1},state={2}) stay in the same state.".format(job.id,job.publish.name,job.state))
                return
            elif next_state.is_error_state:
                return
            elif step:
                return

            #set previous_state to the current state, current_state to the next_state
            previous_state = current_state
            current_state = next_state