Or condition is not working with set_facts in my playbook - ansible

I created one playbook to run the tasks based on the test case so I have created like below
Here when I pass the ansible-playbook playbook.yml -e stage=1, it's skipping all the tasks, and when I debug the test_case* values I could see both are in a false state, So can some help me to work this code.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage == 1 }}"
test_case_2: "{{ stage == 1 or stage == 2 }}"
- name: "running ls command"
shell: "ls -l"
register: testing
when:
- test_case_1 == true
- debug:
msg: "{{ testing.stdout_lines }}"
when:
- test_case_1 == true
- name: "kickoff"
shell: "df -Th"
register: kick
when:
- test_case_2 == true
- name: "printing kickoff"
debug:
msg: "{{ kick.stdout_lines }}"
when:
- test_case_2 == true
Below is the error results which I am getting
[root#server ~]# ansible-playbook playbook.yml -e stage=1
PLAY [test] ***********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [localhost]
TASK [setting the level] **********************************************************************************************
ok: [localhost]
TASK [running ls command] *********************************************************************************************
skipping: [localhost]
TASK [debug] **********************************************************************************************************
skipping: [localhost]
TASK [kickoff] ********************************************************************************************************
skipping: [localhost]
TASK [printing kickoff] ***********************************************************************************************
skipping: [localhost]
PLAY RECAP ************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
[root#server ~]#
expected results should be, it should execute all the tasks from the play.

Your problem is that you are performing an integer comparison (stage == 1), but when you provide a value on the command line via -e stage=1, you are setting a string value.
You probably want to case the value of stage to an integer using the int filter.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage|int == 1 }}"
test_case_2: "{{ stage|int == 1 or stage|int == 2 }}"
With this change, things seem to work as expected.
Unrelated to your question, but you could rewrite the second test like this:
{{ stage|int in [1, 2] }}
That simplifies things a bit.

Related

Ansible: How to check multiple servers for a text file value, to decide which servers to run the script on?

