As discussed with Michael on irc yesterday, I suspected something to be wrong with the calculated group depth, which is AFAICS used to have the right variable precedence according to the depth of a group in the parent-child tree.
As I'm looking into making a custom inventory script, tailored to our very strict standardized environment, I wanted to have a way to more or less visualize the group tree, and be able to check it on possible errors.
It took me some time to figure this out, as the wrong depth values I got were inconsistent
As it turned out, those were two similar, but different bugs in group.y and dir.py
$ cat inventory/ini/hosts2
[emil:children]
emil-on
emil-oe
emil-pr
[emil-on]
emil-on-1-mgt
emil-on-2-mgt
[emil-oe]
emil-oe-1-mgt
emil-oe-2-mgt
[emil-pr]
emil-pr-1-mgt
emil-pr-2-mgt
[tomcat:children]
emil
$ ansible-vars --list-groups -i inventory/ini/hosts
all(0) | p: | c:ungrouped(1),tomcat(1),depth1(1),depth2(2),depth3a(3),depth3b(3),appservers(1)
. appservers(1) | p: all(0) | c:depth3a(3)
. depth1(1) | p: all(0) | c:depth2(2)
.. depth2(2) | p: all(0),depth1(1) | c:depth3a(3),depth3b(3)
... depth3a(3) | p: all(0),depth2(2),appservers(1) | c:
... depth3b(3) | p: all(0),tomcat(1),depth2(2) | c:
. tomcat(1) | p: all(0) | c:depth3b(3)
. ungrouped(1) | p: all(0) | c:
$ ansible-vars --list-groups -i inventory/ini/hosts2
all(0) | p: | c:ungrouped(1),emil(2),emil-on(2),emil-oe(2),emil-pr(2),tomcat(1)
. tomcat(1) | p: all(0) | c:emil(2)
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(2),emil-oe(2),emil-pr(2)
. ungrouped(1) | p: all(0) | c:
######################################
Note: the output of my script is a bit redundant: it starts printing every group with (depth) and list of child (c) and parent (p) groups, then printing every child group beneath it, indented, *only if* it's a direct child (depth+1)
The first ini files by itself turns out to have correct results, but as you can see, all the emil groups have all the same depth, whilst emil is a parent of emil-*
This can be fixed by moving the tomcat group at the top.
For the depth logic to work as of now, you need to put top level groups first, etc.
Combining these to ini files by pointing the inventory to the ini dir, gives us other results:
######################################
$ ansible-vars --list-groups -i inventory/ini
all(0) | p: | c:depth3a(1),tomcat(1),depth3b(2),ungrouped(1),depth2(1),depth1(1),appservers(1),emil(2),emil-on(3),emil-oe(3),ungrouped(1),tomcat(1),emil-pr(3)
. appservers(1) | p: all(0) | c:depth3a(1)
. depth1(1) | p: all(0) | c:depth3a(1),depth3b(2),depth2(1)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
. depth2(1) | p: all(0),depth1(1) | c:depth3a(1),depth3b(2)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
. depth3a(1) | p: depth2(1),all(0),depth1(1),appservers(1) | c:
. tomcat(1) | p: all(0),all(0) | c:depth3b(2),emil(2),emil-on(3),emil-oe(3),emil-pr(3)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(3),emil-oe(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-on(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-pr(3) | p: all(0),emil(2),tomcat(1) | c:
. tomcat(1) | p: all(0),all(0) | c:depth3b(2),emil(2),emil-on(3),emil-oe(3),emil-pr(3)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(3),emil-oe(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-on(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-pr(3) | p: all(0),emil(2),tomcat(1) | c:
. ungrouped(1) | p: all(0),all(0) | c:
. ungrouped(1) | p: all(0),all(0) | c:
######################################
And again different when using the parent dir of that dir:
######################################
$ ansible-vars --list-groups -i inventory
all(0) | p: | c:depth3a(1),emil(1),tomcat(1),depth3b(2),emil-oe(2),ungrouped(1),depth2(1),depth1(1),emil-on(2),emil-pr(2),appservers(1)
. appservers(1) | p: all(0) | c:depth3a(1)
. depth1(1) | p: all(0) | c:depth3a(1),depth3b(2),depth2(1)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
. depth2(1) | p: all(0),depth1(1) | c:depth3a(1),depth3b(2)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
. depth3a(1) | p: depth2(1),all(0),depth1(1),appservers(1) | c:
. emil(1) | p: all(0),tomcat(1) | c:emil-oe(2),emil-on(2),emil-pr(2)
.. emil-oe(2) | p: all(0),emil(1),tomcat(1) | c:
.. emil-on(2) | p: all(0),emil(1),tomcat(1) | c:
.. emil-pr(2) | p: all(0),emil(1),tomcat(1) | c:
. tomcat(1) | p: all(0) | c:emil(1),depth3b(2),emil-oe(2),emil-on(2),emil-pr(2)
.. depth3b(2) | p: depth2(1),all(0),depth1(1),tomcat(1) | c:
.. emil-oe(2) | p: all(0),emil(1),tomcat(1) | c:
.. emil-on(2) | p: all(0),emil(1),tomcat(1) | c:
.. emil-pr(2) | p: all(0),emil(1),tomcat(1) | c:
. ungrouped(1) | p: all(0) | c:
######################################
Note that this last run actually cleaned up some groups that got defined multiple times.
I managed to write two patches that seem to solve this:
######################################
$ ansible-vars --list-groups -i inventory | grep -v debug
all(0) | p: | c:tomcat(1),ungrouped(1),appservers(1),depth1(1),emil(2),depth2(2),depth3a(3),depth3b(3),emil-oe(3),emil-on(3),emil-pr(3)
. appservers(1) | p: all(0) | c:depth3a(3)
. depth1(1) | p: all(0) | c:depth2(2),depth3a(3),depth3b(3)
.. depth2(2) | p: all(0),depth1(1) | c:depth3a(3),depth3b(3)
... depth3a(3) | p: depth2(2),all(0),depth1(1),appservers(1) | c:
... depth3b(3) | p: depth2(2),all(0),depth1(1),tomcat(1) | c:
. tomcat(1) | p: all(0) | c:emil(2),depth3b(3),emil-oe(3),emil-on(3),emil-pr(3)
.. emil(2) | p: all(0),tomcat(1) | c:emil-oe(3),emil-on(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-on(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-pr(3) | p: all(0),emil(2),tomcat(1) | c:
. ungrouped(1) | p: all(0) | c:
$ ansible-vars --list-groups -i inventory/ini/ | grep -v debug
all(0) | p: | c:tomcat(1),ungrouped(1),depth1(1),appservers(1),depth2(2),depth3a(3),depth3b(3),ungrouped(1),tomcat(1),emil(2),emil-on(3),emil-oe(3),emil-pr(3)
. appservers(1) | p: all(0) | c:depth3a(3)
. depth1(1) | p: all(0) | c:depth2(2),depth3a(3),depth3b(3)
.. depth2(2) | p: all(0),depth1(1) | c:depth3a(3),depth3b(3)
... depth3a(3) | p: depth2(2),all(0),depth1(1),appservers(1) | c:
... depth3b(3) | p: depth2(2),all(0),depth1(1),tomcat(1) | c:
. tomcat(1) | p: all(0),all(0) | c:depth3b(3),emil(2),emil-on(3),emil-oe(3),emil-pr(3)
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(3),emil-oe(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-on(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-pr(3) | p: all(0),emil(2),tomcat(1) | c:
. tomcat(1) | p: all(0),all(0) | c:depth3b(3),emil(2),emil-on(3),emil-oe(3),emil-pr(3)
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(3),emil-oe(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-on(3) | p: all(0),emil(2),tomcat(1) | c:
... emil-pr(3) | p: all(0),emil(2),tomcat(1) | c:
. ungrouped(1) | p: all(0),all(0) | c:
. ungrouped(1) | p: all(0),all(0) | c:
$ ansible-vars --list-groups -i inventory/ini/hosts | grep -v debug
all(0) | p: | c:ungrouped(1),tomcat(1),depth1(1),depth2(2),depth3a(3),depth3b(3),appservers(1)
. appservers(1) | p: all(0) | c:depth3a(3)
. depth1(1) | p: all(0) | c:depth2(2)
.. depth2(2) | p: all(0),depth1(1) | c:depth3a(3),depth3b(3)
... depth3a(3) | p: all(0),depth2(2),appservers(1) | c:
... depth3b(3) | p: all(0),tomcat(1),depth2(2) | c:
. tomcat(1) | p: all(0) | c:depth3b(3)
. ungrouped(1) | p: all(0) | c:
$ ansible-vars --list-groups -i inventory/ini/hosts2 | grep -v debug
all(0) | p: | c:ungrouped(1),emil(2),emil-on(3),emil-oe(3),emil-pr(3),tomcat(1)
. tomcat(1) | p: all(0) | c:emil(2)
.. emil(2) | p: all(0),tomcat(1) | c:emil-on(3),emil-oe(3),emil-pr(3)
... emil-oe(3) | p: all(0),emil(2) | c:
... emil-on(3) | p: all(0),emil(2) | c:
... emil-pr(3) | p: all(0),emil(2) | c:
. ungrouped(1) | p: all(0) | c:
######################################
The duplicaton of groups mentioned in multiple ini files is not solved here yet. I don't think that's a bug in my script, but this still could be possible.
Either way, this is less of a problem, as that doesn't seem to affect ansible, the host list is still generated correctly:
######################################
$ ansible-vars --list-hosts -i inventory | grep -v debug
emil-oe-1-mgt
emil-oe-2-mgt
emil-on-1-mgt
emil-on-2-mgt
emil-pr-1-mgt
emil-pr-2-mgt
node1
node2
node3
node4
[grpdpthtstng] serge@goldorak:~/src/ansible$ ansible-vars --list-hosts -i inventory/ini/ | grep -v debug
emil-oe-1-mgt
emil-oe-2-mgt
emil-on-1-mgt
emil-on-2-mgt
emil-pr-1-mgt
emil-pr-2-mgt
node1
node2
node3
node4
######################################
I made a test branch on my fork containing my ansible-vars script and the test inventory, so anyone can reproduce my findings:
Please note:
- at HEAD~1 you have the latest ansible + script + testinventory
- at HEAD you also have my patches containing some print 'debug' statements, which help to see what's going on at parsing time.
Whilst this is actually quite a fundamental bug, I think, I suspect not many people will have bumped into this, as I don't think many people will nest group variables and using precedence at many levels.
I hope this all makes it clear enough :)
Thanks for feedback,
Serge