Search code examples
dynamicansibleinventory

Replacing variable from vars/main.yml into ansible inventory


I am moving a lot of old scripts used to configure a computer room into ansible, and it really has improved the workflow. Currently, there I have several playbooks, and I need to share a common config among them. But in one task I have faced a problem: I need a hostname/ip to be a variable in the inventory. I have read a lot of tutorials and docs and maybe I am dumb or very tired, but I have not found yet a solution after many hours, it seems that it is not possible. Dynamics inventories, group_vars and so on look similar but actually are different from what I require here. I have created a mwe to easy showing the case. This mwe is a subset but the main idea remains: vars inside vars/main.yml are going to be shared among various playbooks (easy) and inventories (the question here). Thanks in advance.

  • ansible.cfg:
[ssh_connection]
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

[defaults]
interpreter_python = python3
  • inventory (ini format, also tried yaml)
[master]
192.168.10.1 ansible_connection=local

[package_clients]
192.168.10.[3:29]

[package_server]
{{PACKAGE_SERVER}} # <--- This var commes from vars/main.yml
  • site.yaml:
---
- name: Configuring package server (exports spack packages)
  hosts: package_server
  gather_facts: false
  vars_files:
    - vars/main.yml
  tasks:

  - name: Printing package server name
    debug:
      msg: "PACKAGE_SERVER  variable -> {{PACKAGE_SERVER}}"

  - name: Creating package dir (this throws an error sin PACKAGE_SERVER is not replaced in inventory)
    file:
      path: /packages
      state: directory
  • vars/main.yml
# Server internal ip
BASE_SERVERIP: '192.168.10'
# Package Server: Simply shares a directory with spack and packages
PACKAGE_SERVER: '{{BASE_SERVERIP}}.2'

When I run this as

ansible-playbook -i inventory site.yml --check

I get the following error:

PLAY [Configuring package server (exports spack packages)] *******************************************************************************************
TASK [Printing package server name] ******************************************************************************************************************
ok: [{{PACKAGE_SERVER}}] => {    "msg": "PACKAGE_SERVER  variable -> 192.168.10.2"
}
TASK [Creating package dir (this throws an error sin PACKAGE_SERVER is not replaced in inventory)] ***************************************************fatal: [{{PACKAGE_SERVER}}]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname {{package_server}}: Name or service not known", "unreachable": true}
PLAY RECAP *******************************************************************************************************************************************
{{PACKAGE_SERVER}}         : ok=1    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

Solution

  • Use the module add_host and create new group package_server in the first play. Then use it in the second play. For example

    shell> cat playbook.yml
    ---
    - name: Create group package_server
      hosts: localhost
      gather_facts: false
      vars:
        BASE_SERVERIP: 192.168.10
        PACKAGE_SERVER: "{{ BASE_SERVERIP }}.2"
      tasks:
        - add_host:
            hostname: "{{ PACKAGE_SERVER }}"
            groups: package_server
    
    - name: Use group package_server
      hosts: package_server
      gather_facts: false
      tasks:
        - debug:
            msg: Creating package dir
    

    gives

    shell> ansible-playbook playbook.yml
    
    PLAY [Create group package_server] **************************************
    
    TASK [add_host] *********************************************************
    changed: [localhost]
    
    PLAY [Use group package_server] *****************************************
    
    TASK [debug] ************************************************************
    ok: [192.168.10.2] => 
      msg: Creating package dir
    
    PLAY RECAP **************************************************************
    192.168.10.2: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   
    localhost   : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 
    

    List of hosts

    If you want to add more hosts to the group create a list. The expansion of slices, e.g. 192.168.10.[3:6], works only in the inventory. Use filters to declare such range in a list, e.g.

    - name: Create group package_server
      hosts: localhost
      gather_facts: false
      vars:
        clients: "{{ ['192.168.10']|
                     product(range(3,6))|
                     map('join', '.')|
                     list }}"
      tasks:
        - debug:
            var: clients
        - add_host:
            hostname: "{{ item }}"
            groups: package_server
          loop: "{{ clients }}"
    

    creates the list below and add the hosts into the group package_server

    clients:
      - 192.168.10.3
      - 192.168.10.4
      - 192.168.10.5
    

    Any octet can be declared as a sequence, e.g.

        c:
          A: [192]
          B: [168]
          C: [10, 12]
          D: [3, 5]
        clients: "{{ range(c.A.0, c.A.1|d(c.A.0 + 1))|
                     product(range(c.B.0, c.B.1|d(c.B.0 + 1)))|map('flatten')|
                     product(range(c.C.0, c.C.1|d(c.C.0 + 1)))|map('flatten')|
                     product(range(c.D.0, c.D.1|d(c.D.0 + 1)))|map('flatten')|
                     map('join', '.')|list }}"
    

    gives

    clients:
      - 192.168.10.3
      - 192.168.10.4
      - 192.168.11.3
      - 192.168.11.4