I am trying to ask Ansible to check if a server is passive or active based on the value of a specific file in each server, then Ansible will decide which server it runs the next script on.
For example with 2 servers:
Server1
cat /tmp/currentstate
PASSIVE
Server2
cat /tmp/currentstate
ACTIVE
In Ansible
Trigger next set of jobs on server where the output was ACTIVE.
Once the jobs complete, trigger next set of jobs on server where output was PASSIVE
What I have done so far to grab the state, and output the value to Ansible is
- hosts: "{{ hostname1 | mandatory }}"
gather_facts: no
tasks:
- name: Grab state of first server
shell: |
cat {{ ans_script_path }}currentstate.log
register: state_server1
- debug:
msg: "{{ state_server1.stdout }}"
- hosts: "{{ hostname2 | mandatory }}"
gather_facts: no
tasks:
- name: Grab state of second server
shell: |
cat {{ ans_script_path }}currentstate.log
register: state_server2
- debug:
msg: "{{ state_server2.stdout }}"
What I have done so far to trigger the script
- hosts: "{{ active_hostname | mandatory }}"
tasks:
- name: Run the shutdown on active server first
shell: sh {{ ans_script_path }}stopstart_terracotta_main.sh shutdown
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
- hosts: "{{ passive_hostname | mandatory }}"
tasks:
- name: Run the shutdown on passive server first
shell: sh {{ ans_script_path }}stopstart_terracotta_main.sh shutdown
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
but I don't know how to set the value of active_hostname & passive_hostname based on the value from the script above.
How can I set the Ansible variable of active_hostname & passive_hostname based on the output of the first section?
A better solution came to my mind is to include hosts in new groups according to their state.
This would be more optimal in case there are more than two hosts.
- hosts: all
gather_facts: no
vars:
ans_script_path: /tmp/
tasks:
- name: Grab state of server
shell: |
cat {{ ans_script_path }}currentstate.log
register: server_state
- add_host:
hostname: "{{ item }}"
# every host will be added to a new group according to its state
groups: "{{ 'active' if hostvars[item].server_state.stdout == 'ACTIVE' else 'passive' }}"
# Shorter, but the new groups will be in capital letters
# groups: "{{ hostvars[item].server_state.stdout }}"
loop: "{{ ansible_play_hosts }}"
changed_when: false
- name: show the groups the host(s) are in
debug:
msg: "{{ group_names }}"
- hosts: active
gather_facts: no
tasks:
- name: Run the shutdown on active server first
shell: hostname -f # changed that for debugging
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
- hosts: passive
gather_facts: no
tasks:
- name: Run the shutdown on passive server first
shell: hostname -f
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
test-001 is PASSIVE
test-002 is ACTIVE
PLAY [all] ***************************************************************
TASK [Grab state of server] **********************************************
ok: [test-002]
ok: [test-001]
TASK [add_host] **********************************************************
ok: [test-001] => (item=test-001)
ok: [test-001] => (item=test-002)
TASK [show the groups the host(s) are in] ********************************
ok: [test-001] => {
"msg": [
"passive"
]
}
ok: [test-002] => {
"msg": [
"active"
]
}
PLAY [active] *************************************************************
TASK [Run the shutdown on active server first] ****************************
changed: [test-002]
TASK [debug] **************************************************************
ok: [test-002] => {
"msg": "test-002"
}
PLAY [passive] ************************************************************
TASK [Run the shutdown on passive server first] ****************************
changed: [test-001]
TASK [debug] **************************************************************
ok: [test-001] => {
"msg": "test-001"
}
PLAY RECAP ****************************************************************
test-001 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test-002 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
For example, given two remote hosts
shell> ssh admin#test_11 cat /tmp/currentstate.log
ACTIVE
shell> ssh admin#test_13 cat /tmp/currentstate.log
PASSIVE
The playbook below reads the files and runs the commands on active and passive servers
shell> cat pb.yml
- hosts: "{{ host1 }},{{ host2 }}"
gather_facts: false
vars:
server_states: "{{ dict(ansible_play_hosts|
zip(ansible_play_hosts|
map('extract', hostvars, ['server_state', 'stdout'])|
list)) }}"
server_active: "{{ server_states|dict2items|
selectattr('value', 'eq', 'ACTIVE')|
map(attribute='key')|list }}"
server_pasive: "{{ server_states|dict2items|
selectattr('value', 'eq', 'PASSIVE')|
map(attribute='key')|list }}"
tasks:
- command: cat /tmp/currentstate.log
register: server_state
- debug:
var: server_state.stdout
- block:
- debug:
var: server_states
- debug:
var: server_active
- debug:
var: server_pasive
run_once: true
- command: echo 'Shutdown active server'
register: out_active
delegate_to: "{{ server_active.0 }}"
- command: echo 'Shutdown passive server'
register: out_pasive
delegate_to: "{{ server_pasive.0 }}"
- debug:
msg: |
{{ server_active.0 }}: [{{ out_active.stdout }}] {{ out_active.start }}
{{ server_pasive.0 }}: [{{ out_pasive.stdout }}] {{ out_pasive.start }}
run_once: true
shell> ansible-playbook pb.yml -e host1=test_11 -e host2=test_13
PLAY [test_11,test_13] ***********************************************************************
TASK [command] *******************************************************************************
changed: [test_13]
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_state.stdout: ACTIVE
ok: [test_13] =>
server_state.stdout: PASSIVE
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_states:
test_11: ACTIVE
test_13: PASSIVE
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_active:
- test_11
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_pasive:
- test_13
TASK [command] *******************************************************************************
changed: [test_11]
changed: [test_13 -> test_11]
TASK [command] *******************************************************************************
changed: [test_11 -> test_13]
changed: [test_13]
TASK [debug] *********************************************************************************
ok: [test_11] =>
msg: |-
test_11: [Shutdown active server] 2022-10-27 11:16:00.766309
test_13: [Shutdown passive server] 2022-10-27 11:16:02.501907
PLAY RECAP ***********************************************************************************
test_11: ok=8 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
From the description of your use case I understand that you like to perform tasks on certain servers which a have service role installed (annot.: Terracotta Server) and based on a certain service state.
Therefore, I like to recommend an approach with Custom facts.
Depending on if you have control about where the currentstate.log is placed or how it is structured, you could use in example something like
cat /tmp/ansible/service/terracotta.fact
[currentstate]
ACTIVE = true
PASSIVE = false
or add dynamic facts by adding executable scripts to facts.d ...
Means, alternatively, you can add the current service state to your host facts by creating and running a script in facts.d, which would just read the content of /tmp/currentstate.log.
Then, a sample playbook like
---
- hosts: localhost
become: false
gather_facts: true
fact_path: /tmp/ansible/service
gather_subset:
- "!all"
- "!min"
- "local"
tasks:
- name: Show Gathered Facts
debug:
msg: "{{ ansible_facts }}"
when: ansible_local.terracotta.currentstate.active | bool
will result into an output of
TASK [Show Gathered Facts] ******
ok: [localhost] =>
msg:
ansible_local:
terracotta:
currentstate:
active: 'true'
passive: 'false'
gather_subset:
- '!all'
- '!min'
- local
module_setup: true
An other approach is to address How the inventory is build and Group the hosts
[terracotta:children]
terracotta_active
terracotta_passive
[terracotta_active]
terracotta1.example.com
[terracotta_passive]
terracotta2.example.com
You can then just easily and simple define where a playbook or task should run, just by Targeting hosts and groups
ansible-inventory -i hosts--graph
#all:
|--#terracotta:
| |--#terracotta_active:
| | |--terracotta1.example.com
| |--#terracotta_passive:
| | |--terracotta2.example.com
|--#ungrouped:
ansible-inventory -i hosts terracotta_active --graph
#terracotta_active:
|--terracotta1.example.com
or Conditionals based on ansible_facts, in example
when: 'terracotta_active' in group_names
... from my understanding, both would be minimal and simple solutions without re-implementing functionality which seems to be already there.

Play recap and ignored=1 in Ansible

I am checking whether all services related to a docker-compose.yml are running on a system. The code snippet shown below.
---
- name:
shell: docker-compose ps -q "{{ item }}"
register: result
ignore_errors: yes
The code above works as expected. I have to ignore errors otherwise Ansible will not complete. The following result shows ignored=1
PLAY RECAP *******************************************************************************************************
192.168.50.219 : ok=38 changed=12 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
If this completes successfully I want to run a subsequent playbook but don't know how to specify ignored=1 correctly.
---
- name:
include_tasks: do_other_things.yml
when: ignored is false
How do I get the result from PLAY RECAP into something I can test with?
A better idea than trying to cope with the error returned by docker compose ps when the container does not exist would be to use the purposed module: docker_container_info to achieve the same.
Given the playbook:
- hosts: localhost
gather_facts: no
tasks:
- docker_container_info:
name: "{{ item }}"
register: containers
loop:
- node1 # exists
- node404 # does not exists
- debug:
msg: "`{{ item.item }}` is not started"
loop: "{{ containers.results }}"
loop_control:
label: "{{ item.item }}"
when: not item.exists
This would yield:
TASK [docker_container_info] *************************************************
ok: [localhost] => (item=node1)
ok: [localhost] => (item=node404)
TASK [debug] *****************************************************************
skipping: [localhost] => (item=node1)
ok: [localhost] => (item=node404) =>
msg: `node404` is not started

Ansible working with multiple loops in conditions

how can I add a condition to ansible task that is based on a loop, when the task itself also based on a loop?
For example, here's my code:
- hosts: all
gather_facts: False
vars:
current_version: 826
versions:
- 805
- 821
- 824
- 826
tasks:
- name: First Task
find:
paths: /Users/tomer/projects/personal/ansible/test
patterns: snapshot*
register: files
when:
- current_version == item
loop: "{{versions}}"
- name: Second task
set_fact:
test_work: "{{ true if item > 0 else false}}"
loop:
- "{{ files | json_query('results[*].matched') }}"
So far, this is working as expected.
The first task is looking for any file with the name snapshot if the current_version is matching one of the versions in the list.
The second task iterates over the dictionary result from the first task and based on each item it is setting the fact. (In my case, only one item has this attribute).
I wanted to run the second task, only when the first task did run, however, the changed status is always false, so this condition is not useful.
I wanted to add the same condition of current_version == item but I can't use item twice here.
Any idea how to achieve that?
The find command is not really going to change anything, it just queries the file system without doing any modification, so it will indeed always give you a false.
On the other hand, you can definitely use the skipped field of the item.
This said, I would simplify the loop on your set_fact, because there is no real need to use json_query here.
This task would do the job perfectly fine:
- set_fact:
test_work: "{{ item.matched > 0 }}"
loop: "{{ files.results }}"
when: item is not skipped
Another extra tip is to not do things like
true if condition_that_evaluates_to_true else false
But rather do right away
condition_that_evaluates_to_true
Here would be a made up example playbook
- hosts: all
gather_facts: no
tasks:
- find:
path: /tmp
pattern: dummy*
when: item == current_version
register: files
loop: "{{ versions }}"
vars:
current_version: 826
versions:
- 805
- 821
- 824
- 826
- debug:
msg: "{{ item.matched > 0 }}"
loop: "{{ files.results }}"
when: item is not skipped
loop_control:
label: "{{ item.item }}"
This would yield the result
PLAY [all] *******************************************************************************************************
TASK [find] ******************************************************************************************************
skipping: [localhost] => (item=805)
skipping: [localhost] => (item=821)
skipping: [localhost] => (item=824)
ok: [localhost] => (item=826)
TASK [debug] *****************************************************************************************************
skipping: [localhost] => (item=805)
skipping: [localhost] => (item=821)
skipping: [localhost] => (item=824)
ok: [localhost] => (item=826) => {
"msg": true
}
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How do I handle rollback in case of failure using handlers in Ansible?

