Repository: ansible/ansible-lint

Issue:
ansible-lint fails to parse inline import_role: name 
# Issue Type
- Bug report

# Ansible and Ansible Lint details

```
ansible-lint 3.4.23
```


# Desired Behaviour

Linting error or no error at all, but clearly not an exception.

# Actual Behaviour (Bug report only)

Try to do something like this in a playbook:
```
- import_role: name="containers/prepare-templates"
```

You will get:
```
Traceback (most recent call last):
File "/Users/ssbarnea/.pyenv/versions/2.7.14/bin/ansible-lint", line 11, in <module>
sys.exit(main())
File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/ansiblelint/__main__.py", line 176, in main
matches.extend(runner.run())
File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/ansiblelint/__init__.py", line 229, in run
for child in ansiblelint.utils.find_children(arg, self.playbook_dir):
File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/ansiblelint/utils.py", line 171, in find_children
for child in play_children(basedir, item, playbook[1], playbook_dir):
File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/ansiblelint/utils.py", line 221, in play_children
return delegate_map[k](basedir, k, v, parent_type)
File "/Users/ssbarnea/.pyenv/versions/2.7.14/lib/python2.7/site-packages/ansiblelint/utils.py", line 247, in _taskshandlers_children
results.extend(_roles_children(basedir, k, [th['import_role'].get('name')], parent_type,
AttributeError: 'AnsibleUnicode' object has no attribute 'get'
```

How to reproduce:

See https://review.gerrithub.io/c/redhat-openstack/infrared/+/415772/2/plugins/tripleo-overcloud/build_templates.yml 



Discussion:
Is this still a problem with latest ansible-lint? I thought I'd merged and published the fix for this

I still get this error with 3.5.1
Mee too 3.5.1, python 3.5.3, python 2.7.13
I can confirm this still happens with 4.1.0
Is there any update on this or any potential workarounds?

Resolving patch:
diff --git a/lib/ansiblelint/utils.py b/lib/ansiblelint/utils.py
--- a/lib/ansiblelint/utils.py
+++ b/lib/ansiblelint/utils.py
@@ -246,12 +246,14 @@ def _taskshandlers_children(basedir, k, v, parent_type):
         elif 'import_tasks' in th:
             append_children(th['import_tasks'], basedir, k, parent_type, results)
         elif 'import_role' in th:
-            results.extend(_roles_children(basedir, k, [th['import_role'].get('name')], parent_type,
-                                           main=th['import_role'].get('tasks_from', 'main')))
+            th = normalize_task_v2(th)
+            results.extend(_roles_children(basedir, k, [th['action'].get('name')], parent_type,
+                                           main=th['action'].get('tasks_from', 'main')))
         elif 'include_role' in th:
-            results.extend(_roles_children(basedir, k, [th['include_role'].get('name')],
+            th = normalize_task_v2(th)
+            results.extend(_roles_children(basedir, k, [th['action'].get('name')],
                                            parent_type,
-                                           main=th['include_role'].get('tasks_from', 'main')))
+                                           main=th['action'].get('tasks_from', 'main')))
         elif 'block' in th:
             results.extend(_taskshandlers_children(basedir, k, th['block'], parent_type))
             if 'rescue' in th:


Test patch:
diff --git a/test/TestImportIncludeRole.py b/test/TestImportIncludeRole.py
new file mode 100644
--- /dev/null
+++ b/test/TestImportIncludeRole.py
@@ -0,0 +1,95 @@
+import os
+import unittest
+import tempfile
+import shutil
+
+from ansiblelint import Runner, RulesCollection
+
+ROLE_TASKS_MAIN = '''
+- name: shell instead of command
+  shell: echo hello world
+'''
+
+ROLE_TASKS_WORLD = '''
+- command: echo this is a task without a name
+'''
+
+PLAY_IMPORT_ROLE = '''
+- hosts: all
+
+  tasks:
+    - import_role:
+        name: test-role
+'''
+
+PLAY_IMPORT_ROLE_INLINE = '''
+- hosts: all
+
+  tasks:
+    - import_role: name=test-role
+'''
+
+PLAY_INCLUDE_ROLE = '''
+- hosts: all
+
+  tasks:
+    - include_role:
+        name: test-role
+        tasks_from: world
+'''
+
+PLAY_INCLUDE_ROLE_INLINE = '''
+- hosts: all
+
+  tasks:
+    - include_role: name=test-role tasks_from=world
+'''
+
+
+class TestImportIncludeRole(unittest.TestCase):
+    def setUp(self):
+        rulesdir = os.path.join('lib', 'ansiblelint', 'rules')
+        self.rules = RulesCollection.create_from_directory(rulesdir)
+
+        # make dir and write role tasks to import or include
+        self.play_root = tempfile.mkdtemp()
+        role_path = os.path.join(self.play_root, 'test-role', 'tasks')
+        os.makedirs(role_path)
+        with open(os.path.join(role_path, 'main.yml'), 'w') as f_main:
+            f_main.write(ROLE_TASKS_MAIN)
+        with open(os.path.join(role_path, 'world.yml'), 'w') as f_world:
+            f_world.write(ROLE_TASKS_WORLD)
+
+    def tearDown(self):
+        shutil.rmtree(self.play_root)
+
+    def _get_play_file(self, playbook_text):
+        with open(os.path.join(self.play_root, 'playbook.yml'), 'w') as f_play:
+            f_play.write(playbook_text)
+        return f_play
+
+    def test_import_role(self):
+        fh = self._get_play_file(PLAY_IMPORT_ROLE)
+        runner = Runner(self.rules, fh.name, [], [], [])
+        results = runner.run()
+        assert 'only when shell functionality is required' in str(results)
+
+    def test_import_role_inline_args(self):
+        fh = self._get_play_file(PLAY_IMPORT_ROLE_INLINE)
+        runner = Runner(self.rules, fh.name, [], [], [])
+        results = runner.run()
+        assert 'only when shell functionality is required' in str(results)
+
+    def test_include_role(self):
+        fh = self._get_play_file(PLAY_INCLUDE_ROLE)
+        runner = Runner(self.rules, fh.name, [], [], [])
+        results = runner.run()
+        assert 'only when shell functionality is required' in str(results)
+        assert 'All tasks should be named' in str(results)
+
+    def test_include_role_inline_args(self):
+        fh = self._get_play_file(PLAY_INCLUDE_ROLE_INLINE)
+        runner = Runner(self.rules, fh.name, [], [], [])
+        results = runner.run()
+        assert 'only when shell functionality is required' in str(results)
+        assert 'All tasks should be named' in str(results)