Ansible: Using item(with_items) in changed_when statement - ansible

I'm using with_items iterator to execute command: brew services stop {{ item }}.
To handle errors I'd like to use changed_when and use item value in it.
command: brew services stop {{ item }}
register: stop_services
changed_when:
- "'Error: Service `{{ item }}` is not started.' not in stop_services.stderr"
with_items:
- memcached
- kafka
If service is not started, I get following error
failed: [127.0.0.1] (item=memcached) => {"changed": false, "cmd": ["brew", "services", "stop", "memcached"], "delta": "0:00:00.464519", "end": "2016-12-29 18:02:37.795973", "failed": true, "item": "memcached", "rc": 1, "start": "2016-12-29 18:02:37.331454", "stderr": "Error: Service memcached is not started.", "stdout": "", "stdout_lines": [], "warnings": []}
But changed_when statement is ignored.
Is it possible to inject item into changed_when statement?
Ansible version: 2.2

You've got a failing task in the first place. The command module will report failure if the return code from the command was other than zero (in your case "rc": 1) and that's why your changed_when condition is not taken into consideration.
Add the following to prevent failing:
failed_when: false

Related

How to check if task output list contains a string using filter or JMESPath?

I am writing a simple Ansible playbook (Ansible version - 2.9) to start a service and then want to extract certain fields – "changed" and "failed" – from the output for debugging purpose
These are my tasks:
- name: Start service if stopped
command: systemctl start confluent-*
register: confluent_start_status
ignore_errors: true
- debug:
var: "{{ confluent_start_status.cmd| json_query([?contains(#,'start') == 'true']) }}"
And this is the output of the start task.
{
"confluent_start_status": {
"changed": true,
"cmd": [
"systemctl",
"start",
"confluent-*"
],
"delta": "0:00:01.425703",
"end": "2023-01-31 14:14:22.567335",
"failed": false,
"rc": 0,
"start": "2023-01-31 14:14:21.141632",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
}
How can I check that the cmd list contains the string start and assign true to a variable if it does?
Basically I want to create a variable using set_fact, when changed is true, failed is false and cmd contains start, to use it in the next task.
How can I check that the cmd list contains the string start and assign true to a variable if it does?
With JMESPath:
- set_fact:
is_start: >-
{{
confluent_start_status
| json_query('contains(cmd, `start`)')
}}
With plain Jinja:
- set_fact:
is_start: "{{ 'start' in confluent_start_status.cmd }}"
As for your requirements to combine the facts that the task should raise a changed state, should not have failed and that the cmd should contain start, there are also tests for tasks results that makes is more human readable:
- set_fact:
confluence_task_status: >-
{{
'start' in confluent_start_status.cmd
and confluent_start_status is changed
and confluent_start_status is not failed
}}

non-zero return code from aptitude command run via Ansible

I'm getting an error when running the following Ansible task:
- name: List packages to upgrade (1/2)
shell: aptitude -q -F%p --disable-columns search "~U"
register: updates
changed_when: False
when: ansible_os_family == 'Debian'
Error:
TASK [List packages to upgrade (1/2)] ********************************************************************************************************************************
fatal: [php7e]: FAILED! => {"changed": false, "cmd": "aptitude -q -F%p --disable-columns search \"~U\"", "delta": "0:00:00.828791", "end": "2021-06-23 10:31:26.849961", "msg": "non-zero return code", "rc": 1, "start": "2021-06-23 10:31:26.021170", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
Noticed on another server, I don't get this error. The server that doesn't have an issue has aptitude 0.7.4 and the one with the errors has aptitude 0.8.10.
obviously aptitude search returns with an exit-code of 1 if no packages match the search term (in your case: if there are no upgradable packages)
as per the changelog:
aptitude (0.7.6-1) unstable; urgency=low
[...]
* [cmdline] "search" now exits with non-zero on errors or empty results
(Closes: #497299)
so: the observed behaviour is correct and expected.
you must update your ansible task accordingly.

How do I find which register attribute to use in Ansible?

I am testing out the results of the register commands and it yields different attribute for various tasks: failures, msg, stderr, err..etc
- yum:
name: packagenotfound
state: present
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"failed": true,
"failures": [
"No package packagenotfound available."
],
"msg": "Failed to install some of the specified packages",
"rc": 1,
"results": []
}}
And
- lvg:
pvs: /dev/sddnotfound
vg: vgdata
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"failed": true,
"msg": "Device /dev/sddnotfound not found."
}
And
- shell: thiscommandwontwork
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": true,
"cmd": "thiscommandwontwork",
"delta": "0:00:00.002560",
"end": "2020-02-05 04:24:35.297556",
"failed": true,
"msg": "non-zero return code",
"rc": 127,
"start": "2020-02-05 04:24:35.294996",
"stderr": "/bin/sh: thiscommandwontwork: command not found",
"stderr_lines": [
"/bin/sh: thiscommandwontwork: command not found"
],
"stdout": "",
"stdout_lines": []
}
And
- lvol:
lv: lvdata
vg: vgroup
size: 2000M
ignore_errors: yes
register: command_result
- debug:
msg: "{{ command_result }}"
ok: [ansible] => {
"msg": {
"changed": false,
"err": " Volume group \"vgroup\" not found\n Cannot process volume group vgroup\n",
"failed": true,
"msg": "Volume group vgroup does not exist.",
"rc": 5
}
Now if I tried to use when: '"xxx" in command_result.err' with yum task for example, it will result in dict_object not found error.
Is there a way to find out which attribute to use without testing?
Testing is definitely the easiest and fastest way to have a look at the content of your registered var in several situation and to take decisions on how to use it in your playbook.
Meanwhile, there are ways to have a global knowledge of what is returned in your registered var from the documenations:
There is a page on Common modules return values
Modules returning specific values usually document them on each relevant doc page. Here is an example for the stat module
You should also be aware that the global register structure is changed when using a loop by addind a top level results list, as explained in registering variables
Knowing what could be in your register does not mean it will be. Your example mentions the (undocumented...) err attribute for the lvol module, which will only be available for an lvol task in error. You can work around such cases by using tests (like my_register is failed) or defaulting values with the default filter.

Ansible fatal error while trying to print the state of firewalld

Here, i am trying to print the status of the firewall-cmd --state command , but a fatal error is being thrown.
name: Check firewall status
hosts: st
tasks:
- name: Check status of firewall
command: firewall-cmd --state
register: status
- name: Print version
debug:
msg: "Status = {{ status.stdout }}"
State is "not running" in the remote host. But am not getting the result.
I get the following output
fatal: [borexample.com]: FAILED! => {"changed": true, "cmd": ["firewall-cmd", "--state"], "delta": "0:00:00.189023", "end": "2018-09-16 11:40:17.319482", "msg": "non-zero return code", "rc": 252, "start": "2018-09-16 11:40:17.130459", "stderr": "", "stderr_lines": [], "stdout": "\u001b[91mnot running\u001b[00m", "stdout_lines": ["\u001b[91mnot running\u001b[00m"]}
How should i modify the code so that i get only the state ?
I prefer using failed_when: to control your output rc. More info at Ansible Documentation. But you can also use ìgnore_errors: true
Check error codes in the Firewall-cmd Documentation to see which codes adding to your playbook.
In your scenario could be good doing:
- name: Check status of firewall
command: firewall-cmd --state
register: status
failed_when:
- status.rc != 0
- status.rc != 252
Even you can go further and use failed_when: false to avoid the command failing.
The ignore_errors suggested by Baptiste Mille-Mathias would allow you to continue, but then you would like to "debug" {{ status.stderr }}, as in that ase stdout would be empty.

Ansible task fails, when creating extensions

While provisioning via Vagrant and Ansible I keep running into this issue.
TASK [postgresql : Create extensions] ******************************************
failed: [myapp] (item=postgresql_extensions) => {"changed": true, "cmd": "psql myapp -c 'CREATE EXTENSION IF NOT EXISTS postgresql_extensions;'", "delta": "0:00:00.037786", "end": "2017-04-01 08:37:34.805325", "failed": true, "item": "postgresql_extensions", "rc": 1, "start": "2017-04-01 08:37:34.767539", "stderr": "ERROR: could not open extension control file \"/usr/share/postgresql/9.3/extension/postgresql_extensions.control\": No such file or directory", "stdout": "", "stdout_lines": [], "warnings": []}
I'm using a railsbox.io generated playbook.
Turns out that railsbox.io is still using a deprecated syntax in the task.
- name: Create extensions
sudo_user: '{{ postgresql_admin_user }}'
shell: "psql {{ postgresql_db_name }} -c 'CREATE EXTENSION IF NOT EXISTS {{ item }};'"
with_items: postgresql_extensions
when: postgresql_extensions
The last line should use full jinja2 syntax.
when: '{{postgresql_extensions}}'

Resources