I wrote below yml file which will install the SSM and cloudwatch agent but I want to rollback the installation in case of any failures during the installation. I tried use FAIL but not working..Please advise..
---
# tasks file for SSMAgnetInstall
- name: status check
command: systemctl status amazon-ssm-agent
register: s_status
- debug:
msg: "{{ s_status }}"
- name: Get CPU architecture
command: getconf LONG_BIT
register: cpu_arch
changed_when: False
check_mode: no
when: s_status.stdout == ""
ignore_errors: true
- name: Install rpm file for Redhat Family (Amazon Linux, RHEL, and CentOS) 32/64-bit
yum:
name: "https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_386/amazon-ssm-agent.rpm"
state: present
when: s_status.stdout == ""
become: yes
ignore_errors: true
- name: cloud status check
command: systemctl status amazon-cloudwatch-agent
register: cld_status
become: yes
- debug:
msg: "{{ cld_status }}"
- name: Register to cloud watch service
become: yes
become_user: root
service:
name: amazon-ssm-agent
enabled: yes
state: started
- name: copy the output to a local file
copy:
content: "{{ myshell_output.stdout }}"
dest: "/home/ansible/rama/output.txt"
delegate_to: localhost
You should have a look at the the documentation on blocks, more specifically the error handling part. This is the general idea with an oversimplified example, you will have to adapt to your specific case.
The test.yml playbook
---
- hosts: localhost
gather_facts: false
tasks:
- block:
- name: I am a task that can fail
debug:
msg: "I {{ gen_fail | default(false) | bool | ternary('failed', 'succeeded') }}"
failed_when: gen_fail | default(false) | bool
- name: I am a task that will never fail
debug:
msg: I succeeded
rescue:
- name: I am a task in a block played when a failure happens
debug:
msg: rescue task
always:
- name: I am a task always played whatever happens
debug:
msg: always task
Played normally (no fail)
$ ansible-playbook test.yml
PLAY [localhost] ************************************************************************
TASK [I am a task that can fail] ********************************************************
ok: [localhost] => {
"msg": "I succeeded"
}
TASK [I am a task that will never fail] *************************************************
ok: [localhost] => {
"msg": "I succeeded"
}
TASK [I am a task always played whatever happens] ***************************************
ok: [localhost] => {
"msg": "always task"
}
PLAY RECAP ******************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Played forcing a fail
$ ansible-playbook test.yml -e gen_fail=true
PLAY [localhost] ************************************************************************
TASK [I am a task that can fail] ********************************************************
fatal: [localhost]: FAILED! => {
"msg": "I failed"
}
TASK [I am a task in a block played when a failure happens] *****************************
ok: [localhost] => {
"msg": "rescue task"
}
TASK [I am a task always played whatever happens] ***************************************
ok: [localhost] => {
"msg": "always task"
}
PLAY RECAP ******************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0

Condition based execution in ansible

I have a scenario if first condition is successful then execute condition two and if condition two is successful then execute condition three.
Below is what i tried.
vi testme.yml
---
- hosts: all
tasks:
- name: run this command and ignore the result
shell: "grep -ci Hello /tmp/data.out"
register: pingout
ignore_errors: yes
- debug: msg="{{ pingout.rc }}"
- name: run the server if Hello is found in the above task
command: echo "The server is UP since `uptime`"
register: output1
when: pingout.rc == 0
- debug: "{{ output1.stdout }}"
When the string is found I was expecting to see this executed and shown in the output: The server is UP since uptime
However, I do not see this printed in the output.
ansible-playbook -i /tmp/myhost /root/testme.yml
Output:
PLAY [all] ******************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [95.76.121.113]
TASK [run this command and ignore the result] *******************************************************************************************************************************
changed: [95.76.121.113]
TASK [debug] ****************************************************************************************************************************************************************
ok: [95.76.121.113] => {
"msg": "0"
}
TASK [run the server if Hello is found in the above task] *******************************************************************************************************************
changed: [95.76.121.113]
TASK [debug] ****************************************************************************************************************************************************************
ok: [95.76.121.113] => {
"msg": "Hello world!"
}
PLAY RECAP ******************************************************************************************************************************************************************
95.76.121.113 : ok=5 changed=2 unreachable=0 failed=0
You do not need to check rc. Ansible knows, when a command failed.
---
- hosts: localhost
connection: local
tasks:
- name: First
shell: "true"
register: first
ignore_errors: yes
- name: Second
shell: "true"
register: second
ignore_errors: yes
when: first is not failed
- name: Third
shell: "true"
register: third
ignore_errors: yes
when: second is not failed
Correct syntax is
- debug: msg="{{ output1.stdout }}"
, or
- debug: var=output1.stdout

Resources