Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ansible: understanding a compound conditional when statement

Consider this trivial ansible playbook and associated output below. Why does task 5 get executed? These tasks were run against debian. Task 1 fails as expected. So, why does and'ing it with 'ansible_lsb.major_release|int < 14' make it true? Does this have something to do with operator precedence?

-jk

---
- name: These tests run against debian
  hosts: frontend001
  vars:
    - bcbio_dir: /mnt/bcbio
    - is_ubuntu: "'{{ansible_distribution}}' == 'Ubuntu'"
    - is_debian: "'{{ansible_distribution}}' == 'Debian'"
  tasks:
    - name: 1. Expect skip because test is_ubuntu
      debug: msg="ansible distribution - {{ansible_distribution}}, release - {{ansible_distribution_release}}, {{ ansible_lsb.major_release }}"
      when: is_ubuntu 

    - name: 2. Expect to print msg because test is_debian
      debug: msg="ansible distribution - {{ansible_distribution}}, release - {{ansible_distribution_release}}, {{ ansible_lsb.major_release }}"
      when: is_debian

    - name: 3. Expect to print msg because release 7 of wheezy
      debug: msg="ansible distribution - {{ansible_distribution}}, release - {{ansible_distribution_release}}, {{ ansible_lsb.major_release }}"
      when:  ansible_lsb.major_release|int < 14

    - name: 4. Expect to print msg because true and true is true
      debug: msg="ansible distribution - {{ansible_distribution}}, release - {{ansible_distribution_release}}, {{ ansible_lsb.major_release }}"
      when: is_debian and ansible_lsb.major_release|int < 14

    - name: 5. Expect to skip because false and true is false
      debug: msg="ansible distribution - {{ansible_distribution}}, release - {{ansible_distribution_release}}, {{ ansible_lsb.major_release }}"
      when: is_ubuntu and ansible_lsb.major_release|int < 14 


$ ansible-playbook -i ~/.elasticluster/storage/ansible-inventory.jkcluster  zbcbio.yml 

PLAY [These tests run against debian] ***************************************** 

GATHERING FACTS *************************************************************** 
ok: [frontend001]

TASK: [1. Expect skip because test is_ubuntu] ********************************* 
skipping: [frontend001]

TASK: [2. Expect to print msg because test is_debian] ************************* 
ok: [frontend001] => {
    "msg": "ansible distribution - Debian, release - wheezy, 7"
}

TASK: [3. Expect to print msg because release 7 of wheezy] ******************** 
ok: [frontend001] => {
    "msg": "ansible distribution - Debian, release - wheezy, 7"
}

TASK: [4. Expect to print msg because true and true is true] ****************** 
ok: [frontend001] => {
    "msg": "ansible distribution - Debian, release - wheezy, 7"
}

TASK: [5. Expect to skip because false and true is false] ********************* 
ok: [frontend001] => {
    "msg": "ansible distribution - Debian, release - wheezy, 7"
}

PLAY RECAP ******************************************************************** 
frontend001                : ok=5    changed=0    unreachable=0    failed=0   

Edited: Listing the changes based on tedder42's answer below in case someone is following along at home.

1) Changed

- is_ubuntu: "'{{ansible_distribution}}' == 'Ubuntu'"

to

- is_ubuntu: "{{ansible_distribution == 'Ubuntu'}}"

2) change

when: is_ubuntu and ansible_lsb.major_release|int < 14 

to

when: is_ubuntu|bool and ansible_lsb.major_release|int < 14 

That did it!

-jk

like image 465
John Avatar asked Oct 03 '14 23:10

John


People also ask

What is when condition in Ansible?

The when clause is a raw Jinja2 expression without double curly braces (see group_by_module). When you run the task or playbook, Ansible evaluates the test for all hosts. On any host where the test passes (returns a value of True), Ansible runs that task.

Which keyword do you use to create a conditional task in Ansible?

To implement conditions in Ansible, we use the when keyword. The keyword takes Boolean expressions based on a value or a variable from previous tasks or facts gathered from the remote hosts.

Can we use if else in Ansible playbook?

Traditional programming language usually uses the if-else statement when more than one outcome is expected. In Ansible, 'when' statement is used instead to determine the outcome of a variable. So instead of using the if-else statement, you define what you want to happen.


1 Answers

TLDR: your variable is being output as a string, not evaluated. Fix the evaluation using jinja2 and then filter the var as |bool.

Debugging

You're only missing one thing to debug this problem. Here's what I ran on my local OSX box:

- name: stackoverflow 26188055
  hosts: local
  vars:
    - bcbio_dir: /mnt/bcbio
    - is_ubuntu: "'{{ansible_distribution}}' == 'Ubuntu'"
    - is_debian: "'{{ansible_distribution}}' == 'Debian'"
  tasks:
    - debug: var=is_ubuntu
    - debug: var=is_debian
    - debug: msg="this shows the conditional passes even though it shouldnt"
      when: is_ubuntu and true

And the output:

TASK: [debug var=is_ubuntu] *************************************************** 
ok: [127.0.0.1] => {
    "is_ubuntu": "'MacOSX' == 'Ubuntu'"
}

TASK: [debug var=is_debian] *************************************************** 
ok: [127.0.0.1] => {
    "is_debian": "'MacOSX' == 'Debian'"
}

TASK: [debug msg="this shows the conditional passes even though it shouldnt"] *** 
ok: [127.0.0.1] => {
    "msg": "this shows the conditional passes even though it shouldnt"
}

Evaluating

As far as I know, you can't really evaluate down to a boolean. Typically this is done by unrolling the variable (placing it in every "when"). However, it can be accomplished as you can get a boolean as a string, then cast it to a bool as hinted on the Variables ansible page (search for "boolean value").

- name: stackoverflow 26188055
  hosts: local
  vars:
    - bcbio_dir: /mnt/bcbio
    - is_ubuntu: "{{ansible_distribution == 'Ubuntu'}}"
    - is_debian: "{{ansible_distribution == 'Debian'}}"
  tasks:
    - debug: var=is_ubuntu
    - debug: var=is_debian
    - debug: msg="this shows the conditional passes even though it shouldnt"
      when: is_ubuntu|bool and true

And here's the output.

TASK: [debug var=is_ubuntu] *************************************************** 
ok: [127.0.0.1] => {
    "is_ubuntu": "False"
}

TASK: [debug var=is_debian] *************************************************** 
ok: [127.0.0.1] => {
    "is_debian": "False"
}

TASK: [debug msg="this shows the conditional passes even though it shouldnt"] *** 
skipping: [127.0.0.1]

Version comparison filter

Note you may want to take advantage of Ansible's version_compare filter. Usage is left as an exercise to the reader.

like image 131
tedder42 Avatar answered Oct 27 '22 00:10

tedder42