gk_2000
gk_2000

Reputation: 351

How to pair hosts with variables in ansible

I have an inventory like:

all:
  children:
    server_group1:
      hosts:
        host1:
    server_group2:
      children:
        app1:
          hosts:
            host2:
            host3:
        app2:
          hosts:
            host4:
            host5:
    server_group3:
...

I have organized my server variables like so:

I am trying to name my dict after the group (thus making them unique) and access it in my playbook:

hosts: server_group2
tasks:
  - name: check file
    local_action: stat path=path/to/test/{{hostvars[0].name1}}
    register: payld_txt

  - name: conditional transfer
    copy:
      src: path/to/test/{{hostvars[0].name1}}
      dest: /svr/path/{{hostvars[0].name2}}
    when: payld_txt.stat.exists

I end up with this error:

The task includes an option with an undefined variable. The error was: 'name1' is undefined

Where am I going wrong?

Upvotes: 2

Views: 9052

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68189

Given the inventory (and the group_vars)

shell> ansible-inventory -i hosts --list --yaml
all:
  children:
    server_group1:
      hosts:
        host1: {}
    server_group2:
      children:
        app1:
          hosts:
            host2:
              app1: &id001
                name1: value1
                name2: value2
              app2: &id002
                name1: value11
                name2: value22
            host3:
              app1: *id001
              app2: *id002
        app2:
          hosts:
            host4:
              app1: *id001
              app2: *id002
            host5:
              app1: *id001
              app2: *id002
    ungrouped: {}

'hostvars[0].name1' The error was: 'name1' is undefined

Q: "Where am I going wrong?"

A: name1 is an attribute of the dictionary app1 or app2. It must be referenced either app1.name1 or app2.name1. In addition to this, hostvars is a dictionary, not an array. hostvars[0] does not exist. An attribute in a dictionary must be referenced by a key. For example,

  my_keys: "{{ hostvars.keys() }}"

gives

  my_keys: [host1, host2, host3, host4, host5]

You can use the keys in a loop. For example,

    - debug:
        msg: "{{ hostvars[item].app1.name1 }}"
      loop: "{{ my_keys }}"
      when: item in groups.app1
    - debug:
        msg: "{{ hostvars[item].app2.name1 }}"
      loop: "{{ my_keys }}"
      when: item in groups.app2

gives

TASK [debug] *********************************************************************************
skipping: [host2] => (item=host1) 
ok: [host2] => (item=host2) => 
  msg: value1
ok: [host2] => (item=host3) => 
  msg: value1
skipping: [host2] => (item=host4) 
skipping: [host2] => (item=host5) 

TASK [debug] *********************************************************************************
skipping: [host2] => (item=host1) 
skipping: [host2] => (item=host2) 
skipping: [host2] => (item=host3) 
ok: [host2] => (item=host4) => 
  msg: value11
ok: [host2] => (item=host5) => 
  msg: value11

Optionally use json_query to create the list of the keys

  my_keys: "{{ hostvars|dict2items|json_query('[].key') }}"


The simplified version of the debug tasks

    - debug:
        msg: "{{ hostvars[inventory_hostname].app1.name1 }}"
      when: inventory_hostname in groups.app1
    - debug:
        msg: "{{ hostvars[inventory_hostname].app2.name1 }}"
      when: inventory_hostname in groups.app2

gives

TASK [debug] *********************************************************************************
ok: [host2] => 
  msg: value1
skipping: [host4]
ok: [host3] => 
  msg: value1
skipping: [host5]

TASK [debug] *********************************************************************************
skipping: [host2]
skipping: [host3]
ok: [host4] => 
  msg: value11
ok: [host5] => 
  msg: value11

In fact, addressing hostvars[inventory_hostname] is not necessary. The simplified tasks below give the same result

    - debug:
        msg: "{{ app1.name1 }}"
      when: inventory_hostname in groups.app1
    - debug:
        msg: "{{ app2.name1 }}"
      when: inventory_hostname in groups.app2

Example of a complete playbook for testing

- hosts: server_group2

  vars:

    my_keys: "{{ hostvars.keys() }}"

  tasks:

    - block:

        - debug:
            var: hostvars[0]

        - debug:
            var: my_keys|to_yaml

        - debug:
            msg: "{{ hostvars[item].app1.name1 }}"
          loop: "{{ my_keys }}"
          when: item in groups.app1
        - debug:
            msg: "{{ hostvars[item].app2.name1 }}"
          loop: "{{ my_keys }}"
          when: item in groups.app2

      run_once: true

    - debug:
        msg: "{{ hostvars[inventory_hostname].app1.name1 }}"
      when: inventory_hostname in groups.app1
    - debug:
        msg: "{{ hostvars[inventory_hostname].app2.name1 }}"
      when: inventory_hostname in groups.app2

    - debug:
        msg: "{{ app1.name1 }}"
      when: inventory_hostname in groups.app1
    - debug:
        msg: "{{ app2.name1 }}"
      when: inventory_hostname in groups.app2

Upvotes: 3

Zeitounator
Zeitounator

Reputation: 44799

Before you go any further, you need to fix your inventory which does not respect ansible's structure for yaml sources. A simple command as the following can give you some hints:

$ ansible -i inventories/test.yml all --list-hosts
 [WARNING]: Skipping unexpected key (server_group1) in group (all), only "vars", "children" and "hosts" are valid

 [WARNING]: Skipping unexpected key (server_group2) in group (all), only "vars", "children" and "hosts" are valid

 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

  hosts (0):

The correct syntax is:

---
all:
  children:
    server_group1:
      hosts:
        host1:
    server_group2:
      children:
        app1:
          hosts:
            host2:
            host3:
        app2:
          hosts:
            host4:
            host5:

Which now gives:

$ ansible -i inventories/test.yml all --list-hosts
  hosts (5):
    host1
    host2
    host3
    host4
    host5

Upvotes: 6

Related Questions