示例#1
0
 def __init__(self, stored=True):
     self._manager = multiprocessing.Manager()
     self._dialog_renderer = CommandlineRenderer()
     self._data = self._manager.list()
     self._answers = AnswerStore(manager=self._manager)
     self._new_data = self._manager.list()
     self._errors = self._manager.list()
     self._stored = stored
示例#2
0
 def __init__(self, stored=True, config_model=None):
     self._manager = multiprocessing.Manager()
     self._dialog_renderer = CommandlineRenderer()
     self._data = self._manager.list()
     self._answers = AnswerStore(manager=self._manager)
     self._new_data = self._manager.list()
     self._errors = self._manager.list()
     self._stored = stored
     self._config_models = (config_model, ) if config_model else ()
示例#3
0
def test_answerstore_load_and_translate_for_workflow(answerfile):
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load_and_translate_for_workflow(answerfile, MockWorkflow)
    assert store['boolscope']['key1'] is True
    assert store['boolscope']['key2'] is False
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
示例#4
0
def answer(args):
    """A command to record user choices to the questions in the answerfile.
       Saves user answer between leapp preupgrade runs.
    """
    cfg = get_config()
    if args.section:
        args.section = list(
            itertools.chain(*[i.split(',') for i in args.section]))
    else:
        raise UsageError(
            'At least one dialog section must be specified, ex. --section dialog.option=mychoice'
        )
    try:
        sections = [
            tuple((dialog_option.split('.', 2) + [value])) for dialog_option,
            value in [s.split('=', 2) for s in args.section]
        ]
    except ValueError:
        raise UsageError(
            "A bad formatted section has been passed. Expected format is dialog.option=mychoice"
        )
    answerfile_path = cfg.get('report', 'answerfile')
    answerstore = AnswerStore()
    answerstore.load(answerfile_path)
    for dialog, option, value in sections:
        answerstore.answer(dialog, option, value)
    not_updated = answerstore.update(answerfile_path, allow_missing=args.add)
    if not_updated:
        sys.stderr.write(
            "WARNING: Only sections found in original userfile can be updated, ignoring {}\n"
            .format(",".join(not_updated)))
示例#5
0
def test_answerstore_load(answerfile):
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load(answerfile)
    assert 'scope1' in store.keys()
    assert 'scope2' in store.keys()
    assert 'boolscope' in store.keys()
    assert 'key1' in store['scope1'].keys()
    assert 'key1' in store['scope1'].keys()
    assert 'key1' in store['scope2']
    assert 'key2' in store['scope2']
    assert 'key1' in store['boolscope']
    assert 'key2' in store['boolscope']
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
    assert store['boolscope']['key1'] == 'True'
    assert store['boolscope']['key2'] == 'False'
示例#6
0
 def __init__(self, stored=True, config_model=None, answer_store=None):
     self._manager = multiprocessing.Manager()
     self._dialog_renderer = CommandlineRenderer()
     self._data = self._manager.list()
     self._answers = answer_store or AnswerStore(manager=self._manager)
     self._new_data = self._manager.list()
     self._commands = self._manager.list()
     self._errors = self._manager.list()
     self._stored = stored
     self._config_models = (config_model, ) if config_model else ()
     self._dialogs = self._manager.list()
     self._stop_after_phase = self._manager.Value(bool, False)
示例#7
0
def test_answerfile_get(monkeypatch, answerfile):
    entries_created = []

    def mocked_create_audit_entry(event, data, message=None):
        entries_created.append((event, data, message))

    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load(answerfile)
    monkeypatch.setattr(leapp.messaging.answerstore, 'create_audit_entry',
                        mocked_create_audit_entry)

    # Testing boolscope
    assert store['boolscope']['key1'] == 'True'
    assert store['boolscope']['key2'] == 'False'
    result = a.get(scope='boolscope', fallback='boolscope')
    assert result != 'boolscope'
    assert result['key1'] == 'True'
    assert result['key2'] == 'False'
    assert entries_created
    assert entries_created[0][1]['scope'] == 'boolscope'
    assert entries_created[0][1]['fallback'] == 'boolscope'
    assert entries_created[0][1]['answer']['key1'] == 'True'
    assert entries_created[0][1]['answer']['key2'] == 'False'
    entries_created.pop()

    # Testing scope1
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    result = a.get(scope='scope1', fallback='scope1')
    assert result
    assert result['key1'] == 'scope1.key1'
    assert result['key2'] == 'scope1.key2'
    assert entries_created
    assert entries_created[0][1]['scope'] == 'scope1'
    assert entries_created[0][1]['fallback'] == 'scope1'
    assert entries_created[0][1]['answer']['key1'] == 'scope1.key1'
    assert entries_created[0][1]['answer']['key2'] == 'scope1.key2'
    entries_created.pop()

    # Testing scope2
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'

    result = a.get(scope='scope2', fallback='scope2')
    assert result
    assert result['key1'] == 'scope2.key1'
    assert result['key2'] == 'scope2.key2'
    assert entries_created
    assert entries_created[0][1]['scope'] == 'scope2'
    assert entries_created[0][1]['fallback'] == 'scope2'
    assert entries_created[0][1]['answer']['key1'] == 'scope2.key1'
    assert entries_created[0][1]['answer']['key2'] == 'scope2.key2'
    entries_created.pop()
示例#8
0
    def __init__(self, logger=None, auto_reboot=False):
        """
        :param logger: Optional logger to be used instead of leapp.workflow
        :type logger: Instance of :py:class:`logging.Logger`
        """
        self.log = (logger or logging.getLogger('leapp')).getChild('workflow')
        self._errors = []
        self._all_consumed = set()
        self._all_produced = set()
        self._initial = set()
        self._phase_actors = []
        self._experimental_whitelist = set()
        self._auto_reboot = auto_reboot
        self._unhandled_exception = False
        self._answer_store = AnswerStore()
        self._dialogs = []
        self._stop_after_phase_requested = False

        if self.configuration:
            config_actors = [
                actor for actor in self.tag.actors
                if self.configuration in actor.produces
            ]
            if config_actors:
                if len(config_actors) == 1:
                    self._phase_actors.append(
                        (_ConfigPhase, PhaseActors((), 'Before'),
                         PhaseActors(tuple(config_actors),
                                     'Main'), PhaseActors((), 'After')))
                else:
                    config_actor_names = [a.name for a in config_actors]
                    raise MultipleConfigActorsError(config_actor_names)
        self.description = self.description or type(self).__doc__

        for phase in self.phases:
            phase.filter.tags += (self.tag, )
            self._phase_actors.append((
                phase,
                # filters all actors with the give tags
                # phasetag .Before
                self._apply_phase(phase.filter.get_before(), 'Before'),
                # phasetag
                self._apply_phase(phase.filter.get(), 'Main'),
                # phasetag .After
                self._apply_phase(phase.filter.get_after(), 'After')))
