summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cammarata <jimi@sngx.net>2016-07-08 16:08:38 -0500
committerJames Cammarata <jimi@sngx.net>2016-07-08 16:08:38 -0500
commit5d1cee3c843d0daed126782cb317eb90aa6e489a (patch)
tree7414f3c5c4fa7382b142d385ffe8333640048e14
parent3d7a7c16124952a38158c8d6f546534867135f74 (diff)
downloadansible-feature_1476.tar.gz
New feature: add new meta action `end_play`feature_1476
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
-rw-r--r--lib/ansible/plugins/strategy/__init__.py73
-rw-r--r--lib/ansible/plugins/strategy/free.py2
-rw-r--r--lib/ansible/plugins/strategy/linear.py5
3 files changed, 59 insertions, 21 deletions
diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index d7441b0908..f4f1d41bd2 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -727,28 +727,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
diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py
index 89287c49ab..52dfc642f2 100644
--- a/lib/ansible/plugins/strategy/free.py
+++ b/lib/ansible/plugins/strategy/free.py
@@ -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
diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py
index b0ba34cae1..644fddead4 100644
--- a/lib/ansible/plugins/strategy/linear.py
+++ b/lib/ansible/plugins/strategy/linear.py
@@ -212,7 +212,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: