Fix for the way host variables were being swallowed, plus some overall simplification. Deciding what
dictionary to use for templating now happens in exactly one place (executor_internal) and the "inject" dictionary is passed to what needs it.
This commit is contained in:
parent
9ef7168a49
commit
5f69c557b9
4 changed files with 52 additions and 57 deletions
|
@ -281,6 +281,7 @@ class PlayBook(object):
|
||||||
# let runner template out future commands
|
# let runner template out future commands
|
||||||
setup_ok = setup_results.get('contacted', {})
|
setup_ok = setup_results.get('contacted', {})
|
||||||
for (host, result) in setup_ok.iteritems():
|
for (host, result) in setup_ok.iteritems():
|
||||||
|
facts = result.get('ansible_facts', {})
|
||||||
self.SETUP_CACHE[host] = result.get('ansible_facts', {})
|
self.SETUP_CACHE[host] = result.get('ansible_facts', {})
|
||||||
return setup_results
|
return setup_results
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class Play(object):
|
||||||
raise errors.AnsibleError('hosts declaration is required')
|
raise errors.AnsibleError('hosts declaration is required')
|
||||||
elif isinstance(hosts, list):
|
elif isinstance(hosts, list):
|
||||||
hosts = ';'.join(hosts)
|
hosts = ';'.join(hosts)
|
||||||
hosts = utils.template(hosts, playbook.extra_vars, {})
|
hosts = utils.template(hosts, playbook.extra_vars)
|
||||||
|
|
||||||
self._ds = ds
|
self._ds = ds
|
||||||
self.playbook = playbook
|
self.playbook = playbook
|
||||||
|
|
|
@ -237,25 +237,14 @@ class Runner(object):
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_module(self, conn, tmp, remote_module_path, args,
|
def _execute_module(self, conn, tmp, remote_module_path, args,
|
||||||
async_jid=None, async_module=None, async_limit=None):
|
async_jid=None, async_module=None, async_limit=None, inject=None):
|
||||||
|
|
||||||
''' runs a module that has already been transferred '''
|
''' runs a module that has already been transferred '''
|
||||||
|
|
||||||
inject = self.setup_cache[conn.host].copy()
|
|
||||||
host_variables = self.inventory.get_variables(conn.host)
|
|
||||||
inject.update(host_variables)
|
|
||||||
inject.update(self.module_vars)
|
|
||||||
|
|
||||||
group_hosts = {}
|
|
||||||
for g in self.inventory.groups:
|
|
||||||
group_hosts[g.name] = map((lambda x: x.get_variables()),g.hosts)
|
|
||||||
|
|
||||||
inject['groups'] = group_hosts
|
|
||||||
|
|
||||||
if type(args) == dict:
|
if type(args) == dict:
|
||||||
args = utils.jsonify(args,format=True)
|
args = utils.jsonify(args,format=True)
|
||||||
|
|
||||||
args = utils.template(args, inject, self.setup_cache)
|
args = utils.template(args, inject)
|
||||||
|
|
||||||
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
||||||
if async_jid is None:
|
if async_jid is None:
|
||||||
|
@ -283,7 +272,7 @@ class Runner(object):
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_raw(self, conn, tmp):
|
def _execute_raw(self, conn, tmp, inject=None):
|
||||||
''' execute a non-module command for bootstrapping, or if there's no python on a device '''
|
''' execute a non-module command for bootstrapping, or if there's no python on a device '''
|
||||||
return ReturnData(host=conn.host, result=dict(
|
return ReturnData(host=conn.host, result=dict(
|
||||||
stdout=self._low_level_exec_command(conn, self.module_args, tmp, sudoable = True)
|
stdout=self._low_level_exec_command(conn, self.module_args, tmp, sudoable = True)
|
||||||
|
@ -291,7 +280,7 @@ class Runner(object):
|
||||||
|
|
||||||
# ***************************************************
|
# ***************************************************
|
||||||
|
|
||||||
def _execute_normal_module(self, conn, tmp, module_name):
|
def _execute_normal_module(self, conn, tmp, module_name, inject=None):
|
||||||
''' transfer & execute a module that is not 'copy' or 'template' '''
|
''' transfer & execute a module that is not 'copy' or 'template' '''
|
||||||
|
|
||||||
# shell and command are the same module
|
# shell and command are the same module
|
||||||
|
@ -300,14 +289,14 @@ class Runner(object):
|
||||||
self.module_args += " #USE_SHELL"
|
self.module_args += " #USE_SHELL"
|
||||||
|
|
||||||
module = self._transfer_module(conn, tmp, module_name)
|
module = self._transfer_module(conn, tmp, module_name)
|
||||||
exec_rc = self._execute_module(conn, tmp, module, self.module_args)
|
exec_rc = self._execute_module(conn, tmp, module, self.module_args, inject=inject)
|
||||||
if exec_rc.is_successful():
|
if exec_rc.is_successful():
|
||||||
self._add_result_to_setup_cache(conn, exec_rc.result)
|
self._add_result_to_setup_cache(conn, exec_rc.result)
|
||||||
return exec_rc
|
return exec_rc
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_async_module(self, conn, tmp, module_name):
|
def _execute_async_module(self, conn, tmp, module_name, inject=None):
|
||||||
''' transfer the given module name, plus the async module, then run it '''
|
''' transfer the given module name, plus the async module, then run it '''
|
||||||
|
|
||||||
# shell and command module are the same
|
# shell and command module are the same
|
||||||
|
@ -322,12 +311,13 @@ class Runner(object):
|
||||||
return self._execute_module(conn, tmp, async, module_args,
|
return self._execute_module(conn, tmp, async, module_args,
|
||||||
async_module=module,
|
async_module=module,
|
||||||
async_jid=self.generated_jid,
|
async_jid=self.generated_jid,
|
||||||
async_limit=self.background
|
async_limit=self.background,
|
||||||
|
inject=inject
|
||||||
)
|
)
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_copy(self, conn, tmp):
|
def _execute_copy(self, conn, tmp, inject=None):
|
||||||
''' handler for file transfer operations '''
|
''' handler for file transfer operations '''
|
||||||
|
|
||||||
# load up options
|
# load up options
|
||||||
|
@ -338,9 +328,6 @@ class Runner(object):
|
||||||
result=dict(failed=True, msg="src and dest are required")
|
result=dict(failed=True, msg="src and dest are required")
|
||||||
return ReturnData(host=conn.host, result=result)
|
return ReturnData(host=conn.host, result=result)
|
||||||
|
|
||||||
# apply templating to source argument
|
|
||||||
inject = self.setup_cache[conn.host]
|
|
||||||
|
|
||||||
# if we have first_available_file in our vars
|
# if we have first_available_file in our vars
|
||||||
# look up the files and use the first one we find as src
|
# look up the files and use the first one we find as src
|
||||||
if 'first_available_file' in self.module_vars:
|
if 'first_available_file' in self.module_vars:
|
||||||
|
@ -380,20 +367,20 @@ class Runner(object):
|
||||||
|
|
||||||
# run the copy module
|
# run the copy module
|
||||||
args = "src=%s dest=%s" % (tmp_src, dest)
|
args = "src=%s dest=%s" % (tmp_src, dest)
|
||||||
exec_rc = self._execute_module(conn, tmp, module, args)
|
exec_rc = self._execute_module(conn, tmp, module, args, inject=inject)
|
||||||
else:
|
else:
|
||||||
# no need to transfer the file, already correct md5
|
# no need to transfer the file, already correct md5
|
||||||
result = dict(changed=False, md5sum=remote_md5, transferred=False)
|
result = dict(changed=False, md5sum=remote_md5, transferred=False)
|
||||||
exec_rc = ReturnData(host=conn.host, result=result)
|
exec_rc = ReturnData(host=conn.host, result=result)
|
||||||
|
|
||||||
if exec_rc.is_successful():
|
if exec_rc.is_successful():
|
||||||
return self._chain_file_module(conn, tmp, exec_rc, options)
|
return self._chain_file_module(conn, tmp, exec_rc, options, inject=inject)
|
||||||
else:
|
else:
|
||||||
return exec_rc
|
return exec_rc
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_fetch(self, conn, tmp):
|
def _execute_fetch(self, conn, tmp, inject=None):
|
||||||
''' handler for fetch operations '''
|
''' handler for fetch operations '''
|
||||||
|
|
||||||
# load up options
|
# load up options
|
||||||
|
@ -405,13 +392,9 @@ class Runner(object):
|
||||||
return ReturnData(host=conn.host, result=results)
|
return ReturnData(host=conn.host, result=results)
|
||||||
|
|
||||||
# apply templating to source argument
|
# apply templating to source argument
|
||||||
inject = self.setup_cache[conn.host]
|
source = utils.template(source, inject)
|
||||||
if self.module_vars is not None:
|
|
||||||
inject.update(self.module_vars)
|
|
||||||
source = utils.template(source, inject, self.setup_cache)
|
|
||||||
|
|
||||||
# apply templating to dest argument
|
# apply templating to dest argument
|
||||||
dest = utils.template(dest, inject, self.setup_cache)
|
dest = utils.template(dest, inject)
|
||||||
|
|
||||||
# files are saved in dest dir, with a subdir for each host, then the filename
|
# files are saved in dest dir, with a subdir for each host, then the filename
|
||||||
dest = "%s/%s/%s" % (utils.path_dwim(self.basedir, dest), conn.host, source)
|
dest = "%s/%s/%s" % (utils.path_dwim(self.basedir, dest), conn.host, source)
|
||||||
|
@ -444,14 +427,14 @@ class Runner(object):
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _chain_file_module(self, conn, tmp, exec_rc, options):
|
def _chain_file_module(self, conn, tmp, exec_rc, options, inject=None):
|
||||||
|
|
||||||
''' handles changing file attribs after copy/template operations '''
|
''' handles changing file attribs after copy/template operations '''
|
||||||
|
|
||||||
old_changed = exec_rc.result.get('changed', False)
|
old_changed = exec_rc.result.get('changed', False)
|
||||||
module = self._transfer_module(conn, tmp, 'file')
|
module = self._transfer_module(conn, tmp, 'file')
|
||||||
args = ' '.join([ "%s=%s" % (k,v) for (k,v) in options.items() ])
|
args = ' '.join([ "%s=%s" % (k,v) for (k,v) in options.items() ])
|
||||||
exec_rc2 = self._execute_module(conn, tmp, module, args)
|
exec_rc2 = self._execute_module(conn, tmp, module, args, inject=inject)
|
||||||
|
|
||||||
new_changed = False
|
new_changed = False
|
||||||
if exec_rc2.is_successful():
|
if exec_rc2.is_successful():
|
||||||
|
@ -465,7 +448,7 @@ class Runner(object):
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_template(self, conn, tmp):
|
def _execute_template(self, conn, tmp, inject=None):
|
||||||
''' handler for template operations '''
|
''' handler for template operations '''
|
||||||
|
|
||||||
if not self.is_playbook:
|
if not self.is_playbook:
|
||||||
|
@ -480,9 +463,6 @@ class Runner(object):
|
||||||
result = dict(failed=True, msg="src and dest are required")
|
result = dict(failed=True, msg="src and dest are required")
|
||||||
return ReturnData(host=conn.host, comm_ok=False, result=result)
|
return ReturnData(host=conn.host, comm_ok=False, result=result)
|
||||||
|
|
||||||
# apply templating to source argument so vars can be used in the path
|
|
||||||
inject = self.setup_cache[conn.host]
|
|
||||||
|
|
||||||
# if we have first_available_file in our vars
|
# if we have first_available_file in our vars
|
||||||
# look up the files and use the first one we find as src
|
# look up the files and use the first one we find as src
|
||||||
if 'first_available_file' in self.module_vars:
|
if 'first_available_file' in self.module_vars:
|
||||||
|
@ -500,14 +480,14 @@ class Runner(object):
|
||||||
if self.module_vars is not None:
|
if self.module_vars is not None:
|
||||||
inject.update(self.module_vars)
|
inject.update(self.module_vars)
|
||||||
|
|
||||||
source = utils.template(source, inject, self.setup_cache)
|
source = utils.template(source, inject)
|
||||||
|
|
||||||
# install the template module
|
# install the template module
|
||||||
copy_module = self._transfer_module(conn, tmp, 'copy')
|
copy_module = self._transfer_module(conn, tmp, 'copy')
|
||||||
|
|
||||||
# template the source data locally
|
# template the source data locally
|
||||||
try:
|
try:
|
||||||
resultant = utils.template_from_file(self.basedir, source, inject, self.setup_cache)
|
resultant = utils.template_from_file(self.basedir, source, inject)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
result = dict(failed=True, msg=str(e))
|
result = dict(failed=True, msg=str(e))
|
||||||
return ReturnData(host=conn.host, comm_ok=False, result=result)
|
return ReturnData(host=conn.host, comm_ok=False, result=result)
|
||||||
|
@ -516,25 +496,25 @@ class Runner(object):
|
||||||
|
|
||||||
# run the COPY module
|
# run the COPY module
|
||||||
args = "src=%s dest=%s" % (xfered, dest)
|
args = "src=%s dest=%s" % (xfered, dest)
|
||||||
exec_rc = self._execute_module(conn, tmp, copy_module, args)
|
exec_rc = self._execute_module(conn, tmp, copy_module, args, inject=inject)
|
||||||
|
|
||||||
# modify file attribs if needed
|
# modify file attribs if needed
|
||||||
if exec_rc.comm_ok:
|
if exec_rc.comm_ok:
|
||||||
return self._chain_file_module(conn, tmp, exec_rc, options)
|
return self._chain_file_module(conn, tmp, exec_rc, options, inject=inject)
|
||||||
else:
|
else:
|
||||||
return exec_rc
|
return exec_rc
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _execute_assemble(self, conn, tmp):
|
def _execute_assemble(self, conn, tmp, inject=None):
|
||||||
''' handler for assemble operations '''
|
''' handler for assemble operations '''
|
||||||
module_name = 'assemble'
|
module_name = 'assemble'
|
||||||
options = utils.parse_kv(self.module_args)
|
options = utils.parse_kv(self.module_args)
|
||||||
module = self._transfer_module(conn, tmp, module_name)
|
module = self._transfer_module(conn, tmp, module_name)
|
||||||
exec_rc = self._execute_module(conn, tmp, module, self.module_args)
|
exec_rc = self._execute_module(conn, tmp, module, self.module_args, inject=inject)
|
||||||
|
|
||||||
if exec_rc.is_successful():
|
if exec_rc.is_successful():
|
||||||
return self._chain_file_module(conn, tmp, exec_rc, options)
|
return self._chain_file_module(conn, tmp, exec_rc, options, inject=inject)
|
||||||
else:
|
else:
|
||||||
return exec_rc
|
return exec_rc
|
||||||
|
|
||||||
|
@ -608,7 +588,17 @@ class Runner(object):
|
||||||
inject.update(host_variables)
|
inject.update(host_variables)
|
||||||
inject.update(self.module_vars)
|
inject.update(self.module_vars)
|
||||||
|
|
||||||
conditional = utils.template(self.conditional, inject, self.setup_cache)
|
# special non-user/non-fact variables:
|
||||||
|
# 'groups' variable is a list of host name in each group
|
||||||
|
# 'hostvars' variable contains variables for each host name
|
||||||
|
# ... and is set elsewhere
|
||||||
|
# 'inventory_hostname' is also set elsewhere
|
||||||
|
group_hosts = {}
|
||||||
|
for g in self.inventory.groups:
|
||||||
|
group_hosts[g.name] = [ h.name for h in g.hosts ]
|
||||||
|
inject['groups'] = group_hosts
|
||||||
|
|
||||||
|
conditional = utils.template(self.conditional, inject)
|
||||||
if not eval(conditional):
|
if not eval(conditional):
|
||||||
result = utils.jsonify(dict(skipped=True))
|
result = utils.jsonify(dict(skipped=True))
|
||||||
self.callbacks.on_skipped(host)
|
self.callbacks.on_skipped(host)
|
||||||
|
@ -621,19 +611,19 @@ class Runner(object):
|
||||||
result = dict(failed=True, msg="FAILED: %s" % str(e))
|
result = dict(failed=True, msg="FAILED: %s" % str(e))
|
||||||
return ReturnData(host=host, comm_ok=False, result=result)
|
return ReturnData(host=host, comm_ok=False, result=result)
|
||||||
|
|
||||||
module_name = utils.template(self.module_name, inject, self.setup_cache)
|
module_name = utils.template(self.module_name, inject)
|
||||||
|
|
||||||
tmp = self._make_tmp_path(conn)
|
tmp = self._make_tmp_path(conn)
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
handler = getattr(self, "_execute_%s" % self.module_name, None)
|
handler = getattr(self, "_execute_%s" % self.module_name, None)
|
||||||
if handler:
|
if handler:
|
||||||
result = handler(conn, tmp)
|
result = handler(conn, tmp, inject=inject)
|
||||||
else:
|
else:
|
||||||
if self.background == 0:
|
if self.background == 0:
|
||||||
result = self._execute_normal_module(conn, tmp, module_name)
|
result = self._execute_normal_module(conn, tmp, module_name, inject=inject)
|
||||||
else:
|
else:
|
||||||
result = self._execute_async_module(conn, tmp, module_name)
|
result = self._execute_async_module(conn, tmp, module_name, inject=inject)
|
||||||
|
|
||||||
self._delete_remote_files(conn, tmp)
|
self._delete_remote_files(conn, tmp)
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
|
@ -192,14 +192,17 @@ def varReplace(raw, vars):
|
||||||
|
|
||||||
return ''.join(done)
|
return ''.join(done)
|
||||||
|
|
||||||
def _template(text, vars, setup_cache=None):
|
def _template(text, vars):
|
||||||
''' run a text buffer through the templating engine '''
|
''' run a text buffer through the templating engine '''
|
||||||
|
|
||||||
|
if vars is None:
|
||||||
|
raise Exception('vars is none')
|
||||||
vars = vars.copy()
|
vars = vars.copy()
|
||||||
vars['hostvars'] = setup_cache
|
# FIXME: do this in runner code
|
||||||
|
vars['hostvars'] = vars.get('setup_cache', {})
|
||||||
return varReplace(unicode(text), vars)
|
return varReplace(unicode(text), vars)
|
||||||
|
|
||||||
def template(text, vars, setup_cache=None):
|
def template(text, vars):
|
||||||
''' run a text buffer through the templating engine until it no longer changes '''
|
''' run a text buffer through the templating engine until it no longer changes '''
|
||||||
|
|
||||||
prev_text = ''
|
prev_text = ''
|
||||||
|
@ -209,21 +212,22 @@ def template(text, vars, setup_cache=None):
|
||||||
if (depth > 20):
|
if (depth > 20):
|
||||||
raise errors.AnsibleError("template recursion depth exceeded")
|
raise errors.AnsibleError("template recursion depth exceeded")
|
||||||
prev_text = text
|
prev_text = text
|
||||||
text = _template(text, vars, setup_cache)
|
text = _template(text, vars)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def template_from_file(basedir, path, vars, setup_cache):
|
def template_from_file(basedir, path, vars):
|
||||||
''' run a file through the templating engine '''
|
''' run a file through the templating engine '''
|
||||||
|
|
||||||
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(basedir), trim_blocks=False)
|
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(basedir), trim_blocks=False)
|
||||||
data = codecs.open(path_dwim(basedir, path), encoding="utf8").read()
|
data = codecs.open(path_dwim(basedir, path), encoding="utf8").read()
|
||||||
t = environment.from_string(data)
|
t = environment.from_string(data)
|
||||||
|
# FIXME: possibly a bit inefficient here, do this in runner code
|
||||||
vars = vars.copy()
|
vars = vars.copy()
|
||||||
vars['hostvars'] = setup_cache
|
vars['hostvars'] = vars.get('setup_cache',{})
|
||||||
res = t.render(vars)
|
res = t.render(vars)
|
||||||
if data.endswith('\n') and not res.endswith('\n'):
|
if data.endswith('\n') and not res.endswith('\n'):
|
||||||
res = res + '\n'
|
res = res + '\n'
|
||||||
return template(res, vars, setup_cache)
|
return template(res, vars)
|
||||||
|
|
||||||
def parse_yaml(data):
|
def parse_yaml(data):
|
||||||
''' convert a yaml string to a data structure '''
|
''' convert a yaml string to a data structure '''
|
||||||
|
|
Loading…
Reference in a new issue