Repository: ansible/ansible

Issue:
ansible-playbook doesn't report error by missing of the inventory file
This issue was reported with the ticket #15035 and should be implemented in the ansible version 2.4+ but seems still to exist (reproduced with the version 2.4.3.0 and 2.6.0 from devel).

 - Bug Report

##### COMPONENT NAME
ansible-playbook

##### ANSIBLE VERSION
```
ansible 2.6.0
  config file = /tmp/ansible.cfg
  configured module search path = [u'/app/inet/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
INVENTORY_UNPARSED_IS_FAILED(/tmp/ansible.cfg) = True

##### OS / ENVIRONMENT
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial

##### SUMMARY
ansible-playbook doesn't report error if the provided inventory file doesn't exist.

##### STEPS TO REPRODUCE
like in the ticket #15035

1. Create minimal play for an imaginary host:
here /tmp/test.yml

```yaml
---
- hosts: somehost
  tasks:
    - command: ls
```

2. create a config file - here /tmp/ansible.cfg
```
[inventory]
unparsed_is_failed=true
```

3. Run ansible-playbook for this play providing not existing file in the --inventory option.
```
/tmp$ ansible-playbook -vvvv -i nonexistinginv test.yml
```

##### EXPECTED RESULTS
There should be an error message saying something abour missing inventory file.

##### ACTUAL RESULTS
```
ansible-playbook 2.6.0
  config file = /tmp/ansible.cfg
  configured module search path = [u'/app/inet/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
Using /tmp/ansible.cfg as config file
setting up inventory plugins
 [WARNING]: Unable to parse /tmp/nonexistinginv as an inventory source

 [WARNING]: No inventory was parsed, only implicit localhost is available

 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/default.pyc

PLAYBOOK: test.yml **************************************************************************************************************************************************************************************************************************
1 plays in test.yml
 [WARNING]: Could not match supplied host pattern, ignoring: somehost


PLAY [somehost] *****************************************************************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
```



Discussion:
@alexejpissarev Greetings! Thanks for taking the time to open this issue. In order for the community to handle your issue effectively, we need a bit more information. 

Here are the items we could not find in your description:
- component name

Please set the description of this issue with this template:
https://raw.githubusercontent.com/ansible/ansible/devel/.github/ISSUE_TEMPLATE.md

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: issue_missing_data --->
##### COMPONENT NAME
 ansible-playbook

The warnings provided in your ansible playbook execution indicate that the provided inventory source was invalid.  As indicated in the issue you reference (https://github.com/ansible/ansible/issues/15035#issuecomment-303597105), what you are seeing is the expected outcome.

```
 [WARNING]: Unable to parse /tmp/nonexistinginv as an inventory source

 [WARNING]: No inventory was parsed, only implicit localhost is available

 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
```

If you have further questions please stop by IRC or the mailing list:

   * IRC: #ansible on irc.freenode.net
   * mailing list: https://groups.google.com/forum/#!forum/ansible-project 
Dear Matt,
did I misunderstand the documentation of the config setting unparsed_is_failed [here](http://docs.ansible.com/ansible/2.4/config.html#common-options)?
...
Description: | If ‘true’ unparsed inventory sources become fatal errors, they are warnings otherwise.
...
I was not able to make ansible produce any fatal errors, only warnings, regardless what the value of this variable is set in the ansible config file or as an environment variable.
In fact the behaviour of ansible doesn't change through this setting.
To my opinion either the documentation is wrong (where are the mentionend fatal errors?) or the setting doesn't have any influence on the playbook output.



so the current code fails only if the plugins fail to parse an existing file, but not when the file does not exist.

Resolving patch:
diff --git a/lib/ansible/inventory/manager.py b/lib/ansible/inventory/manager.py
--- a/lib/ansible/inventory/manager.py
+++ b/lib/ansible/inventory/manager.py
@@ -211,7 +211,10 @@ def parse_sources(self, cache=False):
             # do post processing
             self._inventory.reconcile_inventory()
         else:
-            display.warning("No inventory was parsed, only implicit localhost is available")
+            if C.INVENTORY_UNPARSED_IS_FAILED:
+                raise AnsibleError("No inventory was parsed, please check your configuration and options.")
+            else:
+                display.warning("No inventory was parsed, only implicit localhost is available")
 
         self._inventory_plugins = []
 
@@ -273,18 +276,10 @@ def parse_source(self, source, cache=False):
             else:
                 if not parsed and failures:
                     # only if no plugin processed files should we show errors.
-                    if C.INVENTORY_UNPARSED_IS_FAILED:
-                        msg = "Could not parse inventory source %s with available plugins:\n" % source
-                        for fail in failures:
-                            msg += 'Plugin %s failed: %s\n' % (fail['plugin'], to_native(fail['exc']))
-                            if display.verbosity >= 3:
-                                msg += "%s\n" % fail['exc'].tb
-                        raise AnsibleParserError(msg)
-                    else:
-                        for fail in failures:
-                            display.warning(u'\n* Failed to parse %s with %s plugin: %s' % (to_text(fail['src']), fail['plugin'], to_text(fail['exc'])))
-                            if hasattr(fail['exc'], 'tb'):
-                                display.vvv(to_text(fail['exc'].tb))
+                    for fail in failures:
+                        display.warning(u'\n* Failed to parse %s with %s plugin: %s' % (to_text(fail['src']), fail['plugin'], to_text(fail['exc'])))
+                        if hasattr(fail['exc'], 'tb'):
+                            display.vvv(to_text(fail['exc'].tb))
         if not parsed:
             display.warning("Unable to parse %s as an inventory source" % to_text(source))
 


Test patch: