Beispiel #1
0
    def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):

        if not indent and (result.get('_ansible_verbose_always') or self._display.verbosity > 2):
            indent = 4

        # All result keys stating with _ansible_ are internal, so remove them from the result before we output anything.
        abridged_result = strip_internal_keys(module_response_deepcopy(result))

        # remove invocation unless specifically wanting it
        if not keep_invocation and self._display.verbosity < 3 and 'invocation' in result:
            del abridged_result['invocation']

        # remove diff information from screen output
        if self._display.verbosity < 3 and 'diff' in result:
            del abridged_result['diff']

        # remove exception from screen output
        if 'exception' in abridged_result:
            del abridged_result['exception']

        try:
            jsonified_results = json.dumps(abridged_result, cls=AnsibleJSONEncoder, indent=indent, ensure_ascii=False, sort_keys=sort_keys)
        except TypeError:
            # Python3 bug: throws an exception when keys are non-homogenous types:
            # https://bugs.python.org/issue25457
            # sort into an OrderedDict and then json.dumps() that instead
            if not OrderedDict:
                raise
            jsonified_results = json.dumps(OrderedDict(sorted(abridged_result.items(), key=to_text)),
                                           cls=AnsibleJSONEncoder, indent=indent,
                                           ensure_ascii=False, sort_keys=False)
        return jsonified_results
Beispiel #2
0
    def _load_result(self, result, status, **kwargs):
        """
        This method is called when an individual task instance on a single
        host completes. It is responsible for logging a single result to the
        database.
        """
        hostname = result._host.get_name()
        self.result_ended[hostname] = datetime.datetime.now(
            datetime.timezone.utc).isoformat()

        # Retrieve the host so we can associate the result to the host id
        host = self._get_or_create_host(hostname)

        results = strip_internal_keys(module_response_deepcopy(result._result))

        # Round-trip through JSON to sort keys and convert Ansible types
        # to standard types
        try:
            jsonified = json.dumps(results,
                                   cls=AnsibleJSONEncoder,
                                   ensure_ascii=False,
                                   sort_keys=True)
        except TypeError:
            # Python 3 can't sort non-homogenous keys.
            # https://bugs.python.org/issue25457
            jsonified = json.dumps(results,
                                   cls=AnsibleJSONEncoder,
                                   ensure_ascii=False,
                                   sort_keys=False)
        results = json.loads(jsonified)

        # Sanitize facts
        if "ansible_facts" in results:
            for fact in self.ignored_facts:
                if fact in results["ansible_facts"]:
                    self.log.debug("Ignoring fact: %s" % fact)
                    results["ansible_facts"][
                        fact] = "Not saved by ARA as configured by 'ignored_facts'"

        self.result = self.client.post(
            "/api/v1/results",
            playbook=self.playbook["id"],
            task=self.task["id"],
            host=host["id"],
            play=self.task["play"],
            content=results,
            status=status,
            started=self.result_started[hostname]
            if hostname in self.result_started else self.task["started"],
            ended=self.result_ended[hostname],
            changed=result._result.get("changed", False),
            # Note: ignore_errors might be None instead of a boolean
            ignore_errors=kwargs.get("ignore_errors", False) or False,
        )

        if self.task["action"] in ["setup", "gather_facts"
                                   ] and "ansible_facts" in results:
            self.client.patch("/api/v1/hosts/%s" % host["id"],
                              facts=results["ansible_facts"])
    def clean_copy(self):
        ''' returns 'clean' taskresult object '''

        # FIXME: clean task_fields, _task and _host copies
        result = TaskResult(self._host, self._task, {}, self._task_fields)

        # statuses are already reflected on the event type
        if result._task and result._task.action in ['debug']:
            # debug is verbose by default to display vars, no need to add invocation
            ignore = _IGNORE + ('invocation', )
        else:
            ignore = _IGNORE

        subset = {}
        # preserve subset for later
        for sub in _SUB_PRESERVE:
            if sub in self._result:
                subset[sub] = {}
                for key in _SUB_PRESERVE[sub]:
                    if key in self._result[sub]:
                        subset[sub][key] = self._result[sub][key]

        if isinstance(self._task.no_log,
                      bool) and self._task.no_log or self._result.get(
                          '_ansible_no_log', False):
            x = {
                "censored":
                "the output has been hidden due to the fact that 'no_log: true' was specified for this result"
            }

            # preserve full
            for preserve in _PRESERVE:
                if preserve in self._result:
                    x[preserve] = self._result[preserve]

            result._result = x
        elif self._result:
            result._result = module_response_deepcopy(self._result)

            # actualy remove
            for remove_key in ignore:
                if remove_key in result._result:
                    del result._result[remove_key]

            # remove almost ALL internal keys, keep ones relevant to callback
            strip_internal_keys(result._result, exceptions=CLEAN_EXCEPTIONS)

        # keep subset
        result._result.update(subset)

        return result
Beispiel #4
0
    def _dump_results(self,
                      result,
                      indent=None,
                      sort_keys=True,
                      keep_invocation=False,
                      serialize=True):
        try:
            result_format = self.get_option('result_format')
        except KeyError:
            # Callback does not declare result_format nor extend result_format_callback
            result_format = 'json'

        try:
            pretty_results = self.get_option('pretty_results')
        except KeyError:
            # Callback does not declare pretty_results nor extend result_format_callback
            pretty_results = None

        indent_conditions = (
            result.get('_ansible_verbose_always'),
            pretty_results is None and result_format != 'json',
            pretty_results is True,
            self._display.verbosity > 2,
        )

        if not indent and any(indent_conditions):
            indent = 4
        if pretty_results is False:
            # pretty_results=False overrides any specified indentation
            indent = None

        # All result keys stating with _ansible_ are internal, so remove them from the result before we output anything.
        abridged_result = strip_internal_keys(module_response_deepcopy(result))

        # remove invocation unless specifically wanting it
        if not keep_invocation and self._display.verbosity < 3 and 'invocation' in result:
            del abridged_result['invocation']

        # remove diff information from screen output
        if self._display.verbosity < 3 and 'diff' in result:
            del abridged_result['diff']

        # remove exception from screen output
        if 'exception' in abridged_result:
            del abridged_result['exception']

        if not serialize:
            # Just return ``abridged_result`` without going through serialization
            # to permit callbacks to take advantage of ``_dump_results``
            # that want to further modify the result, or use custom serialization
            return abridged_result

        if result_format == 'json':
            try:
                return json.dumps(abridged_result,
                                  cls=AnsibleJSONEncoder,
                                  indent=indent,
                                  ensure_ascii=False,
                                  sort_keys=sort_keys)
            except TypeError:
                # Python3 bug: throws an exception when keys are non-homogenous types:
                # https://bugs.python.org/issue25457
                # sort into an OrderedDict and then json.dumps() that instead
                if not OrderedDict:
                    raise
                return json.dumps(OrderedDict(
                    sorted(abridged_result.items(), key=to_text)),
                                  cls=AnsibleJSONEncoder,
                                  indent=indent,
                                  ensure_ascii=False,
                                  sort_keys=False)
        elif result_format == 'yaml':
            # None is a sentinel in this case that indicates default behavior
            # default behavior for yaml is to prettify results
            lossy = pretty_results in (None, True)
            if lossy:
                # if we already have stdout, we don't need stdout_lines
                if 'stdout' in abridged_result and 'stdout_lines' in abridged_result:
                    abridged_result['stdout_lines'] = '<omitted>'

                # if we already have stderr, we don't need stderr_lines
                if 'stderr' in abridged_result and 'stderr_lines' in abridged_result:
                    abridged_result['stderr_lines'] = '<omitted>'

            return '\n%s' % textwrap.indent(
                yaml.dump(
                    abridged_result,
                    allow_unicode=True,
                    Dumper=_AnsibleCallbackDumper(lossy=lossy),
                    default_flow_style=False,
                    indent=indent,
                    # sort_keys=sort_keys  # This requires PyYAML>=5.1
                ),
                ' ' * (indent or 4))
Beispiel #5
0
def test_module_response_deepcopy_dict():
    x = {"foo": [1, 2], "bar": 3}
    y = module_response_deepcopy(x)
    assert y == x
    assert x is not y
    assert x["foo"] is not y["foo"]
Beispiel #6
0
def test_module_response_deepcopy_tuple_of_immutables():
    x = ((1, 2), 3)
    y = module_response_deepcopy(x)
    assert x is y
Beispiel #7
0
def test_module_response_deepcopy_tuple():
    x = ([1, 2], 3)
    y = module_response_deepcopy(x)
    assert y == x
    assert x is not y
    assert x[0] is not y[0]
Beispiel #8
0
def test_module_response_deepcopy_empty_tuple():
    x = ()
    y = module_response_deepcopy(x)
    assert x is y
Beispiel #9
0
def test_module_response_deepcopy_list():
    x = [[1, 2], 3]
    y = module_response_deepcopy(x)
    assert y == x
    assert x is not y
    assert x[0] is not y[0]
Beispiel #10
0
def test_module_response_deepcopy_atomic():
    tests = [None, 42, 2**100, 3.14, True, False, 1j,
             "hello", "hello\u1234"]
    for x in tests:
        assert module_response_deepcopy(x) is x
Beispiel #11
0
def test_module_response_deepcopy_basic():
    x = 42
    y = module_response_deepcopy(x)
    assert y == x
Beispiel #12
0
    def _load_result(self, result, status, **kwargs):
        """
        This method is called when an individual task instance on a single
        host completes. It is responsible for logging a single result to the
        database.
        """
        hostname = result._host.get_name()
        self.result_ended[hostname] = datetime.datetime.now(
            datetime.timezone.utc).isoformat()

        # Retrieve the host so we can associate the result to the host id
        host = self._get_or_create_host(hostname)

        # If the task was delegated to another host, retrieve that too.
        # Since a single task can be delegated to multiple hosts (ex: looping on a host group and using delegate_to)
        # this must be a list of hosts.
        delegated_to = []
        # The value of result._task.delegate_to doesn't get templated if the task was skipped
        # https://github.com/ansible/ansible/issues/75339#issuecomment-888724838
        if result._task.delegate_to and status != "skipped":
            task_uuid = str(result._task._uuid[:36])
            if task_uuid in self.delegation_cache:
                for delegated in self.delegation_cache[task_uuid]:
                    delegated_to.append(self._get_or_create_host(delegated))
            else:
                delegated_to.append(
                    self._get_or_create_host(result._task.delegate_to))

        # Retrieve the task so we can associate the result to the task id
        task = self._get_or_create_task(result._task)

        results = strip_internal_keys(module_response_deepcopy(result._result))

        # Round-trip through JSON to sort keys and convert Ansible types
        # to standard types
        try:
            jsonified = json.dumps(results,
                                   cls=AnsibleJSONEncoder,
                                   ensure_ascii=False,
                                   sort_keys=True)
        except TypeError:
            # Python 3 can't sort non-homogenous keys.
            # https://bugs.python.org/issue25457
            jsonified = json.dumps(results,
                                   cls=AnsibleJSONEncoder,
                                   ensure_ascii=False,
                                   sort_keys=False)
        results = json.loads(jsonified)

        # Sanitize facts
        if "ansible_facts" in results:
            for fact in self.ignored_facts:
                if fact in results["ansible_facts"]:
                    self.log.debug("Ignoring fact: %s" % fact)
                    results["ansible_facts"][
                        fact] = "Not saved by ARA as configured by 'ignored_facts'"

        self.result = self.client.post(
            "/api/v1/results",
            playbook=self.playbook["id"],
            task=task["id"],
            host=host["id"],
            delegated_to=[h["id"] for h in delegated_to],
            play=task["play"],
            content=results,
            status=status,
            started=self.result_started[hostname]
            if hostname in self.result_started else task["started"],
            ended=self.result_ended[hostname],
            changed=result._result.get("changed", False),
            # Note: ignore_errors might be None instead of a boolean
            ignore_errors=kwargs.get("ignore_errors", False) or False,
        )

        if task["action"] in ["setup", "gather_facts"
                              ] and "ansible_facts" in results:
            self.client.patch("/api/v1/hosts/%s" % host["id"],
                              facts=results["ansible_facts"])