I'm able to list unreachable
servers using the below Ansible playbook:
---
- name: "Play 1-Find the details here {{ source_host }} & {{ dest_host }}"
hosts: localhost
any_errors_fatal: True
serial: 1
vars:
source_host: "{{ hostvars[inventory_hostname]['serverlist_input'] }}"
gather_facts: no
tasks:
- add_host:
name: "{{ item | trim }}"
groups: source_node
printback_rec: "{{ hostvars[inventory_hostname]['printback_input'] }}"
with_items:
- "{{ source_host.split(',') }}"
- set_fact:
rec_group_names: 'source_node'
- name: Check unreachable hosts
hosts: "{{ hostvars['localhost']['rec_group_names'] }}"
gather_facts: true
any_errors_fatal: false
tasks:
- name: Print group names
debug:
msg: "GROUP NAME TO BE TESTED: {{ group_names }}"
- name: Perform ping in Template
ping:
- block:
- debug:
var: ansible_play_hosts_all
- debug:
var: ansible_play_hosts
- set_fact:
down: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
- debug:
var: down
run_once: true
- name: "Display unreachable hosts one at a time {{ startlogstring | default('start:') }}"
debug:
msg: "Unreachable Host: {{ item }}"
loop: "{{ ansible_play_hosts_all | difference(ansible_play_hosts) }}"
run_once: true
- fail:
msg: "Exiting play if target hosts are unreachable"
when: ansible_play_hosts_all != ansible_play_hosts
run_once: true
- name: Play 2- Configure Source nodes
hosts: source_node
gather_facts: false
any_errors_fatal: false
debugger: never
ignore_unreachable: yes
vars:
ansible_ssh_common_args: '-o ConnectTimeout=2'
tasks:
# - name: Detect unreachable hosts
# import_playbook: "{{ playbook_dir }}/generictask_templates/logunreachablehost.yml"
- name: Perform ping
ping:
Desired Output:
PLAY [Play 1-Find the details here {{ source_host }} & {{ dest_host }}] ********
TASK [Reset Github logs] *******************************************************
changed: [localhost] => (item=precheck.log)
TASK [add_host] ****************************************************************
ok: [localhost] => (item=remotehost4)
ok: [localhost] => (item=remotehost3)
ok: [localhost] => (item=remotehost2)
ok: [localhost] => (item=remotehost1)
TASK [set_fact] ****************************************************************
ok: [localhost]
PLAY [Check unreachable hosts] *************************************************
TASK [Gathering Facts] *********************************************************
fatal: [remotehost1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname remotehost1: Name or service not known", "unreachable": true}
[WARNING]: <redacted>
ok: [remotehost3]
[WARNING]: <redacted>
ok: [remotehost4]
[WARNING]: <redacted>
ok: [remotehost2]
TASK [Print group names] *******************************************************
ok: [remotehost4] => {
"msg": "GROUP NAME TO BE TESTED: ['source_node']"
}
TASK [Perform ping in Template] ************************************************
ok: [remotehost3]
ok: [remotehost4]
ok: [remotehost2]
TASK [debug] *******************************************************************
ok: [remotehost4] => {
"ansible_play_hosts_all": [
"remotehost4",
"remotehost3",
"remotehost2",
"remotehost1"
]
}
TASK [debug] *******************************************************************
ok: [remotehost4] => {
"ansible_play_hosts": [
"remotehost4",
"remotehost3",
"remotehost2"
]
}
TASK [set_fact] ****************************************************************
ok: [remotehost4]
TASK [debug] *******************************************************************
ok: [remotehost4] => {
"down": [
"remotehost1"
]
}
TASK [Display unreachable hosts one at a time start:] ***************
ok: [remotehost4] => (item=remotehost1) => {
"msg": "Unreachable Host: remotehost1"
}
TASK [fail] ********************************************************************
fatal: [remotehost4]: FAILED! => {"changed": false, "msg": "Exiting play if target hosts are unreachable"}
NO MORE HOSTS LEFT *************************************************************
PLAY RECAP *********************************************************************
remotehost4 : ok=11 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
remotehost2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remotehost1 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remotehost3 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Here is my requirement.
I wish to create a reusable template either include_tasks
or import_playbook
where i can pass any <groups>
to hosts:
and it would help me log all unreachable
servers for that group_name
.
The purpose is to reuse this template / playbook in an other play to log unreachable
servers.
I tried the below but it does not work and errors:
Step 1: Created reusable playbook called "{{ playbook_dir }}/templates/logunreachable.yml"
and copied the same logic from the previous successful run without any changes:
cat {{ playbook_dir }}/templates/logunreachable.yml
---
- name: Check unreachable hosts
hosts: "{{ hostvars['localhost']['rec_group_names'] }}"
gather_facts: true
any_errors_fatal: false
tasks:
- name: Print group names
debug:
msg: "GROUP NAME TO BE TESTED: {{ group_names }}"
run_once: true
- name: Perform ping in Template
ping:
- block:
- debug:
var: ansible_play_hosts_all
- debug:
var: ansible_play_hosts
- set_fact:
down: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
- debug:
var: down
run_once: true
- name: "Display unreachable hosts one at a time {{ startlogstring | default('are-inject-start:') }}"
debug:
msg: "Unreachable Host: {{ item }}"
loop: "{{ ansible_play_hosts_all | difference(ansible_play_hosts) }}"
run_once: true
- fail:
msg: "Exiting play if target hosts are unreachable"
when: ansible_play_hosts_all != ansible_play_hosts
run_once: true
In the main playbook I import
the above playbook and expect similar output as before:
Caller playbook:
---
- name: "Play 1-Find the details here {{ source_host }} & {{ dest_host }}"
hosts: localhost
any_errors_fatal: True
serial: 1
vars:
source_host: "{{ hostvars[inventory_hostname]['serverlist_input'] }}"
gather_facts: no
tasks:
- add_host:
name: "{{ item | trim }}"
groups: source_node
printback_rec: "{{ hostvars[inventory_hostname]['printback_input'] }}"
with_items:
- "{{ source_host.split(',') }}"
- set_fact:
rec_group_names: 'source_node'
- name: Play 2- Configure Source nodes
hosts: source_node
gather_facts: false
any_errors_fatal: false
debugger: never
ignore_unreachable: yes
vars:
ansible_ssh_common_args: '-o ConnectTimeout=2'
tasks:
- name: Detect unreachable hosts
import_playbook: "{{ playbook_dir }}/generictask_templates/logunreachablehost.yml"
- name: Perform ping
ping:
However, the output is not as expected like before:
PLAY [Play 1-Find the details here {{ source_host }} & {{ dest_host }}] ********
TASK [Reset Github logs] *******************************************************
changed: [localhost] => (item=precheck.log)
TASK [add_host] ****************************************************************
ok: [localhost] => (item=remotehost4)
ok: [localhost] => (item=remotehost3)
ok: [localhost] => (item=remotehost2)
ok: [localhost] => (item=remotehost1)
TASK [set_fact] ****************************************************************
ok: [localhost]
PLAY [Play 2- Configure Source nodes] ******************************************
TASK [Detect unreachable hosts] ************************************************
fatal: [remotehost1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname remotehost1: Name or service not known", "skip_reason": "Host remotehost1 is unreachable", "unreachable": true}
[WARNING]: <redacted>
fatal: [remotehost3]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3.9"}, "changed": false, "module_stderr": "Shared connection to remotehost3 closed.\\r\\n", "module_stdout": "", "msg": "MODULE FAILURE\\nSee stdout/stderr for the exact error", "rc": 0}
[WARNING]: <redacted>
fatal: [remotehost2]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/local/bin/python3.9"}, "changed": false, "module_stderr": "Shared connection to remotehost2 closed.\\r\\n", "module_stdout": "", "msg": "MODULE FAILURE\\nSee stdout/stderr for the exact error", "rc": 0}
[WARNING]: <redacted>
fatal: [remotehost4]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/local/bin/python3.9"}, "changed": false, "module_stderr": "Shared connection to remotehost4 closed.\\r\\n", "module_stdout": "", "msg": "MODULE FAILURE\\nSee stdout/stderr for the exact error", "rc": 0}
TASK [Perform ping] ************************************************************
fatal: [remotehost1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname remotehost1: Name or service not known", "skip_reason": "Host remotehost1 is unreachable", "unreachable": true}
PLAY RECAP *********************************************************************
remotehost4 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
remotehost2 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
remotehost1 : ok=0 changed=0 unreachable=2 failed=0 skipped=2 rescued=0 ignored=0
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remotehost3 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Can you please suggest a solution where I could pass the hosts: <groupname>
and the workflow could help list all unreachable hosts for the passed ?
The purpose is to reuse this template / playbook in an other play to log unreachable servers.
Ansible automatically skips the unreachable hosts and proceeds with execution without failing the play. Also, Ansible will automatically skip them in all the subsequent plays. The list of unreachable hosts will be shown in the play recap.
Consider the following minimal reproducible example:
# inventory.yaml
---
all:
vars:
ansible_python_interpreter: auto_silent # this suppresses the warnings
source_node:
hosts:
remotehost1:
ansible_connection: ssh
ansible_host: example.com
remotehost[2:4]:
ansible_connection: local
# playbook.yaml
---
- name: Test with unreachable hosts 1
# you can gather facts as well,
# but this is faster is you don't need them
gather_facts: false
hosts: source_node
tasks:
- name: Perform ping 1
ping:
- name: Test with unreachable hosts 2
gather_facts: false
hosts: source_node
tasks:
- name: Perform ping 2
ping:
Output (I have enabled YAML stdout callback enabled for brevity and readability):
Alexanders-Mini:78050414 alexander$ ansible-playbook playbook.yaml -i inventory.yaml
PLAY [Test with unreachable hosts 1] **********************************************************************************************************************************************************************
TASK [Perform ping 1] *************************************************************************************************************************************************************************************
ok: [remotehost2]
ok: [remotehost3]
ok: [remotehost4]
fatal: [remotehost1]: UNREACHABLE! =>
changed: false
msg: 'Failed to connect to the host via ssh: ssh: connect to host example.com port
22: Operation timed out'
unreachable: true
PLAY [Test with unreachable hosts 2] **********************************************************************************************************************************************************************
TASK [Perform ping 2] *************************************************************************************************************************************************************************************
ok: [remotehost3]
ok: [remotehost2]
ok: [remotehost4]
PLAY RECAP ************************************************************************************************************************************************************************************************
remotehost1 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
remotehost2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remotehost3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remotehost4 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now, if you still want to list the unreachable hosts in a separate playbook while passing the list of hosts ad-hoc. Actually, there are three errors preventing that in your current implementation:
hostvars
simply because Ansible doesn't know what hosts should it run the play on;--extra-vars
;localhost
.All of them block the reusability of the playbook.
So, to solve them all, you'll need to add the hosts to the inventory in the same playbook but in a separate play on localhost
, and run ping
on all
hosts if that plain variable is not defined (or on that target group, if it's not empty). You don't need to add them twice, by the way, so I'm checking if they are already present in the inventory. I also added an ability to control the fact gathering and speed up the things:
# detect_and_list_unreachable_hosts.yaml
---
- name: Add the hosts to the inventory if they are missing
hosts: localhost
gather_facts: false
tasks:
- name: Add the list of hosts to the custom group
add_host:
name: "{{ item | trim }}"
groups: "{{ target_group | default ('source_node') }}"
loop: "{{ target_hosts.split(',') }}"
when:
- target_hosts is defined and target_hosts
- groups['source_node'] is defined and item not in item not in groups['source_node'
or item not in groups
- name: List the unreachable hosts
hosts: "{{ test_hosts | default('all') }}"
gather_facts: "{{ gather_facts_on_test_hosts | default('false') }}"
tasks:
- name: Ping the hosts
ping:
- name: Log the ping results
debug:
var: ansible_play_hosts_all | difference(ansible_play_hosts)
run_once: true
This playbook will work in any circumstances: with or without the extra vars, with or without the predefined inventory, with or without localhost in the inventory, with or without the target hosts in the inventory.
You can also add a failed_when
condition, or call assert
or fail
module to stop the execution if there are any unreachable hosts but some others still work. To make the output even more clear, you can also set a fact delegating it to localhost
and displaying it in the subsequent play. Otherwise, it will be shown for a random host, which could be confusing:
# detect_and_list_unreachable_hosts.yaml
---
- name: Add the hosts to the inventory if they are missing
hosts: localhost
gather_facts: false
tasks:
- name: Add the list of hosts to the custom group
add_host:
name: "{{ item | trim }}"
groups: "{{ target_group | default ('source_node') }}"
loop: "{{ target_hosts.split(',') }}"
when:
- target_hosts is defined and target_hosts
- groups['source_node'] is defined and item not in item not in groups['source_node'
or item not in groups
- name: List the unreachable hosts
hosts: "{{ test_hosts | default('all') }}"
gather_facts: "{{ gather_facts_on_test_hosts | default('false') }}"
tasks:
- name: Ping the hosts
ping:
- name: Delegate the ping results to localhost for logging
set_fact:
unreachable_hosts: "{{ ansible_play_hosts_all | difference(ansible_play_hosts) }}"
delegate_to: localhost
delegate_facts: true
run_once: true
- name: Display the unreachable hosts
hosts: localhost
gather_facts: false
tasks:
- name: Log the list of unreachable hosts
debug:
var: unreachable_hosts
failed_when: unreachable_hosts is defined and unreachable_hosts
To use it with any other playbook, just add it as the first play:
# playbook.yaml
---
- name: Detect the unreachable hosts
import_playbook: detect_and_list_unreachable_hosts.yaml
# subsequent plays go here
If you don't want to use extra vars or you don't have an inventory, you can always add vars
to that import:
# playbook.yaml
---
- name: Detect the unreachable hosts
import_playbook: detect_and_list_unreachable_hosts.yaml
vars:
target_hosts: "{{ some_other_hosts }}"
# or even hardcode:
# target_hosts: remotehost5,remotehost6