예제 #1
0
def test_checkpoints():
    checkpoint(actor=_ACTOR_NAME, phase=_PHASE_NAME, context=_CONTEXT_NAME, hostname=_HOSTNAME)
    result = get_checkpoints(_CONTEXT_NAME)
    assert result and len(result) == 1
    assert result[0]['id']
    assert result[0]['actor'] == _ACTOR_NAME
    assert result[0]['phase'] == _PHASE_NAME
    assert result[0]['stamp'].endswith('Z')
예제 #2
0
    def run(self,
            context=None,
            until_phase=None,
            until_actor=None,
            skip_phases_until=None,
            skip_dialogs=False,
            only_with_tags=None):
        """
        Executes the workflow

        :param context: Custom execution ID to be used instead of a randomly generated UUIDv4
        :type context: str
        :param until_phase: Specify until including which phase the execution should run - phase.stage can be used to
                            control it even more granularly. `phase` is any phase name where `stage` refers to `main`,
                            `before` or `after`. If no stage is defined, `after` is assumed to be the default value.
                            The execution ends when this phase (and stage, if specified) has been executed.
        :type until_phase: str
        :param until_actor: The execution finishes when this actor has been executed.
        :type until_actor: str
        :param skip_phases_until: Skips all phases until including the phase specified, and then continues the
               execution.
        :type skip_phases_until: str or None
        :param skip_dialogs: Inform actors about the mode of dialogs processing. If skip_dialogs is set to True it
                             means that dialogs can't be processed in the current workflow run interactively and
                             every attempted call of get_answers api method will be non-blocking, returning an empty
                             dict if no user choice was found in answerfile or a selected option otherwise.
                             If skip_dialogs is set to False then in case of absent recorded answer the dialog will
                             be rendered in a blocking user input requiring way.
                             The value of skip_dialogs will be passed to the actors that can theoretically use it for
                             their purposes.
        :type skip_dialogs: bool
        :param only_with_tags: Executes only actors with the given tag, any other actor is going to get skipped.
        :type only_with_tags: List[str]

        """
        context = context or str(uuid.uuid4())
        os.environ['LEAPP_EXECUTION_ID'] = context
        if not os.environ.get('LEAPP_HOSTNAME', None):
            os.environ['LEAPP_HOSTNAME'] = socket.getfqdn()

        self.log.info('Starting workflow execution: {name} - ID: {id}'.format(
            name=self.name, id=os.environ['LEAPP_EXECUTION_ID']))

        skip_phases_until = (skip_phases_until or '').lower()
        needle_phase = until_phase or ''
        needle_stage = None
        if '.' in needle_phase:
            needle_phase, needle_stage = needle_phase.split('.', 1)
        needle_phase = needle_phase.lower()
        needle_stage = (needle_stage or '').lower()
        needle_actor = (until_actor or '').lower()

        self._errors = get_errors(context)
        config_model = type(self).configuration

        for phase in skip_phases_until, needle_phase:
            if phase and not self.is_valid_phase(phase):
                raise CommandError(
                    'Phase {phase} does not exist in the workflow'.format(
                        phase=phase))

        self._stop_after_phase_requested = False
        for phase in self._phase_actors:
            os.environ['LEAPP_CURRENT_PHASE'] = phase[0].name
            if skip_phases_until:
                if skip_phases_until in phase_names(phase):
                    skip_phases_until = ''
                self.log.info(
                    'Skipping phase {name}'.format(name=phase[0].name))
                continue

            display_status_current_phase(phase)
            self.log.info('Starting phase {name}'.format(name=phase[0].name))
            current_logger = self.log.getChild(phase[0].name)

            early_finish = False
            for stage in phase[1:]:
                if early_finish:
                    return
                current_logger.info(
                    "Starting stage {stage} of phase {phase}".format(
                        phase=phase[0].name, stage=stage.stage))
                for actor in stage.actors:
                    if early_finish:
                        return
                    designation = ''
                    if ExperimentalTag in actor.tags:
                        designation = '[EXPERIMENTAL]'
                        if actor not in self.experimental_whitelist:
                            current_logger.info(
                                "Skipping experimental actor {actor}".format(
                                    actor=actor.name))
                            continue

                    if only_with_tags and not contains_tag(
                            only_with_tags, actor.tags):
                        current_logger.info(
                            "Actor {actor} does not contain any required tag. Skipping."
                            .format(actor=actor.name))
                        continue

                    display_status_current_actor(actor,
                                                 designation=designation)
                    current_logger.info(
                        "Executing actor {actor} {designation}".format(
                            designation=designation, actor=actor.name))
                    messaging = InProcessMessaging(
                        config_model=config_model,
                        answer_store=self._answer_store)
                    messaging.load(actor.consumes)
                    instance = actor(logger=current_logger,
                                     messaging=messaging,
                                     config_model=config_model,
                                     skip_dialogs=skip_dialogs)
                    try:
                        instance.run()
                    except BaseException:
                        self._unhandled_exception = True
                        raise

                    self._stop_after_phase_requested = messaging.stop_after_phase or self._stop_after_phase_requested

                    # Collect dialogs
                    self._dialogs.extend(messaging.dialogs())
                    # Collect errors
                    if messaging.errors():
                        self._errors.extend(messaging.errors())

                        if phase[
                                0].policies.error is Policies.Errors.FailImmediately:
                            self.log.info(
                                'Workflow interrupted due to FailImmediately error policy'
                            )
                            early_finish = True
                            break

                    for command in messaging.commands:
                        if command[
                                'command'] == SkipPhasesUntilCommand.COMMAND:
                            skip_phases_until = command['arguments'][
                                'until_phase']
                            self.log.info(
                                'SkipPhasesUntilCommand received. Skipping phases until {}'
                                .format(skip_phases_until))

                    checkpoint(actor=actor.name,
                               phase=phase[0].name,
                               context=context,
                               hostname=os.environ['LEAPP_HOSTNAME'])
                    if needle_actor in actor_names(actor):
                        self.log.info(
                            'Workflow finished due to the until-actor flag')
                        early_finish = True
                        break
                if not stage.actors:
                    checkpoint(actor='',
                               phase=phase[0].name + '.' + stage.stage,
                               context=context,
                               hostname=os.environ['LEAPP_HOSTNAME'])

                if needle_phase in phase_names(
                        phase) and needle_stage == stage.stage.lower():
                    self.log.info(
                        'Workflow finished due to the until-phase flag')
                    early_finish = True
                    break

            checkpoint(actor='',
                       phase=phase[0].name,
                       context=context,
                       hostname=os.environ['LEAPP_HOSTNAME'])

            if self._errors and phase[
                    0].policies.error is Policies.Errors.FailPhase:
                self.log.info(
                    'Workflow interrupted due to the FailPhase error policy')
                early_finish = True

            elif needle_phase in phase_names(phase):
                self.log.info('Workflow finished due to the until-phase flag')
                early_finish = True

            elif self._stop_after_phase_requested:
                self.log.info('Workflow received request to stop after phase.')
                early_finish = True

            elif phase[0].flags.request_restart_after_phase or phase[
                    0].flags.restart_after_phase:
                reboot = True
                if phase[
                        0].flags.request_restart_after_phase and not self._auto_reboot:
                    reboot = False
                    messaging.request_answers(
                        RawMessageDialog(
                            message=
                            'A reboot is required to continue. Please reboot your system.'
                        ))
                if reboot:
                    self.log.info(
                        'Initiating system reboot due to the restart_after_reboot flag'
                    )
                    reboot_system()
                early_finish = True

            elif phase[0].flags.is_checkpoint:
                self.log.info(
                    'Stopping the workflow execution due to the is_checkpoint flag'
                )
                early_finish = True

            if early_finish:
                return