示例#9
0
def test_answerfile_translate(answerfile):
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load(answerfile)
    assert store['boolscope']['key1'] == 'True'
    assert store['boolscope']['key2'] == 'False'
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
    a.translate(MockDialogBoolScope)
    assert store['boolscope']['key1'] is True
    assert store['boolscope']['key2'] is False
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
示例#10
0
def test_answerstore_answer():
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.answer(scope='scope1', key='key1', value='scope1.key1')
    a.answer(scope='scope1', key='key2', value='scope1.key2')
    a.answer(scope='scope2', key='key1', value='scope2.key1')
    a.answer(scope='scope2', key='key2', value='scope2.key2')
    assert 'scope1' in store.keys()
    assert 'scope2' in store.keys()
    assert 'key1' in store['scope1'].keys()
    assert 'key1' in store['scope1'].keys()
    assert 'key1' in store['scope2']
    assert 'key2' in store['scope2']
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
示例#11
0
class BaseMessaging(object):
    """
    BaseMessaging is the Base class for all messaging implementations. It provides the basic interface that is
    supported within the framework. These are called the `produce` and `consume` methods.
    """
    def __init__(self, stored=True):
        self._manager = multiprocessing.Manager()
        self._dialog_renderer = CommandlineRenderer()
        self._data = self._manager.list()
        self._answers = AnswerStore(manager=self._manager)
        self._new_data = self._manager.list()
        self._errors = self._manager.list()
        self._stored = stored

    def load_answers(self, answer_file, workflow):
        """
        Loads answers from a given answer file

        :param answer_file: Path to file to load as answer file
        :type answer_file: str
        :param workflow: :py:class:`leapp.workflows.Workflow` instance to load the answers for.
        :type workflow: :py:class:`leapp.workflows.Workflow`
        :return: None
        """
        self._answers.load_and_translate_for_workflow(answer_file, workflow)

    @property
    def stored(self):
        """
        :return: If the messages are stored immediately, this function returns True, otherwise False.
        """
        return self._stored

    def errors(self):
        """
        Gets all produced errors.
        :return: List of newly produced errors
        """
        return list(self._errors)

    def messages(self):
        """
        Gets all newly produced messages.
        :return: List of newly processed messages
        """
        return list(self._new_data)

    def _perform_load(self, consumes):
        """
        Loads all messages that are requested from the `consumes` attribute of :py:class:`leapp.actors.Actor`

        :param consumes: Tuple or list of :py:class:`leapp.models.Model` types to preload
        :return: None
        """
        raise NotImplementedError()

    def _process_message(self, message):
        """
        This method performs the actual message sending, which can be sent over the network or stored
        in a database.

        :param message: The message data to process
        :type message: dict
        :return: Pass through a message that might get updated through the sending process.
        """
        raise NotImplementedError()

    def load(self, consumes):
        """
        Loads all messages that are requested from the `consumes` attribute of :py:class:`leapp.actors.Actor`

        :param consumes: Tuple or list of :py:class:`leapp.models.Model` types to preload
        :return: None
        :raises leapp.exceptions.CannotConsumeErrorMessages: When trying to consume ErrorModel
        """
        if ErrorModel in consumes:
            raise CannotConsumeErrorMessages()
        self._perform_load(consumes)

    def report_error(self, message, severity, actor, details):
        """
        Reports an execution error

        :param message: Message to print the error
        :type message: str
        :param severity: Severity of the error
        :type severity: ErrorSeverity
        :param actor: Actor name that produced the message
        :type actor: leapp.actors.Actor
        :param details: A dictionary where additional context information can be passed along with the error
        :type details: dict
        :return: None
        """
        if details:
            details = json.dumps(details)
        model = ErrorModel(message=message,
                           actor=actor.name,
                           severity=severity,
                           details=details,
                           time=datetime.datetime.utcnow())
        self._do_produce(model, actor, self._errors)

    def produce(self, model, actor):
        """
        Called to send a message available for other actors.

        :param model: Model to send as message payload
        :type model: :py:class:`leapp.models.Model`
        :param actor: Actor that sends the message
        :type actor: :py:class:`leapp.actors.Actor`
        :return: the updated message dict
        :rtype: dict
        """
        return self._do_produce(model, actor, self._new_data)

    def feed(self, model, actor):
        """
        Called to pre-fill sent messages and make them available for other actors.

        :param model: Model to send as message payload
        :type model: :py:class:`leapp.models.Model`
        :param actor: Actor that sends the message
        :type actor: :py:class:`leapp.actors.Actor`
        :return: the updated message dict
        :rtype: dict
        """
        return self._do_produce(model, actor, self._data, stored=False)

    def _do_produce(self, model, actor, target, stored=True):
        if not os.environ.get('LEAPP_HOSTNAME', None):
            os.environ['LEAPP_HOSTNAME'] = socket.getfqdn()
        data = json.dumps(model.dump(), sort_keys=True)
        message = {
            'type':
            type(model).__name__,
            'actor':
            type(actor).name,
            'topic':
            model.topic.name,
            'stamp':
            datetime.datetime.utcnow().isoformat() + 'Z',
            'phase':
            os.environ.get('LEAPP_CURRENT_PHASE', 'NON-WORKFLOW-EXECUTION'),
            'context':
            os.environ.get('LEAPP_EXECUTION_ID', 'TESTING-CONTEXT'),
            'hostname':
            os.environ['LEAPP_HOSTNAME'],
            'message': {
                'data': data,
                'hash': hashlib.sha256(data.encode('utf-8')).hexdigest()
            }
        }

        if stored and self.stored:
            self._process_message(message.copy())

        target.append(message)
        return message

    def request_answers(self, dialog):
        return dialog.request_answers(self._answers, self._dialog_renderer)

    def consume(self, actor, *types):
        """
        Returns all consumable messages and filters them by `types`

        :param types: Variable number of :py:class:`leapp.models.Model` derived types to filter messages to be consumed
        :param actor: Actor that consumes the data
        :return: Iterable with messages matching the criteria
        """
        types = tuple((getattr(t, '_resolved', t) for t in types))
        messages = list(self._data) + list(self._new_data)
        lookup = dict([(model.__name__, model)
                       for model in type(actor).consumes])
        if types:
            filtered = set(requested.__name__ for requested in types)
            messages = [
                message for message in messages if message['type'] in filtered
            ]
        return (lookup[message['type']].create(
            json.loads(message['message']['data'])) for message in messages)
示例#12
0
def test_answerfile_generate(tmpdir, answerfile):
    f = tmpdir.join('test_answerfile_generate')
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load_and_translate_for_workflow(answerfile, MockWorkflow)
    assert store['scope1']['key1'] == 'scope1.key1'
    assert store['scope1']['key2'] == 'scope1.key2'
    assert store['scope2']['key1'] == 'scope2.key1'
    assert store['scope2']['key2'] == 'scope2.key2'
    assert store['boolscope']['key1'] is True
    assert store['boolscope']['key2'] is False
    a.answer('scope1', 'key1', 'generate.scope1.key1')
    a.answer('scope1', 'key2', 'generate.scope1.key2')
    a.answer('scope2', 'key1', 'generate.scope2.key1')
    a.answer('scope2', 'key2', 'generate.scope2.key2')
    a.answer('boolscope', 'key1', False)
    a.answer('boolscope', 'key2', True)
    a.generate(MockWorkflow.dialogs, str(f))
    a = AnswerStore(manager=m)
    store2 = a._storage
    a.load_and_translate_for_workflow(str(f), MockWorkflow)
    assert store2['scope1']['key1'] == 'generate.scope1.key1'
    assert store2['scope1']['key2'] == 'generate.scope1.key2'
    assert store2['scope2']['key1'] == 'generate.scope2.key1'
    assert store2['scope2']['key2'] == 'generate.scope2.key2'
    assert store2['boolscope']['key1'] is False
    assert store2['boolscope']['key2'] is True
示例#13
0
def test_answerstore_update(answerfile, answerfile_data, tmpdir):
    m = MockManager()
    a = AnswerStore(manager=m)
    store = a._storage
    a.load(answerfile)
    assert store['boolscope']['key1'] == 'True'
    assert store['boolscope']['key2'] == 'False'
    a.answer('boolscope', 'key1', False)
    a.answer('boolscope', 'key2', True)
    assert store['boolscope']['key1'] is False
    assert store['boolscope']['key2'] is True
    f = tmpdir.join('test_answerstore_update')
    f.write(answerfile_data)
    a.update(str(f))
    a2 = AnswerStore(manager=m)
    store2 = a2._storage
    a2.load(str(f))
    assert store2['boolscope']['key1'] == 'False'
    assert store2['boolscope']['key2'] == 'True'
    assert store2['scope1']['key1'] == 'scope1.key1'
    assert store2['scope1']['key2'] == 'scope1.key2'
    assert store2['scope2']['key1'] == 'scope2.key1'
    assert store2['scope2']['key2'] == 'scope2.key2'
示例#14
0
def test_answerstore_init(monkeypatch):
    manager = MockManager()
    assert type(AnswerStore()._storage) is multiprocessing.managers.DictProxy
    monkeypatch.setattr(multiprocessing, 'Manager', MockManager())
    assert type(AnswerStore(manager=manager)._storage) is dict
    assert type(AnswerStore()._storage) is dict