r/ansible•
ashfsd

children / group structure in hosts file issue

playbooks, roles and collections

hi folks, hoping someone can tell me where i am going wrong here please, have read the docs and can't see what i am doing wrong.

the goal is to have a structure like follows: top level group: kube children groups of kube: kube1, kube2 children groups of kube1 and kube2: kube1_m, kube1_w, kube2_m, kube2_w lastly, each of those groups have hosts in there for their respective roles (kubernetes master node, worker node etc)

my /etc/ansible/hosts is laid out as follows:

all:
  children:
    kube1_m:
      hosts:
        k1.local.domain:
    kube1_w:
      hosts:
        k[2:4].local.domain:
    kube2_m:
      hosts:
        l1.local.domain:
    kube2_w:
      hosts:
        l[2:4].local.domain:
    kube1:
      children:
        kube1_m:
        kube1_w:
    kube2:
      children:
        kube2_m:
        kube2_w:
    kube:
      children:
        kube1:
        kube2:

If i then try and run a sample playbook:

---
- hosts: all
  tasks:
  - name: test
    when: 
      - ansible_hostname in groups['kube1_m']
      - ansible_hostname in groups['kube2_m']
    shell: ping -c 1 -w 2 8.8.8.8
    ignore_errors: true

Using a syntax such as ansible-playbook -vvvvv -l kube2 test.yml it doesn't run as expected.

From the log it seems to parse the hosts file ok but but then skips running it - based on my config it should run on the host l1 as it is present in the kube2_m group.

Parsed /etc/ansible/hosts inventory source with yaml plugin

PLAYBOOK: test.yml **************************************************************************************************************************************************************
Positional arguments: playbooks/k8s/test.yml
verbosity: 5
remote_user: ansible
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('/etc/ansible/hosts',)
subset: kube
forks: 5
1 plays in test.yml

skipping: [l2.local.domain] => {
    "changed": false,
    "skip_reason": "Conditional result was False"
}
skipping: [l1.local.domain] => {
    "changed": false,
    "skip_reason": "Conditional result was False"
}
skipping: [l3.local.domain] => {
    "changed": false,
    "skip_reason": "Conditional result was False"
}
skipping: [l4.local.domain] => {
    "changed": false,
    "skip_reason": "Conditional result was False"
}

is there a more appropriate way for me to be doing this or am i missing something simple here?

thanks!

Posted Jan 1, 2023, 10:42 AM UTC

913
jw_ken•

You are using logical AND in your when: statement (listing multiple conditions is an implied AND). So the host would need to belong to both groups, or else the task would be skipped.

Try this instead:

when: (inventory_hostname in groups['kube1_m']) or (inventory_hostname in groups['kube2_m'])

As u/cluelesssysadmin69 noted, be mindful of difference between ansible_hostname and inventory_hostname, the latter is safer if checking inventory/group membership.

7
ashfsdOP•

so I made some changes based on both bits of advice:

---
- hosts: all
  become: true
  tasks:
  - name: test
    when: 
      - when: (inventory_hostname in groups['kube1_m']) or (inventory_hostname in groups['kube2_m'])
    shell: ping -c 1 -w 2 8.8.8.8
    ignore_errors: true

no changes to hosts file.

If i then run ansible-playbook -vv -l kube1 test.yml or with -l kube2 it runs across the four hosts within each group, i.e. i get changed on all four, skipped on none. Because there is only one host in each kube1_m / kube2_m group I expected it to run just on the singular node. Running it on the top level kube group it runs on all 8 hosts. So i have gone from it running on none of the hosts to all of the hosts, when the desire is for it to only run on hosts present in the very bottom level subgroup (master/worker node groups).

I need to read up more on the differences between inventory_hostname and ansible_hostname, just writing this comment in the spare 10 minutes i have before a family day out. thanks!

edit: i am a moron, i messed up updating the when line. fixing that resolves the issue.

1
zoredache•

If i then run ansible-playbook -vv -l kube1 test.yml or with -l kube2 it runs across the four hosts within each group, i.e. i get changed on all four, skipped on none.

If it isn't working the the example given above, I suspect you have a more complicated inventory then what you have actually given us or something.

1
cluelesssysadmin69•

You’ll want to use inventory _hostname instead of ansible_hostname

4
514link•

Why are you using a when condition for targeting?

Use:

Hosts: kube1m:kube2m

Instead of hosts: all

4
ashfsdOP•

i suppose no good reason other than this playbook started life as a very different version of what it is now. now, there are tasks in it that should be performed based on roles, so i assume i would have to separate it out into different playbooks

1
zoredache•

