Why register is registering even if the module when condition is false? - ansible

I was having a task file which will get container name which has particular volume mounted. The result is stored in container_id variable. This task file need to be executed in linux as well windows. So i added conditional execution but the 'register' variable is behaving weird.
- name: Get the container ID for volume "{{vol}}" mounted
shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
when: ansible_os_family != "Windows"
- name: Get the container ID for volume "{{vol}}" mounted
win_shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
when: ansible_os_family == "Windows"
- debug:
var: container_id
When i run this in linux node , i thought it will get all the container name in container_id variable. But to my surprise the output is
ok: [remotenode] => {
"container_id": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
which means the skipped output from windows module is registered into container_id variable.
Then i placed the debug below linux module as below , which ensured that register at that point of time has proper value
- name: Get the container ID for volume "{{vol}}" mounted
shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
when: ansible_os_family != "Windows"
- debug:
var: container_id
- name: Get the container ID for volume "{{vol}}" mounted
win_shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
when: ansible_os_family == "Windows"
The output is as below
ok: [remotenode] => {
"container_id": {
"changed": true,
"cmd": "docker ps -a -q --filter volume=origvolfd48c6",
"delta": "0:00:00.036279",
"end": "2019-07-30 02:32:13.203036",
"failed": false,
"rc": 0,
"start": "2019-07-30 02:32:13.166757",
"stderr": "",
"stderr_lines": [],
"stdout": "fb81938cdbbe",
"stdout_lines": [
"fb81938cdbbe"
]
}
}
So the register is having value even if the module is skipped. Why it behaves this way. Is there any workaround?

If you take a look at the "Using variables" section of Ansible's Docs you'll see this note:
If a task fails or is skipped, the variable still is registered with a failure or skipped status, the only way to avoid registering a variable is using tags.
So what is happening to you is Ansible's default behaviour. You could modify your playbook to use tags like this:
- name: Get the container ID for volume "{{vol}}" mounted
shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
tags:
- linux
- name: Get the container ID for volume "{{vol}}" mounted
win_shell: "docker ps -a -q --filter volume={{vol}}"
register: container_id
tags:
- windows
- debug:
var: container_id
Then you can run it using the --tags or --skip-tags options.

Related

Why does Ansible think this shell command has a non-zero return code?

I have this recipe:
- name: Kill usr/bin/perl -w /usr/share/debconf/frontend
shell: "{{ item }}"
with_items:
- 'pkill -9 -f debconf/frontend || echo'
changed_when: false # Don't report changed state
When I run this on the command line, it returns a 0 exit code thanks to || echo:
~$ pkill -9 -f debconf/frontend || echo
~$ $?
0
However running in Ansible it shows this:
failed: [testhost1] (item=pkill -9 -f debconf/frontend || echo) => {"ansible_loop_var": "item", "changed": false, "cmd": "pkill -9 -f debconf/frontend || echo", "delta": "0:00:00.132296", "end": "2023-01-13 10:34:11.098765", "item": "sudo pkill -9 -f debconf/frontend || echo", "msg": "non-zero return code", "rc": -9, "start": "2023-01-13 10:34:10.966469", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
Any ideas what I'm doing wrong?
Starting a test process via
perl -MPOSIX -e '$0="test"; pause' &
a minimal example playbook
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Shell task
shell:
cmd: "{{ item }}"
with_items:
- 'pkill -9 -f test || echo'
register: result
- name: Show result
debug:
msg: "{{ result }}"
reproduce the issue. Using instead
with_items:
- 'pkill -9 -f test'
or
with_items:
- 'kill -9 $(pgrep test) || echo'
will execute without an error.
Links
How can I start a process with any name which does nothing?
How to kill a running process using Ansible?
Similar Q&A
with the same error message
Ansible: How force kill process ...?
Further Reading
Since it is recommended to use Ansible modules instead of shell commands if possible
pids module – Retrieves process IDs list if the process is running otherwise return empty list
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Get Process ID (PID)
pids:
name: test
register: PID
- name: Show PID
debug:
msg: "{{ PID.pids | first }}"
- name: Shell task
shell:
cmd: "kill -9 {{ PID.pids | first }}"
register: result
- name: Show result
debug:
msg: "{{ result }}"
Ah, this is because running pkill from inside of Ansible causes it to kill itself (bug in pkill?)
In any case, a workaround is to do this:
with_items:
- 'if pgrep -f "debconf/frontend" | grep -v $$; then sudo pkill -9 -f "debconf/frontend"; fi'

Ansible run a task based on something in output

I'm new in Ansible and I try to do some practice playbook. I write a playbook to deploy a Docker container as follows. I write a task in the block section if an error happened, run a task in the rescue section based on Failed message content. For example, I want to run a specific task to delete an existing container if the Failed message is something like this:
failed: [192.168.1.140] (item=stderr_lines) => {"changed": true, "cmd": ["docker", "run", "-itd", "--name", "h1", "-p", "80:80", "httpd"], "delta": "0:00:00.016385", "end": "2021-01-04 03:00:55.403364", "failed_when_result": true, "item": "stderr_lines", "msg": "non-zero return code", "rc": 125, "start": "2021-01-04 03:00:55.386979", "stderr": "/usr/bin/docker-current: Error response from daemon: Conflict. The container name "/h1" is already in use by container bc5cc803a5f4321358992d06097ce271f3a63b8eba19900cfc0d23e321a4e243. You have to remove (or rename) that container to be able to reuse that name..\nSee '/usr/bin/docker-current run --help'.", "stderr_lines": ["/usr/bin/docker-current: Error response from daemon: Conflict. The container name "/h1" is already in use by container bc5cc803a5f4321358992d06097ce271f3a63b8eba19900cfc0d23e321a4e243. You have to remove (or rename) that container to be able to reuse that name..", "See '/usr/bin/docker-current run --help'."], "stdout": "", "stdout_lines": []}"
My playbook is as follows but it didn't work correctly. Sometimes an error in reading dictionary or "Unexpected templating type error occurred on". Can somebody guide me on what should I write?
- name: run a container
vars:
- run_container: docker run -itd --name h1 -p 80:80 httpd
- rm_container: docker stop h1 && docker rm h1
hosts: 192.168.x.x
tasks:
- name: check docker container
block:
- name: run a container httpd
command: "{{run_container}}"
register: rss
with_items:
- "stderr_lines"
failed_when: "'Error' in rss.stderr"
rescue:
- name: iterate over list
debug:
msg: "{{item.value}}"
loop: "{{rss | dict2items}}"
- name: remove the exited container
command: "{{rm_container}}"
register: rs
with_items:
- "{{rss | dict2items}}"
when: item.value is search("The container name .* is already in use")

Ansible - return code of commands launch into a loop from machines variables

I have some serious struggle with my Ansible tasks right now, i think i need a little bit of help ...
The background :
I have multiple servers in my Ansible inventory, and i want to be able to launch a playbook that check the status of the services running on the servers, without making specific tasks for each services.
I have configure the machines variables to look like this (for example) :
services:
apache-custom:
cmd: ps aux | grep /bin/custom-httpd | ps -efl | grep custom-owner | grep -v grep
return_code: 0
restart: echo restart
mysqld:
cmd: 0
And the playbook is currently the following :
- hosts: all
tasks:
- name: Check native services status
service:
name: "{{ item.key }}"
state: started
with_dict: "{{ services }}"
when: item.value.cmd == 0
- name: Check others services
shell: "{{ item.value.cmd }}"
register: result
with_dict: "{{ services }}"
failed_when: result.rc != item.value.return_code
when: item.value.cmd != 0 and item.value.cmd != ''
ignore_errors: yes
For the services defined with 'cmd: 0', the first task is going to check the status of the service, and launch it if not started.
But for some custom services, the 'service' module cannot help me, so i need to launch command in order to know if the service is start.
The second task is working in this case, but i'm unable to grab the return code of that task in order to launch the task that is going to start the service.
I tried to add this task :
- name: test
debug:
msg: ({{ item.item.key }}) failed
when: item.rc != 0
with_items: "{{ result.results }}"
But Ansible is complaining about the fact that a 'dict' value don't have any return code.
I tried to debug, and i don't understand what's going on.
For example, when i want to see the content of the global result of my tasks, i add this :
- name: test2
debug:
msg: "{{ item }}"
with_items: "{{ result.results }}"
it gave me the following output (that contain the return code that i'm looking for) :
[...]
"failed_when_result": false,
"invocation": {
"module_args": {
"_raw_params": "ps aux | grep /bin/custom-httpd | ps -efl | grep custom_owner | grep -v grep",
"_uses_shell": true,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": {
"key": "apache-custom",
"value": {
"cmd": "ps aux | grep /bin/custom_httpd | ps -efl | grep custom-owner | grep -v grep",
"restart": "echo restart",
"return_code": 0
}
},
"rc": 0, <====== here is the return code
"start": "2018-07-09 15:44:24.859555",
"stderr": "",
"stderr_lines": [],
[...]
But i can't reach it ...
Any idea how to do that ?
Or am i doing something wrong ?
Thanks
"To be able to launch a playbook that check the status of the services running on the servers" you might want to consider the module service_facts.
- name: 'Test service_facts'
hosts: localhost
connection: local
tasks:
- name: populate service facts
service_facts:
- debug:
var: ansible_facts.services

How to make changed_when work in Ansible?

I am trying to run a script using the command module on a Jenkins server. The script is written in such a way that it should return 0 if not making any configuration changes and the Ansible task should not be changed.
Here is the code:
- name: Script to run
command: java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 groovy "{{ jenkins_home }}/userContent/script.groovy"
register: return_code
changed_when: return_code.stdout != 0
But the above code behaves is always showing as changed.
The Ansible output:
TASK [jenkins : Script to run] ********************************
changed: [test] => {"changed": true, "cmd": ["java", "-jar", "/var/cache/jenkins/war/WEB-INF/jenkins-cli.jar", "-s", "http://localhost:8080", "groovy", "/var/lib/jenkins/userContent/script.groovy"], "delta": "0:00:01.547098", "end": "2017-02-06 15:31:05.989134", "rc": 0, "start": "2017-02-06 15:31:04.442036", "stderr": "[WARN] Failed to authenticate with your SSH keys. Proceeding as anonymous", "stdout": "0", "stdout_lines": ["0"], "warnings": []}
You need to compare the stdout value with a string instead of an integer:
- name: Script to run
command: java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080 groovy "{{ jenkins_home }}/userContent/script.groovy"
register: script_call
changed_when: script_call.stdout != "0"

Ansible and `changed_when` based on `stdout` value

I am running a custom command because I haven't found a working module doing what I need, and I want to adjust the changed flag to reflect the actual behaviour:
- name: Remove unused images
shell: '[ -n "$(docker images -q -f dangling=true)" ] && docker rmi $(docker images -q -f dangling=true) || echo Ignoring failure...'
register: command_result
changed_when: "command_result.stdout == 'Ignoring failure...'"
- debug: var="1 {{ command_result.stdout }}"
when: "command_result.stdout != 'Ignoring failure...'"
- debug: var="2 {{ command_result.stdout }}"
when: "command_result.stdout == 'Ignoring failure...'"
(I know the shell command is ugly and could be improved by a more complex script but I don't want to for now)
Running this task on an host where no Docker image can be removed gives the following output:
TASK: [utils.dockercleaner | Remove unused images] ****************************
changed: [cloud-host] => {"changed": true, "cmd": "[ -n \"$(docker images -q -f dangling=true)\" ] && docker rmi $(docker images -q -f dangling=true) || echo Ignoring failure...", "delta": "0:00:00.064451", "end": "2015-07-30 18:37:25.620135", "rc": 0, "start": "2015-07-30 18:37:25.555684", "stderr": "", "stdout": "Ignoring failure...", "stdout_lines": ["Ignoring failure..."], "warnings": []}
TASK: [utils.dockercleaner | debug var="DIFFERENT {{ command_result.stdout }}"] ***
skipping: [cloud-host]
TASK: [utils.dockercleaner | debug var="EQUAL {{ command_result.stdout }}"] ***
ok: [cloud-host] => {
"var": {
"EQUAL Ignoring failure...": "EQUAL Ignoring failure..."
}
}
So, I have this stdout return value "stdout": "Ignoring failure...", and the debug task shows the strings are equal, so why is the task still displayed as "changed"?
I am using ansible 1.9.1.
The documentation I am refering to is this one: http://docs.ansible.com/ansible/playbooks_error_handling.html#overriding-the-changed-result
I think you may have misinterpreted what changed_when does.
changed_when marks the task as changed based on the evaluation of the conditional statement which in your case is:
"command_result.stdout == 'Ignoring failure...'"
So whenever this condition is true, the task will be marked as changed.

Resources