Repository: ansible/ansible

Issue:
Specifying a group's ":vars" before it's ":children" causes the group to match no hosts.
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
inventory

##### ANSIBLE VERSION
```
[centos@ ~]$ ansible --version
ansible 2.4.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/centos/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/centos/ansible-latest/lib/python2.7/site-packages/ansible
  executable location = /home/centos/ansible-latest/bin/ansible
  python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
[centos@ ~]$ 
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).

-->
```
[centos@ ~]$ ansible-config dump --only-changed
DEFAULT_ROLES_PATH(/etc/ansible/ansible.cfg) = [u'/etc/ansible/roles', u'/usr/share/ansible/roles']
[centos@ ~]$ 
```
##### OS / ENVIRONMENT
CentOS Linux release 7.4.1708 (Core) 


##### SUMMARY
Specifying a group's :vars before it's :children causes the group to match no hosts.

##### STEPS TO REPRODUCE
Use the following sample inventory:
```
[centos@ ~]$ cat sample-inventory 
[states:children]
Texas

[Texas:vars]
ansible_ssh_user=centos

[Texas:children]
Dallas

[Dallas]
192.168.0.1
[centos@ ~]$ 
```
Sample playbook:
```
[centos@ ~]$ cat sample-playbook.yaml 
- hosts: states
  tasks:
  - name: Setup
    setup:
 
[centos@ ~]$ 
```
Sample playbook command:
```
[centos@ ~]$ ansible-playbook -i sample-inventory --list-hosts sample-playbook.yaml 
```




##### EXPECTED RESULTS
I expect the task in sample-playbook.yaml to match the Dallas host of 192.168.0.1.



##### ACTUAL RESULTS
No hosts actually match:
```
[centos@ ~]$ ansible-playbook -i sample-inventory --list-hosts sample-playbook.yaml 
 [WARNING]: Could not match supplied host pattern, ignoring: states


playbook: sample-playbook.yaml

  play #1 (states): states      TAGS: []
    pattern: [u'states']
    hosts (0):
[centos@ ~]$ 
```

If I swap the order of vars and children in sample-inventory so that it looks like:
```
[centos@ ~]$ cat sample-inventory 
[states:children]
Texas

[Texas:children]
Dallas

[Texas:vars]
ansible_ssh_user=centos

[Dallas]
192.168.0.1
[centos@ ~]$ 
```
Then I see the expected result:
```
[centos@ ~]$ ansible-playbook -i sample-inventory --list-hosts sample-playbook.yaml 

playbook: sample-playbook.yaml

  play #1 (states): states      TAGS: []
    pattern: [u'states']
    hosts (1):
      192.168.0.1
[centos@ ~]$ 
```


Discussion:
cc @dagnello @emonty @j2sol @juliakreger @rcarrillocruz @shrews @thingee
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
the issue has to do with 'pending' parents not resolving recursively, the change in vars just  pushes the definition earlier and avoids the need for recursion.

Resolving patch:
diff --git a/lib/ansible/plugins/inventory/ini.py b/lib/ansible/plugins/inventory/ini.py
--- a/lib/ansible/plugins/inventory/ini.py
+++ b/lib/ansible/plugins/inventory/ini.py
@@ -193,9 +193,7 @@ def _parse(self, path, lines):
 
                 if groupname in pending_declarations and state != 'vars':
                     if pending_declarations[groupname]['state'] == 'children':
-                        for parent in pending_declarations[groupname]['parents']:
-                            self.inventory.add_child(parent, groupname)
-                    del pending_declarations[groupname]
+                        self._add_pending_children(groupname, pending_declarations)
 
                 continue
             elif line.startswith('[') and line.endswith(']'):
@@ -248,6 +246,13 @@ def _parse(self, path, lines):
                 elif decl['state'] == 'children':
                     raise AnsibleError("%s:%d: Section [%s:children] includes undefined group: %s" % (path, decl['line'], decl['parents'].pop(), decl['name']))
 
+    def _add_pending_children(self, group, pending):
+        for parent in pending[group]['parents']:
+            self.inventory.add_child(parent, group)
+            if parent in pending and pending[parent]['state'] == 'children':
+                self._add_pending_children(parent, pending)
+        del pending[group]
+
     def _parse_group_name(self, line):
         '''
         Takes a single line and tries to parse it as a group name. Returns the


Test patch: