Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using if condition to assign True or False to gather_facts in Ansible doesnt work

---
- hosts: "{{ run_on_node|default('mysql_cluster_sql[0]')}}"
  connection: "{% if migrated is defined and migrated == 'yes' %}local{% else %}ssh{% endif %}" # This works as we are assigning non boolean value
  gather_facts: "{% if migrated is defined and migrated == 'yes' %}false{% else %}true{% endif %}" #This doesnt work well
  tasks:
    - debug: var=ansible_all_ipv4_addresses
    - debug: var=ansible_default_ipv4.address

Inventory File:

[mysql_cluster_sql]
10.200.1.191 migrated=yes

The variable has the value as true and false based on condition but even when gather_facts is false, it gathers the facts.

like image 386
Shubham Saroj Avatar asked Jul 06 '21 12:07

Shubham Saroj


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.

How do you assign variables in ansible?

You can define these variables in your playbooks, in your inventory, in re-usable files or roles, or at the command line. You can also create variables during a playbook run by registering the return value or values of a task as a new variable.

How are facts collected in ansible?

Ansible facts are data gathered about target nodes (host nodes to be configured) and returned back to controller nodes. Ansible facts are stored in JSON format and are used to make important decisions about tasks based on their statistics. Facts are in an ansible_facts variable, which is managed by Ansible Engine.

How does facts gathering work in Ansible?

In Ansible, facts gathering is essentially an unwritten task. When it is turned on (the default) at the start of each play, each host will get a task to gather facts from it.

What is the ansible configs module?

This module takes care of executing the configured facts modules, the default is to use the ansible.builtin.setup module. This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks. It can also be executed directly by /usr/bin/ansible to check what variables are available to a host.

How do you use conditional statements in Ansible?

The simplest conditional statement applies to a single task. Create the task, then add a when statement that applies a test. 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.

How does Ansible test for multiple hosts?

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. For example, if you are installing mysql on multiple machines, some of which have SELinux enabled, you might have a task to configure SELinux to allow mysql to run.


Video Answer


2 Answers

Simplify and fix the condition. Use the default value. This will cover both tests, e.g.

shell> cat pb.yml
- hosts: localhost
  gather_facts: "{{ (migrated|default('no') == 'yes')|ternary(false, true) }}"
  tasks:
    - meta: noop

will gather facts without the variable migrated defined

shell> ansible-playbook pb.yml

PLAY [localhost] ********************************************************

TASK [Gathering Facts] **************************************************
ok: [localhost]

, or when the variable is set to other value than 'yes'

shell> ansible-playbook pb.yml -e migrated=no

PLAY [localhost] ********************************************************

TASK [Gathering Facts] **************************************************
ok: [localhost]

When the variable is set to 'yes' no facts will be gathered

shell> ansible-playbook pb.yml -e migrated=yes

PLAY [localhost] ********************************************************

PLAY RECAP **************************************************************

Jinja

If you insist on Jinja the playbook below gives the same results

shell> cat pb.yml
- hosts: localhost
  gather_facts: "{% if migrated|default('no') == 'yes' %}
                 false
                 {% else %}
                 true
                 {% endif %}"
  tasks:
    - meta: noop

Boolean

You can further simplify the test by explicit conversion to Boolean, e.g.

- hosts: localhost
  gather_facts: "{{ (migrated|default('no')|bool)|ternary(false, true) }}"
  tasks:
    - meta: noop

Truthy/Falsy

Make sure you understand how Boolean conversion and testing work. See results of the tasks

    - debug:
        msg: "True"
      loop: [yes, Yes, true, True, xxx]
      when: item|bool

    - debug:
        msg: "False"
      loop: [no, No, false, False, xxx]
      when: not item|bool

    - debug:
        msg: "{{ item|bool|ternary(True, False) }}"
      loop: [yes, Yes, true, True, xxx,
             no, No, false, False, xxx]

    - debug:
        msg: "{{ item|ternary(True, False) }}"
      loop: [yes, Yes, true, True, xxx,
             no, No, false, False, xxx]

Q: "Passing the variable 'migrated' from within the inventory does not work."

A: You're right. It seems that the inventory variables are not available at the time gather_facts is running. Use setup as a workaround. For example

- hosts: localhost
  gather_facts: false
  tasks:
    - setup:
      when: (migrated|default('no')|bool)|ternary(false, true)
like image 111
Vladimir Botka Avatar answered Oct 23 '22 00:10

Vladimir Botka


gather_facts will be evaluted before the play loop has started so ansible cannot know which group/host var it should load in this case. The problem is exactly the same for the connection attribute.

I only see one way to fulfill your requirement by gathering facts explicitly and setting the connection for each host. Ini format does not play well with this for the inventory so I transformed to yaml. I also modified your default hosts expression in playbook so that it directly get the host name from inventory. You can keep yours if it suits your needs if you wish.

Inventory:

---
all:
  children:
    mysql_cluster_sql:
      hosts:
        10.200.1.191:
          migrated: yes
      vars:
        ansible_connection: "{{ migrated | default(false) | bool | ternary('local', 'ssh') }}"

Playbook:

---
- hosts: "{{ run_on_node | default(groups['mysql_cluster_sql'][0]) }}"
  gather_facts: false

  tasks:

    - name: gather_facts if not migrated
      setup:
      when: not (migrated | default(false) | bool)

    - debug:
        var: ansible_all_ipv4_addresses

    - debug:
        var: ansible_default_ipv4.address
like image 3
Zeitounator Avatar answered Oct 23 '22 02:10

Zeitounator