I am working on a ansible playbook for my servers (mix of ubuntu and centos) and when trying to transfer some config files for monit if a program is installed I am running into an issue. It works perfectly on my centos machines but the ubuntu one's it transfers the template no matter what completely ignoring the conditional.
---
- name: Check for Sendmail (Ubuntu)
shell: dpkg-query -W -f='${Status} ${Version}\n' sendmail
register: ubuntu_installed
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
changed_when: False
failed_when: "'FAILED' in ubuntu_installed.stderr"
- debug: var=ubuntu_installed
- name: Check for Sendmail (CentOs)
shell: rpm -qa | grep sendmail
register: cent_installed
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'
changed_when: False
failed_when: "'FAILED' in cent_installed.stderr"
- name: Transfer Monit config files for Sendmail (Ubuntu)
template: src=monit/templates/sendmail.j2 dest=/etc/monit/conf.d/sendmail owner=root group=root mode=644
when: ubuntu_installed.skipped is not defined and ubuntu_installed.stdout != "" and ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Transfer Monit config files for Sendmail (CentOs)
template: src=monit/templates/sendmail.j2 dest=/etc/monit.d/sendmail owner=root group=root mode=644
when: cent_installed.skipped is not defined and cent_installed.stdout != "" and ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'
I have the debug in there to check to make sure the variable is right. When I run the playbook I get...
TASK: [monit | debug var=ubuntu_installed] ************************************
ok: [server1] => {
"item": "",
"ubuntu_installed": {
"changed": false,
"cmd": "dpkg-query -W -f='${Status} ${Version}\\n' sendmail ",
"delta": "0:00:00.012985",
"end": "2014-07-11 16:56:12.688509",
"failed": false,
"failed_when_result": false,
"invocation": {
"module_args": "dpkg-query -W -f='${Status} ${Version}\\n' sendmail",
"module_name": "shell"
},
"item": "",
"rc": 1,
"start": "2014-07-11 16:56:12.675524",
"stderr": "dpkg-query: no packages found matching sendmail",
"stdout": "",
"stdout_lines": []
}
}
It is completely ignoring the conditional ubuntu_installed.stdout != ""
Here's your conditional:
- when: ubuntu_installed.skipped is not defined and ubuntu_installed.stdout != "" and ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
Your precedence is incorrect: A and B and C or D means is (A and B and C) or D, which is not what you want. (note Ansible conditionals use Jinja2 expressions, which reflect Python conditionals, so here are the precedence rules).
Lazy programmers add parenthesis for clarity; here's the revised conditional:
- when: (ubuntu_installed.skipped is not defined and ubuntu_installed.stdout != "") and (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu')
PS- you showed the output to one command, debug, but it's helpful to give alllll the output. That's ansible-playbook -vvv.
Related
Could you please help me with identifying the error
'dict object' has no attribute 'stdout'
which I am getting while running an Ansible playbook. Please see the error below:
{"msg": "The conditional check '(result.stdout == 1) and (ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat')' failed. The error was: error while evaluating conditional ((result.stdout == 1) and (ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat')): 'dict object' has no attribute 'stdout'\n\nThe error appears to be in '/etc/ansible/playbooks/dev_patching_security_upgrade_version2.yml': line 47, 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: Reboot CentOS Server if Necessary\n ^ here\n"}
I am pasting the relevant part of the code below:
- name: check to see if we need a reboot in Centos
shell:
"needs-restarting; echo $?"
register: result
ignore_errors: yes
when: ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat'
- name: display result
debug:
var: result.stdout
when: ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat'
- name: Reboot CentOS Server if Necessary
command: shutdown -r now "Ansible Updates Triggered reboot"
become: true
async: 30
poll: 0
when: (result.stdout == 1) and (ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat')
Thanks in advance
This is because the result set might not have a dictionary key stdout. To debug a result set you may use
- name: Show result
debug:
var: result
Since the shell module already provides the return code within the Return Values, reboot checks are simple as
- name: Check if reboot_required
shell:
cmd: "needs-restarting -r"
changed_when: false
failed_when: reboot_required.rc != 0 and reboot_required.rc != 1
check_mode: false
register: reboot_required
- name: Report reboot_required
debug:
msg: "{{ reboot_required.rc | bool }} "
changed_when: reboot_required.rc == 1
check_mode: false
and resulting into an output of in example
TASK [Report reboot_required] **************************************************
ok: [test1.example.com] => {
"msg": "True "
}
ok: [test2.example.com] => {
"msg": "False "
}
You could dense down the tasks to one by using one
needs-restarting -r || /usr/sbin/shutdown -r now "Ansible Updates Triggered reboot"
Further Q&A
How to determine if system needs a reboot?
Ansible - How to reboot the server based on condition?
I'm using https://github.com/kubernetes-sigs/kubespray
Is there a way to know exactly which command the following Ansible code will execute on the remote server?
- name: ensure docker packages are installed
package:
name: "{{ docker_package_info.pkgs }}"
state: "{{ docker_package_info.state | default('present') }}"
module_defaults:
apt:
update_cache: true
dnf:
enablerepo: "{{ docker_package_info.enablerepo | default(omit) }}"
yum:
enablerepo: "{{ docker_package_info.enablerepo | default(omit) }}"
zypper:
update_cache: true
register: docker_task_result
until: docker_task_result is succeeded
retries: 4
delay: "{{ retry_stagger | d(3) }}"
notify: restart docker
when:
- not ansible_os_family in ["Flatcar Container Linux by Kinvolk"]
- not is_ostree
- docker_package_info.pkgs|length > 0
I'm getting the following error in RHEL8. I want to run manually docker-ce on the relevant VM and check the error.
{"attempts": 4, "changed": true, "cmd": "set -o pipefail && /usr/bin/docker ps -aq | xargs -r docker rm -fv", "delta": "0:00:00.008934", "end": "2021-07-16 18:46:54.009010", "msg": "non-zero return code", "rc": 127, "start": "2021-07-16 18:46:54.000076", "stderr": "/bin/bash: /usr/bin/docker: No such file or directory", "stderr_lines": ["/bin/bash: /usr/bin/docker: No such file or directory"], "stdout": "", "stdout_lines": []}
Thanks,
My Ansible conditional statements are not evaluating correctly.
- name: A
shell:
cmd: /usr/local/bin/is_A_OK #returns bash true or false (not strings)
register: is_A_OK
- name: B
shell:
cmd: /usr/local/bin/is_B_OK
register: is_B_OK
- name: reboot if both are OK
reboot:
when:
- is_A_OK
- is_B_OK
[DEPRECATION WARNING]: evaluating 'is_A_ok' as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle. This feature will be removed in version 2.12.
However, the logic works correctly, and the reboot is performed when both variables are true. But I can't leave it as-is given that this feature will be removed in version 2.12.
FYI, the bash scripts conclude like this:
if [[ "$my_var" == true ]]; then
true
else
false
fi
I'm running Arch Linux, so bash is new.
The warning plus the ansible docs made me think this would be correct:
- name: reboot if OK
reboot:
when:
- is_A_OK|bool
- is_B_OK|bool
The warning goes away, but the reboot is not preformed, even when both variables are true. I guess I don't understand the docs. (I'm new to Ansible.)
I found this question, but it is not relevant:
Ansible conditional statements not evaluating correctly - Stack Overflow
Ansible conditional statements not evaluating correctly
What am I doing wrong and not understanding?
Short answer: Either test the attribute rc of the registered dictionary or use task results to test success.
Details: The return code of the command is stored in the attribute rc of the registered dictionary. The command
- command: /bin/true
register: result_A
- debug:
var: result_A.rc
- debug:
msg: "{{ (result_A is success)|ternary('OK', 'KO') }}"
- debug:
msg: "{{ result_A|ternary('OK', 'KO') }}"
returns rc=0 as a result of /bin/true. Use task results to test success. Testing of bare variable result_A gives True because the variable is not empty. It also produces the deprecation warning.
"result_A.rc": "0"
"msg": "OK"
"msg": "OK"
In the case of /bin/false the command
- command: /bin/false
register: result_B
ignore_errors: true
- debug:
var: result_B.rc
- debug:
msg: "{{ (result_B is success)|ternary('OK', 'KO') }}"
- debug:
msg: "{{ result_B|ternary('OK', 'KO') }}"
returns rc=1 and the task would fail without ignore_errors: true.
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.003322", "end": "2020-06-14 07:07:17.620345", "msg": "non-zero return code", "rc": 1, "start": "2020-06-14 07:07:17.617023", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
The test success fails and testing of result_B gives True because the variable is not empty.
"result_B.rc": "1"
"msg": "KO"
"msg": "OK"
I want to execute my Ansible task only on a specific SUSE version. But this task gets executed for all Linux OS flavors. Which is not expected. Can someone help me with this? Why is the when condition not working? What I am doing wrong?
Code:
# Action 146: addlink ld-lsb.so.3->ld-2.11.1.so in /lib on sles11.x,12.x for lmutil in cct2000739233
- name: Addlink ld-lsb.so.3->ld-2.11.1.so in /lib on sles11.x,12.x for lmutil
shell: "ls ld-*.so|grep -v lsb|head -n 1"
args:
chdir: /lib
register: ldso
- stat:
path: /lib64/ld-lsb-x86-64.so.3
register: lib64_result
- stat:
path: /lib/ld-lsb.so.3
register: lib_result
- block:
- file:
src: "/lib/{{ ldso.stdout }}"
dest: /lib/ld-lsb.so.3
state: link
force: true
- file:
src: /lib64/ld-linux-x86-64.so.2
dest: /lib64/ld-lsb-x86-64.so.3
state: link
force: true
when:
# Check the OS level. Make sure it runs only on SLES-11 SP4,SLES-12SP0/1/2/3
- ansible_distribution == 'Suse'
- ansible_distribution_major_version == "11"
- ansible_distribution_release == "4"
- ansible_distribution_version == "11.4"
- ansible_distribution == 'SLES'
- ansible_distribution_major_version == "12"
- ansible_distribution_release == "0" or "1" or "2" or "3"
- ansible_distribution_version == "12.0" or "12.1" or "12.2" or "12.3"
- not lib64_result.stat.exists|bool or not lib_result.stat.exists|bool
- ldso.stdout != ''
- debug:
msg: "{{ ldso.stdout }}"
Cannot do or like that. Has to be like this:
- ansible_distribution_release == "0" or ansible_distribution_release == "1" or ansible_distribution_release == "2" or ansible_distribution_release == "3"
- ansible_distribution_version == "12.0" or ansible_distribution_version == "12.1" or ansible_distribution_version == "12.2" or ansible_distribution_version == "12.3"
The way you have it, ansible_distribution_release == "0" or "1", ansible_distribution_release == "0" evaluates to false, that's fine, but then "1" evaluates to true. false or true then evaluates to true.
Team,
I am trying to match a version and that fails when whole string does not match. so I just want to match first two octects. I tried several combons but no luck.
- name: "Validate k8s version"
shell: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_version_live.stdout_lines is not search("{{ k8s_server_version }}")
#failed_when: "'{{ k8s_server_version }}' not in k8s_version_live.stdout_lines"
ignore_errors: yes
- debug:
var: k8s_version_live.stdout_lines
output:
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: k8s_version_live.stdout_lines is not search("{{
k8s_server_version }}")
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "kubectl version --short", "delta": "0:00:00.418128", "end": "2019-12-05 02:13:15.108997", "failed_when_result": true, "rc": 0, "start": "2019-12-05 02:13:14.690869", "stderr": "", "stderr_lines": [], "stdout": "Client Version: v1.13.3\nServer Version: v1.13.10", "stdout_lines": ["Client Version: v1.13.3", "Server Version: v1.13.10"]}
...ignoring
TASK [team-services-pre-install-checks : debug] *************************************************************************************************************************
Thursday 05 December 2019 02:13:15 +0000 (0:00:00.902) 0:00:01.039 *****
ok: [localhost] => {
"k8s_version_live.stdout_lines": [
"Client Version: v1.13.3",
"Server Version: v1.13.10"
]
}```
As the error says:
conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}.
In a conditional statement, you are already inside a Jinja template context. You can just refer to variables by name:
- name: "Validate k8s version"
shell: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_version_live.stdout_lines is not search(k8s_server_version)
ignore_errors: yes
Although you probably want k8s_version_live.stdout instead of k8s_version_live.stdout_lines.
I would probably write the task as:
- name: "Validate k8s version"
command: "kubectl version --short"
register: k8s_version_live
failed_when: k8s_server_version not in k8s_version_live.stdout
ignore_errors: true
Q: "Match a version ... match first two octets"
A: Use Version Comparison. For example, create the variable k8s_server_version from the registred output
- set_fact:
k8s_server_version: "{{ k8s_version_live.stdout_lines.1.split(':').1[2:] }}"
Compare the first two numbers of the version
- debug:
msg: "{{ k8s_server_version }} match 1.13"
when:
- k8s_server_version is version('1.13', '>=')
- k8s_server_version is version('1.14', '<')
gives
}
localhost | SUCCESS => {
"msg": "1.13.10 match 1.13"
}
Fail when the version does not match
- fail:
msg: "{{ k8s_server_version }} does not match 1.12"
when: not (k8s_server_version is version('1.12', '>=') and
k8s_server_version is version('1.13', '<'))
gives
localhost | FAILED! => {
"changed": false,
"msg": "1.13.10 does not match 1.12"
}