Repository: ansible/ansible

Issue:
_raw_params is None in the include inside an include_role
When invoked in some special way `include_role` does not set the _raw_params value to its proper value but to None instead, this leads to the unexpected failure of the playbook.


##### ISSUE TYPE

 - Bug Report

##### COMPONENT NAME

- include + include_role + include

##### ANSIBLE VERSION

```
ansible 2.4.0 (devel f8982dcbd0) last updated 2017/07/07 11:16:35 (GMT +000)
  config file = /var/lib/nodepool/ansible-testrunner/ansible.cfg
  configured module search path = [u'/var/lib/nodepool/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /var/lib/nodepool/ansible-testrunner/ansible/lib/ansible
  executable location = /var/lib/nodepool/ansible-testrunner/ansible/bin/ansible
  python version = 2.7.5 (default, Nov  6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]
```

##### CONFIGURATION

File organization

```
tree .
.
├── playbook.yml
├── roles
│   └── foo
│       ├── tasks
│       │   └── main.yml
│       └── tests
│           └── basic.yml
└── temp.yml

4 directories, 4 files

```

playbook.yml

```
---
- include: temp.yml
```

temp.yml

```
---
- hosts: localhost
  tasks:
    - include_role:
        name: foo
```

roles/foo/tasks/main.yml

```
---
- include: tests/basic.yml
```

roles/foo/tests/basic.yml

```
---
- command: echo 'test'
```

Issues:

```
TASK [foo : include] **************************************************************************************************************************************************************************
task path: /tmp/demo/roles/foo/tasks/main.yml:2
ERROR! Unexpected Exception, this is probably a bug: 'NoneType' object has no attribute 'rfind'
the full traceback was:

Traceback (most recent call last):
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/bin/ansible-playbook", line 106, in <module>
    exit_code = cli.run()
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/lib/ansible/cli/playbook.py", line 130, in run
    results = pbex.run()
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/lib/ansible/executor/playbook_executor.py", line 153, in run
    result = self._tqm.run(play=play)
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/lib/ansible/executor/task_queue_manager.py", line 285, in run
    play_return = strategy.run(iterator, play_context)
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/lib/ansible/plugins/strategy/linear.py", line 331, in run
    variable_manager=self._variable_manager
  File "/home/spredzy/Projects/github.com/Spredzy/ansible/lib/ansible/playbook/included_file.py", line 104, in process_include_results
    parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params')))
  File "/usr/lib64/python2.7/posixpath.py", line 122, in dirname
    i = p.rfind('/') + 1
AttributeError: 'NoneType' object has no attribute 'rfind'
```


##### OS / ENVIRONMENT

- N/A

##### SUMMARY

When running the above playbook, somehow, `parent_include.args` has now `_raw_params` value set, leading to the failure of the playbook execution, as illustrated above.

Howerver if `_raw_params` is specified in the `include_role` module setting it up to the role name, the playbook works correctly.

One example has been done here to prove the point: https://github.com/ansible/ansible/commit/05477412ba122fe3b3d52ddeb2c3ab813979a33c


##### EXPECTED RESULTS

- The playbook to run normally

##### ACTUAL RESULTS

- The playbook fails on the `include` tasks inside the `include_role` tasks


Discussion:
cc @gundalow 
Once this is fixed we will need to revert https://github.com/ansible/ansible/commit/05477412ba122fe3b3d52ddeb2c3ab813979a33c

Abusing labels slightly and adding networking so the Networking Team keep an eye on this


Resolving patch:
diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py
--- a/lib/ansible/playbook/included_file.py
+++ b/lib/ansible/playbook/included_file.py
@@ -22,6 +22,7 @@
 import os
 
 from ansible.playbook.task_include import TaskInclude
+from ansible.playbook.role_include import IncludeRole
 from ansible.template import Templar
 
 try:
@@ -101,7 +102,10 @@ def get_original_host(host):
                                 if not isinstance(parent_include, TaskInclude):
                                     parent_include = parent_include._parent
                                     continue
-                                parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params')))
+                                if isinstance(parent_include, IncludeRole):
+                                    parent_include_dir = os.path.dirname(parent_include._role_path)
+                                else:
+                                    parent_include_dir = os.path.dirname(templar.template(parent_include.args.get('_raw_params')))
                                 if cumulative_path is None:
                                     cumulative_path = parent_include_dir
                                 elif not os.path.isabs(cumulative_path):
diff --git a/lib/ansible/playbook/role_include.py b/lib/ansible/playbook/role_include.py
--- a/lib/ansible/playbook/role_include.py
+++ b/lib/ansible/playbook/role_include.py
@@ -58,6 +58,7 @@ def __init__(self, block=None, role=None, task_include=None):
         self._from_files = {}
         self._parent_role = role
         self._role_name = None
+        self._role_path = None
 
     def get_block_list(self, play=None, variable_manager=None, loader=None):
 
@@ -74,6 +75,9 @@ def get_block_list(self, play=None, variable_manager=None, loader=None):
         actual_role = Role.load(ri, myplay, parent_role=self._parent_role, from_files=self._from_files)
         actual_role._metadata.allow_duplicates = self.allow_duplicates
 
+        # save this for later use
+        self._role_path = actual_role._role_path
+
         # compile role with parent roles as dependencies to ensure they inherit
         # variables
         if not self._parent_role:


Test patch: