Run a handler play and exit play if rc == 0 in ansible - ansible

Hi is there a way I can run a handler play then exit the play if rc == 0. It can only do is exit the play using failed_when and proceed if rc != 0. I can't make notify: Service Guard execute. Been play with other approach like creating 2 play notify and exit no luck.
- name: Exit SG server from play
command: /usr/local/cmcluster/bin/cmversion
register: sg_check
notify: Service Guard
failed_when: sg_check.rc == 0
Here is new code I tried
- name: Check if Service Gurad then exit
command: /usr/local/cmcluster/bin/cmversion
register: sg_check
notify: Service Guard
changed_when: sg_check.rc == 0
ignore_errors: true
- meta: end_play
when: sg_check.rc == 0
but I get this:
ERROR! The conditional check 'sg_check.rc == 0' failed. The error was: error while evaluating conditional (sg_check.rc == 0): 'sg_check' is undefined
The error appears to have been in '/home/ansible/linuxpatchingv2/roles/applyPatch/tasks/main.yml': line 9, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
ignore_errors: true
meta: end_play
^ here

Q: "Run a handler play then exit the play if rc == 0"
A: This is not possible to accomplish in one task because a task can't be both changed and failed at the same time. These two actions must be split. For example, in the playbook below the command will succeed, notify the handler, and end the play
shell> cat pb.yml
- hosts: localhost
gather_facts: false
tasks:
- command: "{{ cmd|default('/bin/true') }}"
register: sg_check
notify: Service Guard
changed_when: sg_check.rc == 0
ignore_errors: true
- meta: end_play
when: sg_check.rc == 0
- debug:
msg: Continue
handlers:
- name: Service Guard
debug:
msg: Service Guard notified
gives (abridged)
shell> ansible-playbook pb.yml
...
RUNNING HANDLER [Service Guard] ****
ok: [localhost] =>
msg: Service Guard notified
PLAY RECAP ****
localhost: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The same playbook will continue if the command fails. For example
shell> ansible-playbook pb.yml -e "cmd=/bin/false"
PLAY [localhost] ****
TASK [command] ****
fatal: [localhost]: FAILED! => changed=false
cmd:
- /bin/false
delta: '0:00:00.003035'
end: '2020-08-24 09:33:22.039762'
msg: non-zero return code
rc: 1
start: '2020-08-24 09:33:22.036727'
stderr: ''
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
TASK [debug] ****
ok: [localhost] =>
msg: Continue
PLAY RECAP ****
localhost: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1

Related

How to make Ansible ignore changes in certain lines in file

I have a regular ubuntu VM. My ansible playbook is the following
---
- hosts: linux
tasks:
- name: "Creating a file file.txt"
become: yes
copy: src=./file.txt dest=~/file.txt
notify: restart some_app
handlers:
- name: restart some_app
become: yes
service:
name=some_app
state=restarted
And the contents of my file are
First line
Second line
After creating my VM I apply my ansible playbook. Then I am doing some work on ubuntu VM which could change the contents of file.txt (in Ubuntu VM, not in host). And when my work is done I apply anible playbook again. But I want to restart some_app only when changes are made to the First line in my file and ignore any changes made to the Second line (during some work in ubuntu VM). For example:
If after my work I have (on Ubuntu VM)
First line
Changed Second line
I want ansible to do nothing to this file (I don't want it to be replaced by initial file.txt on my host) and I don't want some_app to be started.
But if I have
Changed First line
Changed Second line
I want ansible to replace only First line and restart some_app, so after applying ansible playbook I would have
First line
Changed Second line
Is it possible to create Second line only once and then make ansible ignore it completly? How can I make this possible using ansible playbook? I can't split my file.txt into two different files or smth like this.
You can create backup and diff the first lines of the files.
For example, if the destination does not exist the module copy reports changed in the registered dictionary. The attribute backup_file is undefined. In this case, changed_when evaluates to True and the handler is notified
- copy:
src: file.txt
dest: ~/file.txt
backup: true
register: copy_result
changed_when:
- copy_result is changed
- copy_result.backup_file is undefined
notify: restart some_app
If the destination exists you want to notify the handler if the first line changes. Create the block below to test the first line. The tasks in the block will be executed only if a backup file is created
- block:
- script:
cmd: '{{ playbook_dir}}/diff_first_line {{ copy_result.backup_file }}'
executable: /bin/bash
register: diff_result
ignore_errors: true
changed_when: diff_result.stdout_lines.0|d('') == '1c1'
notify: restart some_app
- debug:
var: diff_result
when: debug|d(false)|bool
when:
- copy_result.backup_file is defined
Create the script below. The script returns the diff between the first lines of the destination ~/file.txt and the backup file
shell> cat diff_first_line
diff <(head -n 1 ~/file.txt) <(head -n 1 $1)
exit 0
Create the handler for testing
handlers:
- name: restart some_app
debug:
msg: First line of ~/file.txt changed
Example of a complete playbook for testing
shell> cat pb.yml
- hosts: test_24
tasks:
- copy:
src: file.txt
dest: ~/file.txt
backup: true
register: copy_result
changed_when:
- copy_result is changed
- copy_result.backup_file is undefined
notify: restart some_app
- debug:
var: copy_result
when: debug|d(false)|bool
- block:
- script:
cmd: '{{ playbook_dir}}/diff_first_line {{ copy_result.backup_file }}'
executable: /bin/bash
register: diff_result
ignore_errors: true
changed_when: diff_result.stdout_lines.0|d('') == '1c1'
notify: restart some_app
- debug:
var: diff_result
when: debug|d(false)|bool
when:
- copy_result.backup_file is defined
handlers:
- name: restart some_app
debug:
msg: First line of ~/file.txt changed
If the destination does not exist the file will be created and the handler will be notified
shell> ansible-playbook pb.yml
PLAY [test_24] *******************************************************************************
TASK [copy] **********************************************************************************
changed: [test_24]
TASK [debug] *********************************************************************************
skipping: [test_24]
TASK [script] ********************************************************************************
skipping: [test_24]
TASK [debug] *********************************************************************************
skipping: [test_24]
RUNNING HANDLER [restart some_app] ***********************************************************
ok: [test_24] =>
msg: First line of ~/file.txt changed
PLAY RECAP ***********************************************************************************
test_24: ok=2 changed=1 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0
shell> ssh admin#test_24 sudo cat /root/file.txt
First line
Second line
If the destination exists and the first line changed
shell> ssh admin#test_24 sudo cat /root/file.txt
First line changed
Second line
The script in the block will compare the first lines of the files and will notify the handler. Running the playbook with the variable debug=true gives
shell> ansible-playbook pb.yml -e debug=true
PLAY [test_24] *******************************************************************************
TASK [copy] **********************************************************************************
ok: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
copy_result:
backup_file: /root/file.txt.1520674.2023-01-05#11:00:41~
changed: false
checksum: e23b0534d0708167023aba6f65ef83afe38d52df
dest: /root/file.txt
diff: []
failed: false
gid: 0
group: root
md5sum: cbfa34ef619be185bfbe2b8c8f104d97
mode: '0644'
owner: root
secontext: system_u:object_r:admin_home_t:s0
size: 23
src: /home/admin/.ansible/tmp/ansible-tmp-1672934439.424632-1552928-229786621684419/source
state: file
uid: 0
TASK [script] ********************************************************************************
changed: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
diff_result:
changed: true
failed: false
rc: 0
stderr: |-
Shared connection to test_24 closed.
stderr_lines:
- Shared connection to test_24 closed.
stdout: |-
1c1
< First line
---
> First line changed
stdout_lines:
- 1c1
- < First line
- '---'
- '> First line changed'
RUNNING HANDLER [restart some_app] ***********************************************************
ok: [test_24] =>
msg: First line of ~/file.txt changed
PLAY RECAP ***********************************************************************************
test_24: ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The handler won't be notified if the first line doesn't change
shell> ssh admin#test_24 sudo cat /root/file.txt
First line
Second line changed
shell> ansible-playbook pb.yml -e debug=true
PLAY [test_24] *******************************************************************************
TASK [copy] **********************************************************************************
ok: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
copy_result:
backup_file: /root/file.txt.1522339.2023-01-05#11:02:50~
changed: false
checksum: e23b0534d0708167023aba6f65ef83afe38d52df
dest: /root/file.txt
diff: []
failed: false
gid: 0
group: root
md5sum: cbfa34ef619be185bfbe2b8c8f104d97
mode: '0644'
owner: root
secontext: system_u:object_r:admin_home_t:s0
size: 23
src: /home/admin/.ansible/tmp/ansible-tmp-1672934567.8696983-1553096-127236704493791/source
state: file
uid: 0
TASK [script] ********************************************************************************
ok: [test_24]
TASK [debug] *********************************************************************************
ok: [test_24] =>
diff_result:
changed: false
failed: false
rc: 0
stderr: |-
Shared connection to test_24 closed.
stderr_lines:
- Shared connection to test_24 closed.
stdout: ''
stdout_lines: []
PLAY RECAP ***********************************************************************************
test_24: ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Or condition is not working with set_facts in my playbook

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.

Need to skip failures in ansible so that script continues to execute but want to get failed count at the end

I am new to ansible and have a scenario where my ansible script should execute completely even if some
conditional failure occurs.
At the end of execution ,I want to check how many condition failed as failed count.
PLAY RECAP **********************************************************************************************************************************************************************************************
10.0.*.*** : ok=0 changed= unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Below is what i tried:
I am running a ansible play which is calling a role.
- name: Checking access
hosts: cluster
tasks:
- name: role call
include_role:
name: url_access
If a condition fails in the playbook ,it should continue with execution but in the end failed count should show the number of failures.
The main.yml of role is as follows:
- name: Access to google.com
shell: >
curl -s -o /dev/null -w "%{http_code}" google.com
ignore_errors: yes
register: access1
- debug:
msg: "Access Failed to google.com"
when: access1.stdout!="200"
- debug:
msg: "Access Successful to google.com"
when: access1.stdout=="200"
- name: Access to redhat.com
shell: >
curl -s -o /dev/null -w "%{http_code}" redhat.com
ignore_errors: yes
register: access2
- debug:
msg: "Access Failed to redhat.com"
when: access2.stdout!="301"
- debug:
msg: "Access Successful to redhat.com"
when: access2.stdout=="301"
This is the output I am getting
TASK [url_access : Access to google.com] ************************************************************************************************************************************************
changed: [10.0.*.***]
changed: [10.0.*.***]
TASK [url_access : debug] *******************************************************************************************************************************************************************************
skipping: [10.0.*.***]
skipping: [10.0.*.***]
TASK [url_access : debug] *******************************************************************************************************************************************************************************
ok: [10.0.*.***] => {
"msg": "Access Successful to google.com"
}
ok: [10.0.*.***] => {
"msg": "Access Successful to google.com"
}
TASK [url_access : Access to redhat.com] ************************************************************************************************************************************************
changed: [10.0.*.***]
changed: [10.0.*.***]
TASK [url_access : debug] *******************************************************************************************************************************************************************************
ok: [10.0.*.***] => {
"msg": "Access Failed to redhat.com"
}
ok: [10.0.*.***] => {
"msg": "Access Failed to redhat.com"
}
TASK [url_access : debug] *******************************************************************************************************************************************************************************
skipping: [10.0.*.***]
skipping: [10.0.*.***]
PLAY RECAP **********************************************************************************************************************************************************************************************
10.0.*.*** : ok=2 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
10.0.*.*** : ok=2 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
I used 'ignore_errors: yes' so my playbook is getting executed fully but in the end skipped count is coming while I wanted to have failed count.
Is there any way so that my script executes fully, skipping the failures but in the end I get failure count instead of skipped?
Q: "Is there any way so that my script executes fully, skipping the failures but in the end, I get failure count instead of skipped?"
A: Use blocks and count the number of errors in the rescue sections. For example
shell> cat playbook.yml
- hosts: localhost
tasks:
- block:
- command: /bin/true
rescue:
- set_fact:
errs: "{{ errs|default(0)|int + 1 }}"
- block:
- command: /bin/false
rescue:
- set_fact:
errs: "{{ errs|default(0)|int + 1 }}"
- block:
- command: /bin/false
rescue:
- set_fact:
errs: "{{ errs|default(0)|int + 1 }}"
- debug:
var: errs|default(0)|int
gives
errs|default(0)|int: '2'

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\$'

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

Resources