3

I'm having issues to get my command output when task timeout is reached.

Here is my task:

  - name: test of echo-ing something
    ansible.builtin.command:
      cmd: "/bin/echo 'it works !'"
    timeout: 10
    register: command_result
    ignore_errors: yes

  - name: command_result
    ansible.builtin.debug:
      var: command_result

When there is no timeout, everything goes well, I have in command_result variable:

ok: [server1] => {
    "command_result": {
        "changed": true,
        "cmd": [
            "/bin/echo",
            "it works !"
        ],
        "delta": "0:00:00.004629",
        "end": "2022-03-24 09:47:12.104234",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2022-03-24 09:47:12.099605",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "it works !",
        "stdout_lines": [
            "it works !"
        ]
    }
}

However, when I make my command triggering the timeout (for example: cmd: "/bin/sleep 40"), I have this :

TASK [test of echo-ing something] ***************************************
fatal: [server1]: FAILED! => {"changed": false, "msg": "The ansible.builtin.command action failed to execute in the expected time frame (10) and was terminated"}
...ignoring

TASK [command_result] ***************************************************
ok: [server1] => {
    "command_result": {
        "changed": false,
        "failed": true,
        "msg": "The ansible.builtin.command action failed to execute in the expected time frame (10) and was terminated"
    }
}

How to get standard output (stdout) and/or standard error (stderr) from the command when timeout is reached ? On my CI/CD, I need it to know why a command had reached the timeout

Thanks !

[EDIT] Regarding the answer given by @vladimir-botka I found another way to timeout command and not tasks. I'm pasting it here just in case it helps other people:

- name: "exec my command"
  ansible.builtin.shell:
    cmd: "timeout -v --foreground --signal=SIGINT 600 my_script.sh"
    chdir: "{{ work_dir }}"
    executable: /bin/bash
  register: cmd_output
- name: "command output"
  ansible.builtin.debug:
    var: cmd_output

2 Answers 2

4

Q: "How to get stdout and/or stderr from the command when timeout is reached ?"

A: When a timeout is reached the task will be terminated not the command. Task keyword timeout says:

Time limit for task to execute in, if exceeded Ansible will interrupt and fail the task.

As a result, you don't get stdout/stderr from the command when the task's timeout is reached.


Note

One might expect to get stdout/stderr from the terminated command. For example, the script

shell> cat sleep_10.sh
#!/bin/bash
echo PID $$
sleep 10
echo Finished
exit 0

Will print Terminated when killed by shell> kill SIGTERM 875125

shell> ./sleep_10.sh
PID 875125
Terminated

But when the script is executed by the Ansible module command

    - command: '$PWD/sleep_10.sh'
      timeout: 5
      register: result
      ignore_errors: true

    - debug:
        var: result

there is no stdout/stderr about the termination. Instead, the message says the task was terminated

  result:
    changed: false
    failed: true
    msg: The command action failed to execute in the expected time frame (5) and was terminated

Running the script asynchronously doesn't help either

    - name: Run an async task
      command: '$PWD/sleep_10.sh'
      async: 5
      poll: 0
      register: result

    - name: Check on an async task
      async_status:
        jid: "{{ result.ansible_job_id }}"
      register: job_result
      until: job_result.finished
      retries: 10
      delay: 2
      ignore_errors: true

    - debug:
        var: job_result

gives (abridged)

TASK [Run an async task] *******************************************
changed: [localhost]

TASK [Check on an async task] **************************************
FAILED - RETRYING: [localhost]: Check on an async task (10 retries left).
FAILED - RETRYING: [localhost]: Check on an async task (9 retries left).
FAILED - RETRYING: [localhost]: Check on an async task (8 retries left).
FAILED - RETRYING: [localhost]: Check on an async task (7 retries left).
FAILED - RETRYING: [localhost]: Check on an async task (6 retries left).
fatal: [localhost]: FAILED! => changed=false 
  ansible_job_id: '633379103837.876333'
  attempts: 6
  child_pid: 876338
  finished: 1
  msg: Timeout exceeded
  results_file: /home/admin/.ansible_async/633379103837.876333
  started: 1
  stderr: ''
  stderr_lines: <omitted>
  stdout: ''
  stdout_lines: <omitted>
...ignoring

TASK [debug] ****************************************************
ok: [localhost] => 
  job_result:
    ansible_job_id: '633379103837.876333'
    attempts: 6
    changed: false
    child_pid: 876338
    failed: true
    finished: 1
    msg: Timeout exceeded
    results_file: /home/admin/.ansible_async/633379103837.876333
    started: 1
    stderr: ''
    stderr_lines: []
    stdout: ''
    stdout_lines: []
Sign up to request clarification or add additional context in comments.

2 Comments

Many thanks for the explanation, I indeed misinterpreted the timeout action on tasks. However, it does not answer completely the question: how to get command stdout/stderr of that task after its timeout ?
You're welcome! The complete answer is: "It is not implemented". If you want to learn details see Ansible module architecture and Ansiballz framework. The code shows how the timeout is implemented ATM.
0

The best solution is already here, but for the sake of completeness I'll add another possibility.

There is also a possible workaround to redirect the command output to a temporary file and then print the file contents.

Example:

  - name: Create a temporary file to store script.sh output
    command: mktemp 
    register: output_file

  - name: Run script.sh
    shell: >
      script.sh
      param1
      param2 > {{ output_file.stdout }}
    async: 120
    poll: 60
    ignore_errors: yes

  - slurp:
      src: "{{ output_file.stdout }}"
    register: script_stdout

  - name: Print the script.sh output
    debug: 
      msg: "{{ script_stdout.content | b64decode }}"

  - file:
      path: "{{ output_file.stdout }}"
      state: absent

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.