Esempio n. 1
0
def cli(args):
    log = configure_logger()
    basedir = find_repository_basedir('.')
    repository = find_and_scan_repositories(basedir, include_locals=True)
    try:
        repository.load()
    except LeappError as exc:
        sys.stderr.write(exc.message)
        sys.stderr.write('\n')
        sys.exit(1)
    actor_logger = log.getChild('actors')
    actor = repository.lookup_actor(args.actor_name)
    if not actor:
        raise CommandError('Actor "{}" not found!'.format(args.actor_name))
    messaging = InProcessMessaging(stored=args.save_output)
    messaging.load(actor.consumes)

    failure = False
    with beautify_actor_exception():
        try:
            actor(messaging=messaging, logger=actor_logger).run()
        except BaseException:
            failure = True
            raise

    report_errors(messaging.errors())

    if failure or messaging.errors():
        sys.exit(1)

    if args.print_output:
        json.dump(messaging.messages(), sys.stdout, indent=2)
        sys.stdout.write('\n')
Esempio n. 2
0
def test_deprecations(repository):
    start = datetime.datetime.utcnow()
    actor = repository.lookup_actor('DeprecationTests')
    messaging = InProcessMessaging()
    os.environ['LEAPP_EXECUTION_ID'] = str(uuid.uuid4())
    with py.path.local(actor.directory).as_cwd():
        actor(messaging=messaging).run()
    entries = leapp.utils.audit.get_audit_entry('deprecation', os.getenv('LEAPP_EXECUTION_ID'))
    assert entries
    entries = [entry for entry in entries
               if datetime.datetime.strptime(entry['stamp'].rstrip('Z'), '%Y-%m-%dT%H:%M:%S.%f') > start]
    assert entries and len(entries) == 5
Esempio n. 3
0
def test_report_error(repository_dir):
    with repository_dir.as_cwd():
        msg = InProcessMessaging()
        msg.report_error('Some error',
                         ErrorSeverity.ERROR,
                         FakeActor(),
                         details=None)
        msg.report_error('Some error with details',
                         ErrorSeverity.ERROR,
                         FakeActor(),
                         details={'foo': 'bar'})
        assert len(msg.errors()) == 2
Esempio n. 4
0
def test_messaging_messages(repository_dir, stored):
    with repository_dir.as_cwd():
        msg = InProcessMessaging(stored=stored)
        v = UnitTestModel()
        msg.produce(v, FakeActor())
        consumed = tuple(msg.consume(FakeActor(), UnitTestModel))
        assert len(consumed) == 1
        assert len(msg.messages()) == 1
        assert consumed[0] == v
Esempio n. 5
0
def cli(args):
    start = datetime.datetime.utcnow()
    log = configure_logger()
    basedir = find_repository_basedir('.')
    repository = find_and_scan_repositories(basedir, include_locals=True)
    try:
        repository.load()
    except LeappError as exc:
        sys.stderr.write(exc.message)
        sys.stderr.write('\n')
        sys.exit(1)
    actor_logger = log.getChild('actors')
    actor = repository.lookup_actor(args.actor_name)
    if not actor:
        raise CommandError('Actor "{}" not found!'.format(args.actor_name))
    config_model = getattr(import_module('leapp.models'),
                           args.actor_config) if args.actor_config else None
    messaging = InProcessMessaging(stored=args.save_output,
                                   config_model=config_model)
    messaging.load(actor.consumes)

    failure = False
    with beautify_actor_exception():
        try:
            actor(messaging=messaging,
                  logger=actor_logger,
                  config_model=config_model).run()
        except BaseException:
            failure = True
            raise

    report_errors(messaging.errors())
    report_deprecations(os.getenv('LEAPP_EXECUTION_ID'), start=start)

    if failure or messaging.errors():
        sys.exit(1)

    if args.print_output:
        json.dump(messaging.messages(), sys.stdout, indent=2)
        sys.stdout.write('\n')