There is a lookup that can make this easier. This lookup pretty much uses the same pattern syntax that you can use in the 'hosts' pattern for a play.

Play

---
- hosts: all
  gather_facts: no
  tasks:
  - name: Exmaple
    when: inventory_hostname in query('inventory_hostnames', 'kube1_m:kube2_m')
    debug:
      msg: "running for {{ inventory_hostname }}"

Output

$ ansible-playbook -i hosts.yml play.yml 

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

TASK [Exmaple] *****************************************************************************************************************************
skipping: [k2.local.domain]
ok: [k1.local.domain] => {
    "msg": "running for k1.local.domain"
}
skipping: [k3.local.domain]
skipping: [k4.local.domain]
ok: [l1.local.domain] => {
    "msg": "running for l1.local.domain"
}
skipping: [l2.local.domain]
skipping: [l3.local.domain]
skipping: [l4.local.domain]

PLAY RECAP *********************************************************************************************************************************
k1.local.domain            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
k2.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
k3.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
k4.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
l1.local.domain            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
l2.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
l3.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
l4.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

$ ansible-playbook -i hosts.yml play.yml -l kube1

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

TASK [Exmaple] *****************************************************************************************************************************
skipping: [k2.local.domain]
ok: [k1.local.domain] => {
    "msg": "running for k1.local.domain"
}
skipping: [k3.local.domain]
skipping: [k4.local.domain]

PLAY RECAP *********************************************************************************************************************************
k1.local.domain            : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
k2.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
k3.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
k4.local.domain            : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
2
ashfsdOP•

edit: well, it now works and the time i wasted on my previous comment here was for nought because i didn't notice i didn't correctly add your when statement into my playbook...

$ ansible-playbook -v -l kube2 test.yml 
Using /home/me/ansible/ansible.cfg as config file

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

TASK [test] *************************************************************************************************************************
skipping: [l2.local.domain] => {"changed": false, "skip_reason": "Conditional result was False"}
skipping: [l3.local.domain] => {"changed": false, "skip_reason": "Conditional result was False"}
skipping: [l4.local.domain] => {"changed": false, "skip_reason": "Conditional result was False"}
changed: [l1.local.domain] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "changed": true, "cmd": "ping -c 1 -w 2 8.8.8.8", "delta": "0:00:00.005154", "end": "2023-01-02 20:17:31.766218", "msg": "", "rc": 0, "start": "2023-01-02 20:17:31.761064", "stderr": "", "stderr_lines": [], "stdout": "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=60 time=2.26 ms\n\n--- 8.8.8.8 ping statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time 0ms\nrtt min/avg/max/mdev = 2.260/2.260/2.260/0.000 ms", "stdout_lines": ["PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.", "64 bytes from 8.8.8.8: icmp_seq=1 ttl=60 time=2.26 ms", "", "--- 8.8.8.8 ping statistics ---", "1 packets transmitted, 1 received, 0% packet loss, time 0ms", "rtt min/avg/max/mdev = 2.260/2.260/2.260/0.000 ms"]}

PLAY RECAP **************************************************************************************************************************
l1.local.domain                 : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
l2.local.domain                 : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
l3.local.domain                 : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
l4.local.domain                 : ok=0    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0  

updated my main playbooks and everything is working as expected, so thank you for the help!

1
zoredache•

when i run it against kube it runs a ping on all 8 nodes despite the 'master' group limitation.

Can you pastebin your output of running the playbook?

looks similar and would seem to imply that nested groups are not supported?

Nested groups are supported in the configuration files, but they are basically flattened when the inventory is read. There are certain ways of nesting that will not work.

This will be fine

all:
  children:
    foo:
      hosts:
        foo_1:
          hosts:
            host1:
    bar:
      hosts:
        bar_1:
          hosts:
            host2:

This will confuse things. IE the 'same', 'foo', and 'bar' groups will all have BOTH 'host1', and 'host2' as members.

all:
  children:
    foo:
      hosts:
        same:
          hosts:
            host1:
    bar:
      hosts:
        same:
          hosts:
            host2:
1
ashfsdOP•

you can see how i originally messed up updating the when line in the playbook in my comment at https://www.reddit.com/r/ansible/comments/100h575/children_group_structure_in_hosts_file_issue/j2jnqeu/, fixing that resolved the issue, thanks again :)

1
[deleted]•

[deleted]

1
FizzingWizzby•

I think your syntax is slightly off in your inventory - ansible needs things to been indented with 2 spaces from the line above. I’m on mobile so I can’t give an example easily, but it looks like you only have 1 space for each line

1
ashfsdOP•

indents are at two

2