How to ignore the connection error in play - ansible

I want to continue my playbook to do some other tasks when some hosts are unreachable. However, the ignore_errors seems doesn't work. The debug msg is not printed.
ansible version is 2.5.4. Is there a way to do this in this version?
- name: check accessibility
hosts: myhosts
tasks:
- ping:
ignore_errors: yes
- fail:
msg: "Host {{ansible_hostname}} is not accessible"
when: False

An option would be to ping each 'inventory_hostname' in the block and end the play if the ping fails.
- hosts: myhosts
gather_facts: no
tasks:
- block:
- delegate_to: localhost
command: ping -c1 "{{ inventory_hostname }}"
rescue:
- fail:
msg: "{{ inventory_hostname }} not accessible. End of play."
- debug:
msg: "Host {{ inventory_hostname }} continue play."
- setup:
Notes:
Set 'gather_facts: no', because we are not sure all hosts are available
Use 'inventory_hostname', because of 'gather_facts: no'
use 'setup' module after the 'block' if necessary
Running the playbook with available hosts: test_01, test_02, test_03 and unavailable host test_99 gives (abridged):
TASK [fail]
fatal: [test_99]: FAILED! => {"changed": false, "msg": "test_99 not accessible. End of play."}
TASK [debug]
ok: [test_03] => {
"msg": "Host test_03 continue play."
}
ok: [test_01] => {
"msg": "Host test_01 continue play."
}
ok: [test_02] => {
"msg": "Host test_02 continue play."
}
TASK [setup]
ok: [test_03]
ok: [test_01]
ok: [test_02]
PLAY RECAP
test_01 : ok=3 changed=1 unreachable=0 failed=0
test_02 : ok=3 changed=1 unreachable=0 failed=0
test_03 : ok=3 changed=1 unreachable=0 failed=0
test_99 : ok=0 changed=0 unreachable=0 failed=2

Related

Ansible - Compare dictionaries works but will not accept an addition

I am trying to compare two dictionaries and create a 3rd dictionary if an item has changed or is not present. I have this playbook
---
- hosts: localhost
connection: local
gather_facts: no
vars:
host_list1:
host1: 1.1.1.1
host2: 2.2.2.2
host3: 3.3.3.3
host_list2:
host1: 1.1.1.1
host2: 2.2.2.2
host3: 3.3.3.3
tasks:
- name: Define Dict
set_fact:
diff_list: {}
- name: Compare lists
set_fact:
diff_list: "{{ diff_list|combine({item: host_list1[item]}) }}"
loop: "{{ host_list1.keys()|list }}"
when: host_list1[item] != host_list2[item]
- name: Display Results
debug:
msg: "{{ diff_list }}"
If I change the IP in host_list1 it has the desired result and will update the diff_list.
PLAY [localhost] **************************************************************************************************************************************************************
TASK [Define Dict] ************************************************************************************************************************************************************
ok: [localhost]
TASK [Compare lists] **********************************************************************************************************************************************************
ok: [localhost] => (item=host3)
ok: [localhost] => (item=host2)
skipping: [localhost] => (item=host1)
TASK [Display Results] ********************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"host2": "2.2.2.5",
"host3": "3.3.3.4"
}
}
PLAY RECAP ********************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
However if I try and add another host into the host_list1 dictionary I get the following error
PLAY [localhost] **************************************************************************************************************************************************************
TASK [Define Dict] ************************************************************************************************************************************************************
ok: [localhost]
TASK [Compare lists] **********************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'host_list1[item] != host_list2[item]' failed. The error was: error while evaluating conditional (host_list1[item] != host_list2[item]): 'dict object' has no attribute u'host4'\n\nThe error appears to be in '/home/playbooks/test.yml': line 20, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Compare lists\n ^ here\n"}
PLAY RECAP ********************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
I have tried updating the when statement
when: host_list1[item] != host_list2[item] or not defined
However this does not work and I am unable to get the new host appended to the diff_list dictionary
The problem is your conditional statement:
when: host_list1[item] != host_list2[item]
If item is key that is present in only host_list1, then host_list2[item] is an error. There are (at least!) two ways of fixing this.
Use a default value
You can use the default filter to provide a default value when the result of an item lookup is undefined:
when: host_list1[item] != host_list2[item]|default(none)
Explicitly check for membership
You can check that the key exists in host_list2 before attempting to get its value:
when: item not in host_list2 or host_list1[item] != host_list2[item]
You can get your result using a single jinja2 expression without using any tasks.
The following playbook:
---
- hosts: localhost
connection: local
gather_facts: no
vars:
hosts_current:
host1: 1.1.1.1
host2: 2.2.2.3
host3: 3.3.3.4
host4: 5.5.5.5
hosts_origin:
host1: 1.1.1.1
host2: 2.2.2.2
host3: 3.3.3.3
diff_dict: >-
{{
hosts_origin | dict2items
| symmetric_difference(hosts_current | dict2items)
| items2dict
}}
tasks:
- name: Show the calculated var
debug:
var: diff_dict
gives:
PLAY [localhost] **************************************************************************************************************************************************************************************************************
TASK [Show the calculated var] ************************************************************************************************************************************************************************************************
ok: [localhost] => {
"diff_dict": {
"host2": "2.2.2.3",
"host3": "3.3.3.4",
"host4": "5.5.5.5"
}
}
PLAY RECAP ********************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible OR not working correctly (I know its me!)