Esempio n. 6
0
def cli(args):
    log = configure_logger()
    basedir = find_repository_basedir('.')
    repository = find_and_scan_repositories(basedir, include_locals=True)
    try:
        repository.load()
    except LeappError as exc:
        sys.stderr.write(exc.message)
        sys.exit(1)
    actor_logger = log.getChild('actors')
    actor = repository.lookup_actor(args.actor_name)
    messaging = InProcessMessaging(stored=args.save_output)
    messaging.load(actor.consumes)

    actor(messaging=messaging, logger=actor_logger).run()

    report_errors(messaging.errors())

    if args.print_output:
        json.dump(messaging.messages(), sys.stdout, indent=2)
        sys.stdout.write('\n')
Esempio n. 7
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
Esempio n. 8
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
Esempio n. 9
0
 def __init__(self, actor=None):
     self._actor = actor
     self._messaging = InProcessMessaging()
Esempio n. 10
0
class ActorContext(object):
    """
    ActorContext is a helper class for testing actors. It helps to eliminate the boilerplate for executing
    actors. It provides a set of methods that allow specifying input messages for the actor, executing the
    actor and to retrieve messages sent by the actor.
    """
    apis = ()

    def __init__(self, actor=None):
        self._actor = actor
        self._messaging = InProcessMessaging()

    def set_actor(self, actor):
        """
        Internally used method to set the current actor specification object to setup the current actor for the
        test function.

        :param actor: ActorSpecification instance to use.
        :return: None
        """
        type(self).name = actor.name + '_feeder'
        # Consumes is here what it actually produces because we want to consume produced messages in 'consume'
        type(self).consumes = get_api_models(actor, 'produces')
        self._actor = actor

    def feed(self, *models):
        """
        Feed the messaging model with messages to be available to consume.

        :param models: Data in form of model instances to be available for the actor to consume.
        :type models: Variable number of instances of classes derived from :py:class:`leapp.models.Model`

        :return: None
        """
        for model in models:
            self._messaging.feed(model, self)

    def run(self, config_model=None):
        """
        Execute the current actor.

        :param config_model: Config model for the actor to consume.
        :type config_model: Config model instance derived from :py:class:`leapp.models.Model`
        :return: None
        """
        config_model_cls = config_model.__class__ if config_model else None
        if config_model:
            self._messaging.feed(config_model, self)
            # we have to make messaging system aware of config model being used as this is normally done by workflow
            self._messaging._config_models = (config_model_cls, )
        # the same as above applies here for actor
        self._actor(messaging=self._messaging,
                    config_model=config_model_cls).run()

    def messages(self):
        """
        Returns raw messages produced by the actor.

        :return: list of raw message data dictionaries.
        """
        return self._messaging.messages()

    def consume(self, *models):
        """
        Retrieve messages produced by the actor execution and specified in the actors :py:attr:`produces`
        attribute, and filter message types by models.

        :param models: Models to use as a filter for the messages to return
        :type models: Variable number of the derived classes from :py:class:`leapp.models.Model`
        :return:
        """
        return tuple(self._messaging.consume(self, *models))
Esempio n. 11
0
def test_loading(repository_dir):
    with repository_dir.as_cwd():
        msg = InProcessMessaging()
        with pytest.raises(CannotConsumeErrorMessages):
            msg.load((ErrorModel, ))
        msg.load((UnitTestModel, ))
        v = UnitTestModel()
        consumed = tuple(msg.consume(FakeActor(), UnitTestModel))
        assert len(consumed) == 1
        assert len(msg.messages()) == 0
        assert consumed[0] == v

        consumed = tuple(msg.consume(FakeActor(), UnitTestModelUnused))
        assert len(consumed) == 0
        assert len(msg.messages()) == 0

        consumed = tuple(msg.consume(FakeActor()))
        assert len(consumed) == 1
        assert len(msg.messages()) == 0
        assert consumed[0] == v

        consumed = tuple(msg.consume(FakeActor(), UnitTestModelUnused))
        assert len(consumed) == 0
        assert len(msg.messages()) == 0