예제 #3
0
    def run(self, context=None, until_phase=None, until_actor=None, skip_phases_until=None):
        """
        Executes the workflow

        :param context: Custom execution ID to be used instead of a randomly generated UUIDv4
        :type context: str
        :param until_phase: Specify until including which phase the execution should run - phase.stage can be used to
                            control it even more granularly. `phase` is any phase name where `stage` refers to `main`,
                            `before` or `after`. If no stage is defined, `after` is assumed to be the default value.
                            The execution ends when this phase (and stage, if specified) has been executed.
        :type until_phase: str
        :param until_actor: The execution finishes when this actor has been executed.
        :type until_actor: str
        :param skip_phases_until: Skips all phases until including the phase specified, and then continues the
               execution.
        :type skip_phases_until: str or None

        """
        context = context or str(uuid.uuid4())
        os.environ['LEAPP_EXECUTION_ID'] = context
        if not os.environ.get('LEAPP_HOSTNAME', None):
            os.environ['LEAPP_HOSTNAME'] = socket.getfqdn()

        self.log.info('Starting workflow execution: {name} - ID: {id}'.format(
            name=self.name, id=os.environ['LEAPP_EXECUTION_ID']))

        skip_phases_until = (skip_phases_until or '').lower()
        needle_phase = until_phase or ''
        needle_stage = None
        if '.' in needle_phase:
            needle_phase, needle_stage = needle_phase.split('.', 1)
        needle_phase = needle_phase.lower()
        needle_stage = (needle_stage or '').lower()
        needle_actor = (until_actor or '').lower()

        self._errors = get_errors(context)

        for phase in self._phase_actors:
            os.environ['LEAPP_CURRENT_PHASE'] = phase[0].name

            if skip_phases_until:
                if skip_phases_until in (phase[0].__name__.lower(), phase[0].name.lower()):
                    skip_phases_until = ''
                self.log.info('Skipping phase {name}'.format(name=phase[0].name))
                continue

            self.log.info('Starting phase {name}'.format(name=phase[0].name))
            current_logger = self.log.getChild(phase[0].name)

            for stage in phase[1:]:
                current_logger.info("Starting stage {stage} of phase {phase}".format(
                    phase=phase[0].name, stage=stage.stage))
                for actor in stage.actors:
                    designation = ''
                    if ExperimentalTag in actor.tags:
                        designation = '[EXPERIMENTAL]'
                        if actor not in self.experimental_whitelist:
                            current_logger.info("Skipping experimental actor {actor}".format(actor=actor.name))
                            continue
                    current_logger.info("Executing actor {actor} {designation}".format(designation=designation,
                                                                                       actor=actor.name))
                    messaging = InProcessMessaging()
                    messaging.load(actor.consumes)
                    actor(logger=current_logger, messaging=messaging).run()

                    # Collect errors
                    if messaging.errors():
                        self._errors.extend(messaging.errors())

                        if phase[0].policies.error is Policies.Errors.FailImmediately:
                            self.log.info('Workflow interrupted due to FailImmediately error policy')
                            return

                    checkpoint(actor=actor.name, phase=phase[0].name, context=context,
                               hostname=os.environ['LEAPP_HOSTNAME'])
                    if needle_actor in (actor.name.lower(), actor.class_name.lower()):
                        self.log.info('Workflow finished due to the until-actor flag')
                        return
                if not stage.actors:
                    checkpoint(actor='', phase=phase[0].name + '.' + stage.stage, context=context,
                               hostname=os.environ['LEAPP_HOSTNAME'])

                if needle_phase in (phase[0].__name__.lower(), phase[0].name.lower()) and \
                        needle_stage == stage.stage.lower():
                    self.log.info('Workflow finished due to the until-phase flag')
                    return

            checkpoint(actor='', phase=phase[0].name, context=context, hostname=os.environ['LEAPP_HOSTNAME'])

            if self._errors and phase[0].policies.error is Policies.Errors.FailPhase:
                self.log.info('Workflow interrupted due to the FailPhase error policy')
                return

            if needle_phase in (phase[0].__name__.lower(), phase[0].name.lower()):
                self.log.info('Workflow finished due to the until-phase flag')
                return

            if phase[0].flags.request_restart_after_phase or phase[0].flags.restart_after_phase:
                reboot = True
                if phase[0].flags.request_restart_after_phase and not self._auto_reboot:
                    reboot = False
                    messaging.request_answers(
                        RawMessageDialog(message='A reboot is required to continue. Please reboot your system.')
                    )
                if reboot:
                    self.log.info('Initiating system reboot due to the restart_after_reboot flag')
                    reboot_system()
                return