Failed host list from 'rescue' task

Hello Everyone,

Greetings!

I am working to get failed hosts from ‘rescue’ section into a CSV file.

When i run task for 'inventory_hostname’ from ‘rescue’ section:

rescue:

  • name: inventory_host name list debug
    debug:
    msg: “{{ inventory_hostname }}”
    Output:

TASK [inventory_host name list debug] *******************************************************************************************************************************************************************************************************
ok: [bogus1] => {}

MSG:

bogus1
ok: [bogus2] => {}

MSG:

bogus2

when i tried to append data to a list.

  • set_fact:
    failed_list: “{{ failed_list + [ansible_host] }}”
  • name: failed_list debug
    debug: var=failed_list

Output:

TASK [set_fact] *****************************************************************************************************************************************************************************************************************************
ok: [bogus1]
ok: [bogus2]

TASK [failed_list debug] ********************************************************************************************************************************************************************************************************************
ok: [bogus1] => {
“failed_list”: [
“bogus1”
]
}
ok: [bogus2] => {
“failed_list”: [
“bogus2”
]
}

Here bogus1, bogus2 host names are failed in ‘resce’ section.
We have multiple hosts in our environment. While running playbook we have to capture failed hostname into a file as mentioned below:

failed_hosts.csv:
number of failed hots: 2
hostname:
bogus1
bogus2

Thank you for your help.

Study and play around with these expressions until you understand what each piece does.
set_fact sets a host-specific fact, which for convenience can be accessed like any other variable.
Any host can see other hosts’ facts/variables by looking in hostvars[‘somehost’].varname.
The “CSV” is really just a list of failed hosts. With only one column, does CSV really mean anything?
The final copy task should be a template task, but I left it in-line for clarity.

  - name: Update failed_list fact
    ansible.builtin.set_fact:
      failed_list: "{{ failed_list | default([]) + [ansible_host] }}"

  - name: Debug list
    ansible.builtin.debug:
     msg:
      - "by play_hosts: {{ ansible_play_hosts | map('extract', hostvars) | map(attribute='failed_list') | flatten }}"
      - "by all: {{ hostvars | dict2items | map(attribute='value') | map(attribute='failed_list', default=[]) | flatten }}"

  - name: Create failed_list CSV
    ansible.builtin.copy:
      content: |
        failed
        {% for host in hostvars | dict2items | map(attribute='value') | map(attribute='failed_list', default=[]) | flatten %}
        {{ host }}
        {% endfor %}
      dest: /tmp/failed_list.csv
    run_once: true

Hope this helps.

I am working to get failed hosts from 'rescue' section into a CSV file.
[...]
> *when i tried to append data to a list. *

> - set_fact:
> failed_list: "{{ failed_list + [ansible_host] }}"

*failed_hosts.csv:*
*number of failed hots: 2*
*hostname:*
*bogus1*
*bogus2*

Each host will have its own variable *failed_list*. Therefore, it
makes no sense to record *ansible_host* in this list. Instead, you
might want to record the failed tasks. For example, the block below
runs three tasks. If any of the tasks fails the name of it will be
added to the list *failed_list*

    - name: block A
      block:
        - name: task1A
          command: "{{ ['true', 'false']|random }}"
        - name: task2A
          command: "{{ ['true', 'false']|random }}"
        - name: task3A
          command: "{{ ['true', 'false']|random }}"
      rescue:
        - set_fact:
            failed_list: "{{ failed_list +
                             [ansible_failed_task.name] }}"

Create dictionary of all hosts and failed tasks

  failed_lists: "{{ dict(groups.all|
                    zip(hostvars|dict2items|
                    map(attribute='value.failed_list',
                    default=))) }}"

For example, given the inventory

  > cat hosts
  host_A ansible_host=10.1.0.61
  host_B ansible_host=10.1.0.62
  host_C ansible_host=10.1.0.63

a play running two blocks (A and B) on two hosts (host_A and host_C)
gives

  failed_lists:
    host_A: [task1A, task1B]
    host_B:
    host_C: [task3A, task2B]

Declare the list of failed hosts by selecting nonempty lists

  failed_hosts: "{{ failed_lists|dict2items|
                    selectattr('value')|
                    map(attribute='key')|list }}"

gives

  failed_hosts: [host_A, host_C]

Now you can create reports. For example, to write the file on the
controller, delegate the task to localhost

    - copy:
        dest: /tmp/failed_hosts.yaml
        content: |
          number_of_failed_hosts: {{ failed_hosts|length }}
          hostnames: {{ failed_hosts|join(', ') }}
          {% for h,l in failed_lists.items() %}
          {{ h }}: {{ l|sort|join(', ') }}
          {% endfor %}
      delegate_to: localhost
      run_once: true

gives the YAML file

  > cat /tmp/failed_hosts.yaml
  number_of_failed_hosts: 2
  hostnames: host_A, host_C
  host_A: task1A, task1B
  host_B:
  host_C: task2B, task3A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Example of a complete playbook for testing

Hi Todd,

Greetings!

Thank you for your time and help. It is tested for my playbook and also It is very helpful and I am exploring it.

The below one is not successful as it got error 'The task includes an option with an defined variable. The error was: ‘ansible.vars.hostvars.HostVarsVars object’ has no attribute ‘failed_host’ ’

- "by play_hosts: {{ ansible_play_hosts | map('extract', hostvars)

Yes, I got the expected results ans added my other requirements to csv data.

Once again thank you.

I am digging into  ansible.builtin.copy and ansible.builtin.template **content** but not get options to append.

Can we append two different tasks data into one csv file instead of over write?

One task i have under ‘rescue’ section as example below:

rescue:
- name: Create failed_list CSV
  **ansible.builtin.copy:**
      **content: |**
        failed
        {% for host in hostvars | dict2items | map(attribute='value') | map(attribute='failed_list', default=[]) | flatten %}
        {{ host }}
        {% endfor %}
      dest: **/tmp/facts.csv**
    run_once: true
/tmp/facts.csv gives

# cat /tmp/facts.csv
somedata1
somedata2

 I have another task cvs data under '**always**' section:

always:
- name: append volume info to CSV
  **ansible.builtin.copy:**
      **content: |**
        failed
        {% example rule  %}
        {{ host }}
        {% endfor %}
      dest: /tmp/facts.csv
    run_once: true

Gives:

# cat /tmp/facts.csv
somedata3

somedata4 

It should append data instead of overwrite here. Is it possible?
We just need end of two files data into one:

# cat /tmp/facts.csv
somedata1
somedata2
somedata3
somedata4

Thank you,

Consider https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assemble_module.html

It combines multiple files into one.

Greetings for the day!

Looks like ansible.builtin.assemble for two different files from directory to another required file.

Sorry to raise my query again but…wanted to be clear.

My requirement is, As i mentioned in previous mail: playbook should be having only one csv file but two different tasks using that.( not to store 2 diff files and combine). It is to append one file from 2 tasks.

**# cat /tmp/facts.csv**
somedata1   [coming from 'rescue' task
somedata2   [coming from 'rescue' task 
somedata3   [ coming from 'always' task
somedata4   [coming from 'always' task

Thank you,

Right, but as you’ve discovered copy and template won’t update part of a file.
So your two choices are: (1) remove the various template/copy tasks from the various places in your playbook and replace them with a single task at the end that creates the single file, or (2) store the output of the various tasks to separate files and use assemble (or a shell script) at then end to put the disparate pieces into one file.
If there are other options, they aren’t obvious to me at the moment.

Thanks Todd.

I will give a try option 1.

(1) remove the various template/copy tasks from the various places in your playbook and replace them with a single task at the end that creates the single file, or (2) store the output of the various tasks to separate files and use assemble (or a shell script) at then end to put the disparate pieces into one file.