1

I have a Jinja template where I'm trying to extract the master and replica hosts IPs like this:

  <node>
      <host>{{ hostvars[master|default(ansible_hostname|lower)]['ansible_default_ipv4']['address'] }}</host>
      <port>7800</port>
  </node>
  <node>
      <host>{{ hostvars[replica|default(ansible_hostname|lower)]['ansible_default_ipv4']['address'] }}</host>
      <port>7800</port>
  </node>

That works perfect if I run this job on both the master and replica hosts at the same time.
But, if there is a change I have a handler that restart the service. And I don't want to restart both nodes at the same time. So, I run the playbook in serial mode.

This is where my problem lies: I don't get the ansible_default_ipv4 from both the master and replica hosts when I'm running only one of them, in serial.

The solution I am working on, now, is to run a pre_task:

- pre_tasks:
  - name: Run setup
    ansible.builtin.setup:
      filter:
        - all_ipv4_addresses
    delegate_to: "{{ item }}"
    loop:
      - "{{ master }}"
      - "{{ replica }}"
    register: ip_v4

Then set facts.

  - name: Set facts
    set_fact:
      master_ip:  "{{ip_v4.results[0]}}"
      replica_ip: "{{ip_v4.results[1]}}"

And from here I'm stuck. Because the value in ip_v4.results[0] contains a lot of info. And I only need the IP.

Or is there a better way to solve this issue?

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83

2 Answers2

0

You can run a first play just to gather the hosts facts, then run your actual play.
This way the hostvars of the "other" hosts will be pre-populated.

For example, with the inventory:

all:
  hosts:
    master:
      ansible_host: ansible-node-1 ## one of my lab's hostname
    replica:
      ansible_host: ansible-node-2 ## another of my lab's hostname
## This plays will just gather the facts of the targeted hosts
- hosts: master, replica
  gather_subset:
    - all_ipv4_addresses

## This is the actual play running in serial
- hosts: master, replica
  serial: 1

  tasks:
    - debug:
        msg: |-
          IP of master: {{ hostvars.master.ansible_default_ipv4.address }}
          IP of replica: {{ hostvars.replica.ansible_default_ipv4.address }}

Running it would yield, as expected:

PLAY [master, replica] ********************************************

TASK [Gathering Facts] ********************************************
ok: [master]
ok: [replica]

PLAY [master, replica] ********************************************

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

TASK [debug] ******************************************************
ok: [master] => 
  msg: |-
    IP of master: 172.18.0.2
    IP of replica: 172.18.0.3

PLAY [master, replica] ********************************************

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

TASK [debug] ******************************************************
ok: [replica] => 
  msg: |-
    IP of master: 172.18.0.2
    IP of replica: 172.18.0.3
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
  • master and replica are values stored in host_vars, Because i have several "master and replica" with different ip's. So I cant put it in an inventory. And I can't ask DNS beacuse some of the hosts have different ip in the DNS contra host. So unfortunately this does not work – Magnus Persson Dec 28 '22 at 09:00
0

Enable cache. For example,

shell> grep fact_caching ansible.cfg
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_cache
fact_caching_prefix = ansible_facts_
fact_caching_timeout = 86400

Given the inventory

shell> ansible-inventory --graph --yaml
@all:
  |--@example_org:
  |  |--master
  |  |--replica
  |--@ungrouped:
  |  |--localhost

The playbook runs setup on all hosts and caches both the facts and the dictionary default_ipv4 created by set_fact

shell> cat pb.yml 
- hosts: all
  gather_facts: false

  tasks:

    - setup:
        gather_subset:
          - default_ipv4

    - set_fact:
        default_ipv4:
          master: "{{ hostvars.master.ansible_default_ipv4.address }}"
          replica: "{{ hostvars.replica.ansible_default_ipv4.address }}"
        cacheable: true
      run_once: true

    - debug:
        var: default_ipv4
      run_once: true

gives

shell> ansible-playbook pb.yml 

PLAY [all] ***********************************************************************************

TASK [setup] *********************************************************************************
ok: [localhost]
ok: [master]
ok: [replica]

TASK [set_fact] ******************************************************************************
ok: [localhost]

TASK [debug] *********************************************************************************
ok: [localhost] => 
  default_ipv4:
    master: 192.168.1.242
    replica: 10.1.0.61

PLAY RECAP ***********************************************************************************
localhost: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
master: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
replica: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

The facts including the dictionary default_ipv4 were cached

shell>tree /tmp/ansible_cache/
/tmp/ansible_cache/
├── ansible_facts_localhost
├── ansible_facts_master
└── ansible_facts_replica

The cache of all hosts from the inventory will be available also to a single host running the playbook. For example,

shell> ansible-playbook pb.yml -l master

PLAY [all] ***********************************************************************************

TASK [setup] *********************************************************************************
ok: [master]

TASK [set_fact] ******************************************************************************
ok: [master]

TASK [debug] *********************************************************************************
ok: [master] => 
  default_ipv4:
    master: 192.168.1.242
    replica: 10.1.0.61

PLAY RECAP ***********************************************************************************
master: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

If the facts are cached you can run the play with serial: 1

shell> cat pb.yml
- hosts: all
  serial: 1
  gather_facts: false
  ...

gives

shell> ansible-playbook pb.yml

PLAY [all] ***********************************************************************************

TASK [setup] *********************************************************************************
ok: [localhost]

TASK [set_fact] ******************************************************************************
ok: [localhost]

TASK [debug] *********************************************************************************
ok: [localhost] => 
  default_ipv4:
    master: 192.168.1.242
    replica: 10.1.0.61

PLAY [all] ***********************************************************************************

TASK [setup] *********************************************************************************
ok: [master]

TASK [set_fact] ******************************************************************************
ok: [master]

TASK [debug] *********************************************************************************
ok: [master] => 
  default_ipv4:
    master: 192.168.1.242
    replica: 10.1.0.61

PLAY [all] ***********************************************************************************

TASK [setup] *********************************************************************************
ok: [replica]

TASK [set_fact] ******************************************************************************
ok: [replica]

TASK [debug] *********************************************************************************
ok: [replica] => 
  default_ipv4:
    master: 192.168.1.242
    replica: 10.1.0.61

PLAY RECAP ***********************************************************************************
localhost: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
master: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
replica: ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Then, the creation of the XML file is trivial. The task

    - template:
        src: nodes.xml.j2
        dest: /tmp/nodes.xml
      run_once: true
      delegate_to: localhost
      vars:
        nodes: [master, replica]

and the template

shell> cat nodes.xml.j2
{% for host in nodes %}
<node>
  <host>{{ default_ipv4[host] }}</host>
  <port>7800</port>
</node>
{% endfor %}

give always the recent results for all hosts

shell> cat /tmp/nodes.xml 
<node>
  <host>192.168.1.242</host>
  <port>7800</port>
</node>
<node>
  <host>10.1.0.61</host>
  <port>7800</port>
</node>
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • master and replica are values stored in host_vars, Because i have several "master and replica" with different ip's. So I cant put it in an inventory. And I can't ask DNS beacuse some of the hosts have different ip in the DNS contra host. So unfortunately this does not work – Magnus Persson Dec 28 '22 at 09:00