Repository: ansible/ansible

Issue:
Ansible 2.0 prompts for vars_prompt when --extra-vars is used
##### Issue Type

Bug Report
##### Ansible Version

2.0 from the "stable-2.0" branch, commit 91c7691a92e63580216e0e5c657b689361330beb

```
$ ansible --version
ansible 2.0.0
  config file =
  configured module search path = Default w/o overrides
```
##### Ansible Configuration

No changes
##### Environment

RHEL 7
##### Summary

Ansible 2.0 stops execution and requests the user to enter variables specified with `vars_prompt` even if the same variable has been specified on the comand line.

Ansible 1.9.4 does not prompt for `vars_prompt` if `--extra-vars` specifies the same variable.
##### Steps To Reproduce:
###### playbook.yml

``` yaml

---
- hosts: all
  gather_facts: False
  vars_prompt:
    - name: x
      prompt: 'Enter value for x'
  tasks:
    - local_action: debug var=x
```

```
$ cat inventory
localhost

$ ansible-playbook -i inventory playbook.yml -e x=1
```
##### Expected Results:

Non-interactive run, as on 1.9.4:

```
$ ansible-playbook --version
ansible-playbook 1.9.4
  configured module search path = None

$ ansible-playbook -i inventory playbook.yml -e x=foo

PLAY [all] ********************************************************************

TASK: [debug var=x] ***********************************************************
ok: [localhost -> 127.0.0.1] => {
    "var": {
        "x": "foo"
    }
}

PLAY RECAP ********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```
##### Actual Results:

The user is prompted:

```
$ ansible-playbook -i inventory playbook.yml -e x=foo
Enter value for x:
```



Discussion:
so there is code that checks for this (and it used to work) https://github.com/ansible/ansible/blob/devel/lib/ansible/executor/playbook_executor.py#L114

But for some reason `play.vars` is now always empty.

@chlunde can you test the PR above and confirm the fix?


Resolving patch:
diff --git a/lib/ansible/executor/playbook_executor.py b/lib/ansible/executor/playbook_executor.py
--- a/lib/ansible/executor/playbook_executor.py
+++ b/lib/ansible/executor/playbook_executor.py
@@ -111,13 +111,12 @@ def run(self):
                             salt_size = var.get("salt_size", None)
                             salt      = var.get("salt", None)
 
-                            if vname not in play.vars:
+                            if vname not in self._variable_manager.extra_vars:
+                                self._tqm.send_callback('v2_playbook_on_vars_prompt', vname, private, prompt, encrypt, confirm, salt_size, salt, default)
                                 if self._tqm:
-                                    self._tqm.send_callback('v2_playbook_on_vars_prompt', vname, private, prompt, encrypt, confirm, salt_size, salt, default)
-                                if self._options.syntax:
+                                    play.vars[vname] = display.do_var_prompt(vname, private, prompt, encrypt, confirm, salt_size, salt, default)
+                                else: # we are either in --list-<option> or syntax check
                                     play.vars[vname] = default
-                                else:
-                                    play.vars[vname] = self._do_var_prompt(vname, private, prompt, encrypt, confirm, salt_size, salt, default)
 
                     # Create a temporary copy of the play here, so we can run post_validate
                     # on it without the templating changes affecting the original object.
@@ -237,48 +236,3 @@ def _get_serialized_batches(self, play):
 
             return serialized_batches
 
-    def _do_var_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
-
-        if sys.__stdin__.isatty():
-            if prompt and default is not None:
-                msg = "%s [%s]: " % (prompt, default)
-            elif prompt:
-                msg = "%s: " % prompt
-            else:
-                msg = 'input for %s: ' % varname
-
-            def do_prompt(prompt, private):
-                if sys.stdout.encoding:
-                    msg = prompt.encode(sys.stdout.encoding)
-                else:
-                    # when piping the output, or at other times when stdout
-                    # may not be the standard file descriptor, the stdout
-                    # encoding may not be set, so default to something sane
-                    msg = prompt.encode(locale.getpreferredencoding())
-                if private:
-                    return getpass.getpass(msg)
-                return raw_input(msg)
-
-            if confirm:
-                while True:
-                    result = do_prompt(msg, private)
-                    second = do_prompt("confirm " + msg, private)
-                    if result == second:
-                        break
-                    display.display("***** VALUES ENTERED DO NOT MATCH ****")
-            else:
-                result = do_prompt(msg, private)
-        else:
-            result = None
-            display.warning("Not prompting as we are not in interactive mode")
-
-        # if result is false and default is not None
-        if not result and default is not None:
-            result = default
-
-        if encrypt:
-            result = do_encrypt(result, encrypt, salt_size, salt)
-
-        # handle utf-8 chars
-        result = to_unicode(result, errors='strict')
-        return result
diff --git a/lib/ansible/utils/display.py b/lib/ansible/utils/display.py
--- a/lib/ansible/utils/display.py
+++ b/lib/ansible/utils/display.py
@@ -267,13 +267,56 @@ def error(self, msg, wrap_text=True):
             self._errors[new_msg] = 1
 
     @staticmethod
-    def prompt(msg):
+    def prompt(msg, private=False):
         prompt_string = to_bytes(msg, encoding=Display._output_encoding())
         if sys.version_info >= (3,):
             # Convert back into text on python3.  We do this double conversion
             # to get rid of characters that are illegal in the user's locale
             prompt_string = to_unicode(prompt_string)
-        return input(prompt_string)
+
+        if private:
+            return getpass.getpass(msg)
+        else:
+            return input(prompt_string)
+
+    @classmethod
+    def do_var_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
+
+        result = None
+        if sys.__stdin__.isatty():
+
+            do_prompt = self.prompt
+
+            if prompt and default is not None:
+                msg = "%s [%s]: " % (prompt, default)
+            elif prompt:
+                msg = "%s: " % prompt
+            else:
+                msg = 'input for %s: ' % varname
+
+            if confirm:
+                while True:
+                    result = do_prompt(msg, private)
+                    second = do_prompt("confirm " + msg, private)
+                    if result == second:
+                        break
+                    display.display("***** VALUES ENTERED DO NOT MATCH ****")
+            else:
+                result = do_prompt(msg, private)
+        else:
+            result = None
+            display.warning("Not prompting as we are not in interactive mode")
+
+        # if result is false and default is not None
+        if not result and default is not None:
+            result = default
+
+        if encrypt:
+            result = do_encrypt(result, encrypt, salt_size, salt)
+
+        # handle utf-8 chars
+        result = to_unicode(result, errors='strict')
+        return result
 
     @staticmethod
     def _output_encoding(stderr=False):


Test patch: