toussa
toussa

Reputation: 1098

Ansible logging on 'command' task timeout

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

Upvotes: 3

Views: 4023

Answers (2)

arnE
arnE

Reputation: 1

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

Upvotes: 0

Vladimir Botka
Vladimir Botka

Reputation: 68144

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: []

Upvotes: 4

Related Questions