Repository: ansible/ansible

Issue:
Empty vars_prompt causes exception 'NoneType' object is not iterable 
<!---
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
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
<!--- Please do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" --->
vars_prompt

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0 (devel 65ffb92bff) last updated 2017/12/06 20:34:58 (GMT +000)
  config file = /home/ansible/plays/ansible.cfg
  configured module search path = [u'/home/ansible/extensions/modules']
  ansible python module location = /home/ansible/src/ansible/lib/ansible
  executable location = /home/ansible/src/ansible/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)]

```

##### SUMMARY
<!--- Explain the problem briefly -->

Had a case where I commented out the variables in a vars_prompt section, but skipped the header and realized this caused an unfriendly NoneType exception...

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---

- hosts: localhost
  gather_facts: no
  connection: local

  vars_prompt:
#    - name: "some_prompt"
#      prompt: "Enter It"

  tasks:
  - name: version
    ios_command:
      commands: show version
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->

A User-friendly error message or warning "Encountered an empty vars_prompt section in processing playbook"

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
Traceback (most recent call last):
  File "/home/ansible/src/ansible/bin/ansible-playbook", line 109, in <module>
    exit_code = cli.run()
  File "/home/ansible/src/ansible/lib/ansible/cli/playbook.py", line 130, in run
    results = pbex.run()
  File "/home/ansible/src/ansible/lib/ansible/executor/playbook_executor.py", line 82, in run
    pb = Playbook.load(playbook_path, variable_manager=self._variable_manager, loader=self._loader)
  File "/home/ansible/src/ansible/lib/ansible/playbook/__init__.py", line 54, in load
    pb._load_playbook_data(file_name=file_name, variable_manager=variable_manager)
  File "/home/ansible/src/ansible/lib/ansible/playbook/__init__.py", line 106, in _load_playbook_data
    entry_obj = Play.load(entry, variable_manager=variable_manager, loader=self._loader)
  File "/home/ansible/src/ansible/lib/ansible/playbook/play.py", line 112, in load
    return p.load_data(data, variable_manager=variable_manager, loader=loader)
  File "/home/ansible/src/ansible/lib/ansible/playbook/base.py", line 252, in load_data
    self._attributes[target_name] = method(name, ds[name])
  File "/home/ansible/src/ansible/lib/ansible/playbook/play.py", line 199, in _load_vars_prompt
    for prompt_data in new_ds:
TypeError: 'NoneType' object is not iterable
```



Discussion:
Files identified in the description:
None

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

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

Resolving patch:
diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py
--- a/lib/ansible/playbook/play.py
+++ b/lib/ansible/playbook/play.py
@@ -196,22 +196,23 @@ def _load_roles(self, attr, ds):
     def _load_vars_prompt(self, attr, ds):
         new_ds = preprocess_vars(ds)
         vars_prompts = []
-        for prompt_data in new_ds:
-            if 'name' not in prompt_data:
-                display.deprecated("Using the 'short form' for vars_prompt has been deprecated", version="2.7")
-                for vname, prompt in prompt_data.items():
-                    vars_prompts.append(dict(
-                        name=vname,
-                        prompt=prompt,
-                        default=None,
-                        private=None,
-                        confirm=None,
-                        encrypt=None,
-                        salt_size=None,
-                        salt=None,
-                    ))
-            else:
-                vars_prompts.append(prompt_data)
+        if new_ds is not None:
+            for prompt_data in new_ds:
+                if 'name' not in prompt_data:
+                    display.deprecated("Using the 'short form' for vars_prompt has been deprecated", version="2.7")
+                    for vname, prompt in prompt_data.items():
+                        vars_prompts.append(dict(
+                            name=vname,
+                            prompt=prompt,
+                            default=None,
+                            private=None,
+                            confirm=None,
+                            encrypt=None,
+                            salt_size=None,
+                            salt=None,
+                        ))
+                else:
+                    vars_prompts.append(prompt_data)
         return vars_prompts
 
     def _compile_roles(self):
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -151,26 +151,6 @@ def options_vars(self, value):
             raise AnsibleAssertionError("the type of 'value' for options_vars should be a dict, but is a %s" % type(value))
         self._options_vars = value.copy()
 
-    def _preprocess_vars(self, a):
-        '''
-        Ensures that vars contained in the parameter passed in are
-        returned as a list of dictionaries, to ensure for instance
-        that vars loaded from a file conform to an expected state.
-        '''
-
-        if a is None:
-            return None
-        elif not isinstance(a, list):
-            data = [a]
-        else:
-            data = a
-
-        for item in data:
-            if not isinstance(item, MutableMapping):
-                raise AnsibleError("variable files must contain either a dictionary of variables, or a list of dictionaries. Got: %s (%s)" % (a, type(a)))
-
-        return data
-
     def get_vars(self, play=None, host=None, task=None, include_hostvars=True, include_delegate_to=True, use_cache=True):
         '''
         Returns the variables, with optional "context" given via the parameters


Test patch: