Failed host list from 'rescue' task

266 views
Skip to first unread message

Aharonu

unread,
Mar 15, 2023, 1:45:23 PM3/15/23
to ansible...@googlegroups.com
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.

Todd Lewis

unread,
Mar 15, 2023, 2:43:20 PM3/15/23
to ansible...@googlegroups.com, uto...@gmail.com
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.
--
Todd
--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/CANGEjuXCUuCkps8CU9oWnh3XHN7jo6OJnGJQOCQRvay9w1rg2w%40mail.gmail.com.

-- 
Todd

Vladimir Botka

unread,
Mar 15, 2023, 7:49:08 PM3/15/23
to Aharonu, ansible...@googlegroups.com
On Wed, 15 Mar 2023 17:44:55 +0000
Aharonu <ahar...@gmail.com> wrote:

> 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

shell> 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

shell> 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

---
- hosts: host_A,host_C

vars:

failed_list: []
failed_lists: "{{ dict(groups.all|
zip(hostvars|dict2items|
map(attribute='value.failed_list',
default=[]))) }}"
failed_hosts: "{{ failed_lists|dict2items|
selectattr('value')|
map(attribute='key')|list }}"

tasks:

- 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] }}"

- name: block B
block:
- name: task1B
command: "{{ ['true', 'false']|random }}"
- name: task2B
command: "{{ ['true', 'false']|random }}"
- name: task3B
command: "{{ ['true', 'false']|random }}"
rescue:
- set_fact:
failed_list: "{{ failed_list +
[ansible_failed_task.name] }}"

- block:
- debug:
var: failed_lists|to_yaml
- debug:
var: failed_hosts|to_yaml
run_once: true

- 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

- block:
- include_vars:
file: /tmp/failed_hosts.yaml
name: test
- debug:
var: test
run_once: true
...


--
Vladimir Botka

Aharonu

unread,
Mar 16, 2023, 9:39:02 AM3/16/23
to ansible...@googlegroups.com, Todd Lewis
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.

Aharonu

unread,
Mar 17, 2023, 1:40:05 AM3/17/23
to ansible...@googlegroups.com, Todd Lewis, Vladimir Botka

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,
On Thu, 16 Mar 2023, 00:13 Todd Lewis, <uto...@gmail.com> wrote:

Todd Lewis

unread,
Mar 17, 2023, 8:06:32 AM3/17/23
to Ansible Project

Aharonu

unread,
Mar 17, 2023, 9:43:30 AM3/17/23
to ansible...@googlegroups.com
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,

Todd Lewis

unread,
Mar 17, 2023, 11:35:12 AM3/17/23
to Ansible Project
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.

Aharonu

unread,
Mar 17, 2023, 12:18:57 PM3/17/23
to ansible...@googlegroups.com
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.

Reply all
Reply to author
Forward
0 new messages