New feature: add new meta action end_play
This feature also cleans up and extends the meta subsystem: * Allows for some meta actions (noop, clear_facts, clear_host_errors, and end_play) to operate on a per-host basis, meaning they can work with the free strategy as expected. * Allows for conditionals on meta tasks. * Fixes a bug where (for the linear strategy) metas were not treated as a run_once task, meaning every host in inventory would run the meta task. Fixes #1476
This commit is contained in:
parent
01911e4958
commit
78b8f81cab
3 changed files with 59 additions and 21 deletions
|
@ -695,28 +695,63 @@ class StrategyBase:
|
|||
|
||||
return ret
|
||||
|
||||
def _execute_meta(self, task, play_context, iterator):
|
||||
def _execute_meta(self, task, play_context, iterator, target_host=None):
|
||||
|
||||
# meta tasks store their args in the _raw_params field of args,
|
||||
# since they do not use k=v pairs, so get that
|
||||
meta_action = task.args.get('_raw_params')
|
||||
|
||||
if meta_action == 'noop':
|
||||
# FIXME: issue a callback for the noop here?
|
||||
pass
|
||||
elif meta_action == 'flush_handlers':
|
||||
self.run_handlers(iterator, play_context)
|
||||
elif meta_action == 'refresh_inventory':
|
||||
self._inventory.refresh_inventory()
|
||||
elif meta_action == 'clear_facts':
|
||||
for host in iterator._host_states:
|
||||
self._variable_manager.clear_facts(host)
|
||||
#elif meta_action == 'reset_connection':
|
||||
# connection_info.connection.close()
|
||||
elif meta_action == 'clear_host_errors':
|
||||
self._tqm._failed_hosts = dict()
|
||||
self._tqm._unreachable_hosts = dict()
|
||||
for host in iterator._host_states:
|
||||
iterator._host_states[host].fail_state = iterator.FAILED_NONE
|
||||
# FIXME(s):
|
||||
# * raise an error or show a warning when a conditional is used
|
||||
# on a meta task that doesn't support them
|
||||
|
||||
def _evaluate_conditional(h):
|
||||
all_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=task)
|
||||
templar = Templar(loader=self._loader, variables=all_vars)
|
||||
return task.evaluate_conditional(templar, all_vars)
|
||||
|
||||
if target_host:
|
||||
host_list = [target_host]
|
||||
else:
|
||||
raise AnsibleError("invalid meta action requested: %s" % meta_action, obj=task._ds)
|
||||
host_list = [host for host in self._inventory.get_hosts(iterator._play.hosts) if host.name not in self._tqm._unreachable_hosts]
|
||||
|
||||
results = []
|
||||
for host in host_list:
|
||||
result = None
|
||||
if meta_action == 'noop':
|
||||
# FIXME: issue a callback for the noop here?
|
||||
result = TaskResult(host, task, dict(changed=False, msg="noop"))
|
||||
elif meta_action == 'flush_handlers':
|
||||
self.run_handlers(iterator, play_context)
|
||||
elif meta_action == 'refresh_inventory':
|
||||
self._inventory.refresh_inventory()
|
||||
result = TaskResult(host, task, dict(changed=False, msg="inventory successfully refreshed"))
|
||||
elif meta_action == 'clear_facts':
|
||||
if _evaluate_conditional(host):
|
||||
self._variable_manager.clear_facts(target_host)
|
||||
result = TaskResult(host, task, dict(changed=True, msg="inventory successfully refreshed"))
|
||||
else:
|
||||
result = TaskResult(host, task, dict(changed=False, skipped=True))
|
||||
elif meta_action == 'clear_host_errors':
|
||||
if _evaluate_conditional(host):
|
||||
self._tqm._failed_hosts.pop(host.name, False)
|
||||
self._tqm._unreachable_hosts.pop(host.name, False)
|
||||
iterator._host_states[host.name].fail_state = iterator.FAILED_NONE
|
||||
result = TaskResult(host, task, dict(changed=True, msg="successfully cleared host errors"))
|
||||
else:
|
||||
result = TaskResult(host, task, dict(changed=False, skipped=True))
|
||||
elif meta_action == 'end_play':
|
||||
if _evaluate_conditional(host):
|
||||
iterator._host_states[host.name].run_state = iterator.ITERATING_COMPLETE
|
||||
result = TaskResult(host, task, dict(changed=True, msg="ending play"))
|
||||
else:
|
||||
result = TaskResult(host, task, dict(changed=False, skipped=True))
|
||||
#elif meta_action == 'reset_connection':
|
||||
# connection_info.connection.close()
|
||||
else:
|
||||
raise AnsibleError("invalid meta action requested: %s" % meta_action, obj=task._ds)
|
||||
|
||||
if result is not None:
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
|
|
@ -137,7 +137,7 @@ class StrategyModule(StrategyBase):
|
|||
continue
|
||||
|
||||
if task.action == 'meta':
|
||||
self._execute_meta(task, play_context, iterator)
|
||||
self._execute_meta(task, play_context, iterator, target_host=host)
|
||||
self._blocked_hosts[host_name] = False
|
||||
else:
|
||||
# handle step if needed, skip meta actions as they are used internally
|
||||
|
|
|
@ -214,7 +214,10 @@ class StrategyModule(StrategyBase):
|
|||
continue
|
||||
|
||||
if task.action == 'meta':
|
||||
self._execute_meta(task, play_context, iterator)
|
||||
# for the linear strategy, we run meta tasks just once and for
|
||||
# all hosts currently being iterated over rather than one host
|
||||
results.extend(self._execute_meta(task, play_context, iterator))
|
||||
run_once = True
|
||||
else:
|
||||
# handle step if needed, skip meta actions as they are used internally
|
||||
if self._step and choose_step:
|
||||
|
|
Loading…
Reference in a new issue