Repository: ansible/ansible

Issue:
ansible drops 'non sectioned' hosts in ini inventory 
##### ISSUE TYPE

Bug Report

##### COMPONENT NAME

Ansible Core: general inventory management (`lib/ansible/inventory/...`)

##### ANSIBLE VERSION

It affects current head of `stable-2.3` branch. I tested from git source on macOS with this version:

```
ansible 2.3.0.0 (stable-2.3 8f6fd60fe2) last updated 2017/03/21 18:23:01 (GMT +200)
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Dec 18 2016, 07:03:39) [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]
```

and also with `v2.3.0.0-0.1.rc1` (`ansible 2.3.0.0` (detached HEAD e4494f85b6))

##### CONFIGURATION

No custom configuration in any `ansible.cfg` file.

##### OS / ENVIRONMENT

- Ansible Control Machine: macOS 10.12.3
- Managed machines: N/A

##### SUMMARY

In Ansible 2.2 (and previous versions), it is possible to give a path to a _directory_ as inventory argument (`-i` or `--inventory-file`). In such a case Ansible will parses (reads and/or executes) all the files or subdirectories contained in this directory.

Such behaviour is no longer working in Ansible 2.3 RC1, as illustrated in the section below.
I haven't take enough time to precisely identify the cause and location of this bug, prior to report it. As a first hint: With quick investigations I found that after the call to `lib/ansible/inventory/__init__.py::_enumerate_matches` for the pattern `"all"` the `_hosts_cache` is empty, while it shouldn't (Ansible 2.2.1.0 correctly fills the cache as shown below).

Note: I also verified with `v2.2.2.0-0.2.rc2`: no problem there :-)

##### STEPS TO REPRODUCE

Create a basic inventory directory, like this:

```
inventory_dir
├── a_hosts
└── b_hosts

$  cat inventory_dir/a_hosts 
a1
a2
$  cat inventory_dir/b_hosts
b1
b2
b3
```

Run the following command:

```shell
$ ansible -i inventory_dir  --list-hosts "all"
```

##### EXPECTED RESULTS

The result of the above command should be:

```shell
$  ansible -i inventory_dir  --list-hosts "all"
  hosts (5):
    a1
    a2
    b1
    b2
    b3
```

##### ACTUAL RESULTS

The result obtained with Ansible 2.3 RC1 is instead:

```shell
$  ansible -i inventory_dir  --list-hosts "all"
 [WARNING]: provided hosts list is empty, only localhost is available

 [WARNING]: No hosts matched, nothing to do

  hosts (0):
```



Discussion:
@shoreflyer [please don't ask](https://github.com/ansible/ansible/blob/devel/CONTRIBUTING.md#questions-) "install/usage question" here (but better check the [docs](docs.ansible.com/ansible/intro_installation.html) and ask in the google mailing list). I'd really appreciate to keep this thread focused on the original bug report.
confirmed, also affects current devel
so i found the issue, it is being processed as an ini file, but not detecting any hosts not inside a [section]

```
[all]
a1
a2
```
makes those hosts 'availble'
@bcoca if you want I can share what I did to get 2.3.0 to run. Message me so we can Skype and I can explain the procedure and you can share your insight too.

Resolving patch:
diff --git a/lib/ansible/inventory/ini.py b/lib/ansible/inventory/ini.py
--- a/lib/ansible/inventory/ini.py
+++ b/lib/ansible/inventory/ini.py
@@ -103,7 +103,6 @@ def _parse(self, lines):
             self.lineno += 1
 
             line = line.strip()
-
             # Skip empty lines and comments
             if not line or line[0] in self._COMMENT_MARKERS:
                 continue
@@ -156,6 +155,9 @@ def _parse(self, lines):
                 for h in hosts:
                     self.groups[groupname].add_host(h)
 
+                #FIXME: needed to save hosts to group, find out why
+                self.groups[groupname].get_hosts()
+
             # [groupname:vars] contains variable definitions that must be
             # applied to the current group.
             elif state == 'vars':
@@ -199,7 +201,7 @@ def _parse(self, lines):
         # 'all' at the time it was created.
 
         for group in self.groups.values():
-            if group.depth == 0 and group.name not in ('all', 'ungrouped'):
+            if group.depth == 0 and group.name != 'all':
                 self.groups['all'].add_child_group(group)
 
     def _parse_group_name(self, line):


Test patch: