From 4c733fe4b73b152d8ff7d8d570e23f0b01eecfaa Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Tue, 21 Jul 2015 14:51:53 -0400 Subject: [PATCH] Fix issues with vars_prompt and vars_files * Prompt had to be moved up, as it needed to be done before the first templating of the play occurs, otherwise vars_files won't be templated properly * Fixed a bug related to an earlier fix of vars_files incorporating extra vars to do the templating of the file name Fixes #11404 --- lib/ansible/executor/playbook_executor.py | 67 ++++++++++++++++++++++ lib/ansible/executor/task_queue_manager.py | 64 --------------------- lib/ansible/vars/__init__.py | 14 +++-- 3 files changed, 76 insertions(+), 69 deletions(-) diff --git a/lib/ansible/executor/playbook_executor.py b/lib/ansible/executor/playbook_executor.py index e692b76b8f..d89071adbc 100644 --- a/lib/ansible/executor/playbook_executor.py +++ b/lib/ansible/executor/playbook_executor.py @@ -19,7 +19,9 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import getpass import signal +import sys from ansible import constants as C from ansible.errors import * @@ -87,6 +89,25 @@ class PlaybookExecutor: # clear any filters which may have been applied to the inventory self._inventory.remove_restriction() + if play.vars_prompt: + for var in play.vars_prompt: + if 'name' not in var: + raise AnsibleError("'vars_prompt' item is missing 'name:'", obj=play._ds) + + vname = var['name'] + prompt = var.get("prompt", vname) + default = var.get("default", None) + private = var.get("private", True) + + confirm = var.get("confirm", False) + encrypt = var.get("encrypt", None) + salt_size = var.get("salt_size", None) + salt = var.get("salt", None) + + if vname not in play.vars: + self._tqm.send_callback('v2_playbook_on_vars_prompt', vname, private, prompt, encrypt, confirm, salt_size, salt, default) + 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. all_vars = self._variable_manager.get_vars(loader=self._loader, play=play) @@ -233,3 +254,49 @@ class PlaybookExecutor: serialized_batches.append(play_hosts) 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 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("***** VALUES ENTERED DO NOT MATCH ****") + else: + result = do_prompt(msg, private) + + # if result is false and default is not None + if not result and default is not None: + result = default + + # FIXME: make this work with vault or whatever this old method was + #if encrypt: + # result = utils.do_encrypt(result, encrypt, salt_size, salt) + + # handle utf-8 chars + # FIXME: make this work + #result = to_unicode(result, errors='strict') + return result + + diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index ad112eff37..6b605bb73b 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -19,7 +19,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -import getpass import multiprocessing import os import socket @@ -155,50 +154,6 @@ class TaskQueueManager: self._callbacks_loaded = True - def _do_var_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None): - - 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("***** VALUES ENTERED DO NOT MATCH ****") - else: - result = do_prompt(msg, private) - - # if result is false and default is not None - if not result and default is not None: - result = default - - # FIXME: make this work with vault or whatever this old method was - #if encrypt: - # result = utils.do_encrypt(result, encrypt, salt_size, salt) - - # handle utf-8 chars - # FIXME: make this work - #result = to_unicode(result, errors='strict') - return result - def run(self, play): ''' Iterates over the roles/tasks in a play, using the given (or default) @@ -211,25 +166,6 @@ class TaskQueueManager: if not self._callbacks_loaded: self.load_callbacks() - if play.vars_prompt: - for var in play.vars_prompt: - if 'name' not in var: - raise AnsibleError("'vars_prompt' item is missing 'name:'", obj=play._ds) - - vname = var['name'] - prompt = var.get("prompt", vname) - default = var.get("default", None) - private = var.get("private", True) - - confirm = var.get("confirm", False) - encrypt = var.get("encrypt", None) - salt_size = var.get("salt_size", None) - salt = var.get("salt", None) - - if vname not in play.vars: - self.send_callback('v2_playbook_on_vars_prompt', vname, private, prompt, encrypt, confirm, salt_size, salt, default) - play.vars[vname] = self._do_var_prompt(vname, private, prompt, encrypt, confirm, salt_size, salt, default) - all_vars = self._variable_manager.get_vars(loader=self._loader, play=play) templar = Templar(loader=self._loader, variables=all_vars) diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py index fa4bb15f10..2a5be96690 100644 --- a/lib/ansible/vars/__init__.py +++ b/lib/ansible/vars/__init__.py @@ -24,6 +24,8 @@ import os from collections import defaultdict from collections import MutableMapping +from jinja2.exceptions import UndefinedError + try: from hashlib import sha1 except ImportError: @@ -189,13 +191,13 @@ class VariableManager: if play: all_vars = self._combine_vars(all_vars, play.get_vars()) - # create a set of temporary vars here, which incorporate any extra vars - # which may have been specified, so we can properly template vars_files - temp_vars = self._combine_vars(all_vars, self.extra_vars) - templar = Templar(loader=loader, variables=temp_vars) - for vars_file_item in play.get_vars_files(): try: + # create a set of temporary vars here, which incorporate the + # extra vars so we can properly template the vars_files entries + temp_vars = self._combine_vars(all_vars, self._extra_vars) + templar = Templar(loader=loader, variables=temp_vars) + # we assume each item in the list is itself a list, as we # support "conditional includes" for vars_files, which mimics # the with_first_found mechanism. @@ -218,6 +220,8 @@ class VariableManager: # whether or not vars files errors should be fatal at this # stage, or just base it on whether a host was specified? pass + except UndefinedError, e: + continue if not C.DEFAULT_PRIVATE_ROLE_VARS: for role in play.get_roles():