Categories
DevOps

How to access ansible facts regardless of the target limits

Access required ansible facts regardless of the target limits on a particular run.

The inventory

Create an inventory file. I will use database group in the following examples.

$ cat hosts.yml
---
database:
  hosts:
    beaver:
      ansible_host: 172.16.40.11
    wombat:
      ansible_host: 172.16.40.12
    rabbit:
      ansible_host: 172.16.40.13

Create playbook

Create standard_playbook.yml that will be used to illustrate the issue.

$ cat standard_playbook.yml
---                                                                                                                                                                
- hosts: all                                                                                                                                                       
  tasks:                                                                                                                                                           
    - name: "List default IPv4 address"                                                                                                                            
      debug:                                                                                                                                                       
        msg: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"                                                                               
                                                                                                                                                                   
    - name: "List default IPv4 addresses in database group"                                                                                                        
      debug:                                                                                                                                                       
        msg: "{% for host in groups['database'] %}{{ hostvars[host]['ansible_eth0']['ipv4']['address'] | d() }}{% if not loop.last and hostvars[host]['ansible_eth0']['ipv4']['address'] is defined %}, {% endif %}{% endfor %}"   

Display default IPv4 addresses.

$ ansible-playbook -i hosts.yml standard_playbook.yml
PLAY [all] *************************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************************************************************************************
ok: [beaver]
ok: [rabbit]
ok: [wombat]

TASK [List default IPv4 address] ***************************************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11"
}
ok: [wombat] => {
    "msg": "172.16.40.12"
}
ok: [rabbit] => {
    "msg": "172.16.40.13"
}

TASK [List default IPv4 addresses in database group] *******************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}
ok: [wombat] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}
ok: [rabbit] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************************************************************************
beaver                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
rabbit                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
wombat                     : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

It works as expected in this simple case.

The issue

Define target limits to see the issue as the service will be accidentally reconfigured.

$ ansible-playbook -i hosts.yml standard_playbook.yml --limit beaver,rabbit
PLAY [all] *************************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************************************************************************************
ok: [beaver]
ok: [rabbit]

TASK [List default IPv4 address] ***************************************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11"
}
ok: [rabbit] => {
    "msg": "172.16.40.13"
}

TASK [List default IPv4 addresses in database group] *******************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11, 172.16.40.13"
}
ok: [rabbit] => {
    "msg": "172.16.40.11, 172.16.40.13"
}

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

The list of default IPv4 addresses in the database group does not include wombat hostname as facts were not collected for it due to limits imposed on the inventory.

The solution

Define yaml as facts caching plugin and yaml_fact_cache caching directory. yaml will use YAML format, specify jsonfile to use JSON format.

$ cat ansible.cfg
[defaults]
fact_caching = yaml
fact_caching_connection = yaml_fact_cache

Define an empty playbook to simply gather facts.

$ cat gather_facts.yml
---
- hosts: all
  tasks: []

Update playbook to skip facts gathering.

$ cat updated_playbook.yml
---
- hosts: all
  gather_facts: no
  tasks:
    - name: "List default IPv4 address"
      debug:
        msg: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"

    - name: "List default IPv4 addresses in database group"
      debug:
        msg: "{% for host in groups['database'] %}{{ hostvars[host]['ansible_eth0']['ipv4']['address'] | d() }}{% if not loop.last and hostvars[host]['ansible_eth0
']['ipv4']['address'] is defined %}, {% endif %}{% endfor %}"

Skip this step for now, but delete gathered facts in subsequent runs.

$ find yaml_fact_cache -type f -delete

Gather facts.

$ ansible-playbook -i hosts.yml gather_facts.yml
PLAY [all] *************************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************************************************************************************
ok: [beaver]
ok: [rabbit]
ok: [wombat]

PLAY RECAP *************************************************************************************************************************************************************************************************************************************************************************
beaver                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
rabbit                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
wombat                     : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Run playbook using gathered facts.

$ ansible-playbook -i hosts.yml updated_playbook.yml
PLAY [all] *************************************************************************************************************************************************************************************************************************************************************************

TASK [List default IPv4 address] ***************************************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11"
}
ok: [wombat] => {
    "msg": "172.16.40.12"
}
ok: [rabbit] => {
    "msg": "172.16.40.13"
}

TASK [List default IPv4 addresses in database group] *******************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}
ok: [wombat] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}
ok: [rabbit] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************************************************************************
beaver                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
rabbit                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
wombat                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Run playbook with target limits using gathered facts.

$ ansible-playbook -i hosts.yml updated_playbook.yml --limit beaver,rabbit
PLAY [all] *************************************************************************************************************************************************************************************************************************************************************************

TASK [List default IPv4 address] ***************************************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11"
}
ok: [rabbit] => {
    "msg": "172.16.40.13"
}

TASK [List default IPv4 addresses in database group] *******************************************************************************************************************************************************************************************************************************
ok: [beaver] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}
ok: [rabbit] => {
    "msg": "172.16.40.11, 172.16.40.12, 172.16.40.13"
}

PLAY RECAP *************************************************************************************************************************************************************************************************************************************************************************
beaver                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
rabbit                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

The list of default IPv4 addresses in the database group is created correctly as facts were gathered for every host in this group.

Additional notes

List available fact cache plugins.

$ ansible-doc -t cache -l
jsonfile  JSON formatted files
memcached Use memcached DB for cache
memory    RAM backed, non persistent
mongodb   Use MongoDB for caching
pickle    Pickle formatted files
redis     Use Redis DB for cache
yaml      YAML formatted files

Read more about cache plugins.

I will anticipate your question about using a single playbook to perform both actions. The answer is that you can put everything in a single playbook. Play with it and have fun!