ansible / ansible-runner

A tool and python library that helps when interfacing with Ansible directly or as part of another system whether that be through a container image interface, as a standalone tool, or as a Python module that can be imported. The goal is to provide a stable and consistent interface abstraction to Ansible.
Other
957 stars 352 forks source link

is there away to control ansible stdout output? #1389

Open braindevices opened 1 month ago

braindevices commented 1 month ago

we normally use the yaml or the diy callback for stdout, this make the large failure message far more readable on the fly. The json mode put the output the dict on single line, which is hard to read when the dict contains multiline strings

for example:

fatal: [localhost]: FAILED! => {"changed": false, "cmd": "set -e\nset -x\necho hello\nexit 1\n", "delta": "0:00:00.002386", "end": "2024-08-14 22:34:33.969828", "msg": "non-zero return code", "rc": 1, "start": "2024-08-14 22:34:33.967442", "stderr": "+ echo hello\n+ exit 1", "stderr_lines": ["+ echo hello", "+ exit 1"], "stdout": "hello", "stdout_lines": ["hello"]}

however, according to this https://github.com/ansible/ansible-runner/issues/212, it prevent us to change the stdout_callback

I wonder is there a way to tune the stdout in the way we wanted with ansible-runner?

I try to use event_handler but it won't sync with the playbook screen output

def handle_event(event):
    if event['event'] == "runner_on_failed" and 'event_data' in event:
        event_data = event['event_data']
        keys = ["task_path", 'res']
        data = {k:event_data[k] for k in keys if k in event_data}
        if data:
            print(yaml.safe_dump(data, default_flow_style=False))

The print out can happen any time not synced with the normal output

braindevices commented 1 month ago

I found a very ugly way to achieve this but I do not know if this is stable or just a side effect:

def my_stdout(stdout: str, res: dict):
    remove_large_res = re.compile(r"( => )\{.*\}")
    neg_keys = [
        'stdout_lines', 'stderr_lines',
        'start', 'end', 'delta',
        'invocation',
        # '_ansible_no_log',
        # "_ansible_item_label"
    ]
    data = {k:v for k, v in res.items() if k not in neg_keys and not k.startswith('_')}
    _dump = r'\1\n' + yaml.safe_dump(data)
    return remove_large_res.sub(_dump, stdout)

def handle_event(event):
    # Format event as YAML
    if event['event'].endswith("_failed") and 'event_data' in event:
        event_data = event['event_data']
        host = event_data["host"]
        # remove stderrlines and stderrouts
        if 'res' in event_data:
            keys_to_remove = ['stdout_lines', 'stderr_lines']
            for k in keys_to_remove:
                if k in event_data['res']:
                    event_data['res'].pop(k)

            event['stdout'] =my_stdout(event['stdout'], event_data['res'])
    return True

this depends on the fact that handle_event() can actually modify the event, is this a stable feature?