I know this is something I am doing wrong but its slowly driving me crazy. Please note, this cannot be yum controlled unfortunately. I have an ansible playbook that checks for the presence of tomcat, then contents of a file and retrieves a version:
Vars:
tomcat_version: 9.0.62
Checks:
- name: Check for Tomcat installation
stat:
path: "{{ tomcat_directory }}/lib/catalina.jar"
register: tomcat_already_installed
- name: Get existing Tomcat version
shell: "{{ java_directory }}/bin/java -cp {{ tomcat_directory }}/lib/catalina.jar org.apache.catalina.util.ServerInfo |grep \"Server number\" |awk -F':[[:blank:]]*' '{print $2}' | sed '/\\./ s/\\.\\{0,1\\}0\\{1,\\}$//'"
register: existing_tomcat_version
This returns a true / false and also a version number.
The next playbook will install Tomcat or Upgrade it... if the version (set in vars) isn't matched or Tomcat found.
- name: Install tomcat
include_tasks: install.yml
when:
The issue I have is with the when statement. Independently these two work:
# - not tomcat_already_installed
# - "existing_tomcat_version.stdout|string not in tomcat_version"
However, I've tried an or between them and it either doesn't install at all or just keeps running the installer every run. Things I've tried:
# - "not tomcat_already_installed or existing_tomcat_version.stdout|string not in tomcat_version"
# - not tomcat_already_installed or existing_tomcat_version.stdout|string not in tomcat_version
# - not tomcat_already_installed or 'existing_tomcat_version.stdout|string not in tomcat_version'
- not tomcat_already_installed or "existing_tomcat_version.stdout|string" not in tomcat_version
- "( not tomcat_already_installed or existing_tomcat_version.stdout|string not in tomcat_version"
Debug isn't really showing me anything other than the values look ok:
- name: Debug existing_tomcat_version.stdout
debug:
msg: "existing_tomcat_version = {{ existing_tomcat_version }}"
- name: Debug vars_tomcat_version
debug:
msg: "vars_tomcat_version = {{ tomcat_version }}"
ok: [i-0ddfdsfdsf90b8] => {
"msg": "existing_tomcat_version = 9.0.62"
}
Thursday 12 May 2022 15:38:27 +0200 (0:00:00.012) 0:00:38.711 **********
ok: [i-sdfdsfsdfsdffd] => {
"msg": "vars_tomcat_version = 9.0.62"
}
TASK [tomcat : debug tomcat_already_installed] *********************************************************************************************************************************************************************************************************************************
Thursday 12 May 2022 16:12:12 +0200 (0:00:00.915) 0:00:38.940 **********
Thursday 12 May 2022 16:12:12 +0200 (0:00:00.915) 0:00:38.939 **********
ok: [i-sdfsdfsdfsdfsdf] => {
"msg": "tomcat_already_installed = {'stat': {'exists': False}, 'changed': False, 'failed': False}"
Any help welcomed, I'm sure this is just a string misread or format thing.
Thanks!
Just to put you on track, the following playbook:
---
- hosts: localhost
gather_facts: false
vars:
# Fake the original example vars from extra_vars on this example
existing_tomcat_version:
stdout: "{{ f_tomcat_version | d('0.0.0') }}"
tomcat_already_installed: "{{ f_tomcat_exists | bool }}"
tomcat_version: 9.0.62
tasks:
- debug:
msg: "Tomcat isn't installed or is not using version {{ tomcat_version }}"
when: not (tomcat_already_installed and existing_tomcat_version.stdout is version(tomcat_version, 'eq'))
Gives with different faked values passed as extra vars:
$ ansible-playbook playbook.yml -e f_tomcat_exists=false
PLAY [localhost] ****************************************************************************************************************************
TASK [debug] ********************************************************************************************************************************
ok: [localhost] => {
"msg": "Tomcat isn't installed or is not using version 9.0.62"
}
PLAY RECAP **********************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ ansible-playbook playbook.yml -e f_tomcat_exists=true -e f_tomcat_version=8.4.5
PLAY [localhost] ****************************************************************************************************************************
TASK [debug] ********************************************************************************************************************************
ok: [localhost] => {
"msg": "Tomcat isn't installed or is not using version 9.0.62"
}
PLAY RECAP **********************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ ansible-playbook playbook.yml -e f_tomcat_exists=true -e f_tomcat_version=9.0.62
PLAY [localhost] ****************************************************************************************************************************
TASK [debug] ********************************************************************************************************************************
skipping: [localhost]
PLAY RECAP **********************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

Fetch hosts without domain from inventory file

I have following inventory file
$ cat hosts
[web]
server1.example.com
server2.example.com
I would like to fetch the hostname, without the part of domain (.example.com).
I tried with the following playbook, however, it is still fetching with the entire hostname..
$ playbook.yaml
- hosts: localhost
tasks:
- debug:
msg: "{{ groups['web'] }}"
Output
PLAY [localhost] *************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"server1.example.com"
"server2.example.com"
]
}
PLAY RECAP *******************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Expected output
PLAY [localhost] *************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"server1"
"server2"
]
}
PLAY RECAP *******************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can get what you want from a magic variable called inventory_hostname_short which basically returns anything before the first . found in the inventory_hostname.
To get this in a normal play host loop, it's as easy as:
- hosts: all
tasks:
- name: show target short name
debug:
var: inventory_hostname_short
If you need to get that for hosts not in the host loop, you will have to go through hostvars. Here is an example to get all those names in a list for a given group running from localhost:
- hosts: localhost
gather_facts: false
tasks:
- name: show list of shortnames for group 'toto'
debug:
msg: "{{ groups['toto'] | map('extract', hostvars, 'inventory_hostname_short') }}"
An other example to get that name only for the first server in group 'toto'
- hosts: localhost
gather_facts: false
tasks:
- name: show shortnames for first server in group 'toto'
vars:
server_name: "{{ groups['toto'][0] }}"
debug:
msg: "{{ hostvars[server_name].inventory_hostname_short }}"

Ansible PLaybook: Escape '$' in Linux path

I have a path which looks like this -
base_dir/123/path/to/G\$/subdirectory/html/
When I try to set this path in Ansible playbook, it throws error. If add \$ to escape '\', it throws unexpected failure error.
Playbkook -
- hosts: localhost
vars:
account_id: 123
tasks:
- name: Add \ to path
debug:
var: "base_dir/{{ account_id }}/path/to/G\\$/subdirectory/html/"
Result -
TASK [Gathering Facts] *************************************************************************************************************************************************
task path: /playbooks/example_path.yml:2
ok: [localhost]
META: ran handlers
TASK [Add \ to path] ***************************************************************************************************************************************************
task path: /playbooks/exmaple_path.yml:6
fatal: [localhost]: FAILED! => {
"msg": "Unexpected failure during module execution."
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1
As explained in the debug module documentation, the var option is expecting a variable name, not a scalar for output. You are getting an error because \ is not expected in a variable name. Running the playbook with -vvv will give you a little more explanations.
In this case you need to use the msg option.
- hosts: localhost
gather_facts: false
vars:
account_id: 123
tasks:
- name: Add \ to path
debug:
msg: "base_dir/{{ account_id }}/path/to/G\\$/subdirectory/html/"
Result
PLAY [localhost] ***************************************************************
TASK [Add \ to path] ***********************************************************
ok: [localhost] => {
"msg": "base_dir/123/path/to/G\\$/subdirectory/html/"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to use the Single-Quoted Style. See the example below
- hosts: localhost
vars:
my_dir1: "/scratch/tmp/G1\\$"
my_dir2: '/scratch/tmp/G2\$'
tasks:
- file:
state: directory
path: "{{ item }}"
loop:
- "{{ my_dir1 }}"
- "{{ my_dir2 }}"
# ls -1 /scratch/tmp/
'G1\$'
'G2\$'

Ansible: move on to the next task if the task is completed on one host

In ansible what i require is to check for a file is available on two hosts. But if the file is available on even one host i need to cancel the task on other host and move onto the next task. The reason why i require this is because a the next task can only be done if that particular file is available and that file can be randomly written to any of the hosts.
The following play does exactly what you want:
---
- hosts:
- server1
- server2
gather_facts: False
vars:
file_name: 'foo.bar'
tasks:
- name: wait for file
wait_for:
path: '{{ file_name }}'
state: present
timeout: 30
ignore_errors: True
- name: stat
stat:
path: '{{ file_name }}'
register: result
- name: next
debug:
msg: "File {{ file_name }} available on {{ ansible_host }}"
when: result.stat.isreg is defined and result.stat.isreg
The output is:
PLAY [server1,server2] *********************************************************
TASK [wait for file] ***********************************************************
ok: [server1]
fatal: [server2]: FAILED! => {"changed": false, "elapsed": 3, "msg": "Timeout when waiting for file foo.bar"}
...ignoring
TASK [stat] ********************************************************************
ok: [server1]
ok: [server2]
TASK [next] ********************************************************************
skipping: [server2]
ok: [server1] => {
"msg": "File foo.bar available on server1"
}
PLAY RECAP *********************************************************************
server1 : ok=3 changed=0 unreachable=0 failed=0
server2 : ok=0 changed=0 unreachable=0 failed=0
You can use the stat module to check the status like below and for also you can add the serial:1 below hosts: in your playbook
stat:
path: /path/to/something
register: p
debug:
msg: "Path exists and is a directory"
when: p.stat.isdir is defined and p.stat.isdir
https://docs.ansible.com/ansible/latest/modules/stat_module.html for more details

Resources