From f12695f28761ba63d7ba634d2f34fbceaa527969 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Mon, 2 Mar 2015 12:38:45 -0600 Subject: [PATCH] Add run_once/BYPASS_HOST_LOOP feature in v2 --- v2/ansible/executor/task_executor.py | 1 + v2/ansible/plugins/__init__.py | 3 +++ v2/ansible/plugins/strategies/linear.py | 14 ++++++++++++++ v2/samples/test_run_once.yml | 6 ++++++ 4 files changed, 24 insertions(+) create mode 100644 v2/samples/test_run_once.yml diff --git a/v2/ansible/executor/task_executor.py b/v2/ansible/executor/task_executor.py index 99beeeae66f..012fb991949 100644 --- a/v2/ansible/executor/task_executor.py +++ b/v2/ansible/executor/task_executor.py @@ -359,6 +359,7 @@ class TaskExecutor: loader=self._loader, module_loader=self._module_loader, ) + if not handler: raise AnsibleError("the handler '%s' was not found" % handler_name) diff --git a/v2/ansible/plugins/__init__.py b/v2/ansible/plugins/__init__.py index 2aa3360bcde..31b684e70dd 100644 --- a/v2/ansible/plugins/__init__.py +++ b/v2/ansible/plugins/__init__.py @@ -221,6 +221,9 @@ class PluginLoader: path = self.find_plugin(name) if path is None: return None + elif kwargs.get('class_only', False): + return getattr(self._module_cache[path], self.class_name) + if path not in self._module_cache: self._module_cache[path] = imp.load_source('.'.join([self.package, name]), path) return getattr(self._module_cache[path], self.class_name)(*args, **kwargs) diff --git a/v2/ansible/plugins/strategies/linear.py b/v2/ansible/plugins/strategies/linear.py index 1725145d74f..e4f55be86a9 100644 --- a/v2/ansible/plugins/strategies/linear.py +++ b/v2/ansible/plugins/strategies/linear.py @@ -22,6 +22,7 @@ __metaclass__ = type from ansible.errors import AnsibleError from ansible.executor.play_iterator import PlayIterator from ansible.playbook.task import Task +from ansible.plugins import action_loader from ansible.plugins.strategies import StrategyBase from ansible.utils.debug import debug @@ -143,8 +144,17 @@ class StrategyModule(StrategyBase): if not task: continue + run_once = False work_to_do = True + # test to see if the task across all hosts points to an action plugin which + # sets BYPASS_HOST_LOOP to true, or if it has run_once enabled. If so, we + # will only send this task to the first host in the list. + + action = action_loader.get(task.action, class_only=True) + if task.run_once or getattr(action, 'BYPASS_HOST_LOOP', False): + run_once = True + debug("getting variables") task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=task) debug("done getting variables") @@ -183,6 +193,10 @@ class StrategyModule(StrategyBase): self._process_pending_results(iterator) + # if we're bypassing the host loop, break out now + if run_once: + break + debug("done queuing things up, now waiting for results queue to drain") self._wait_on_pending_results(iterator) diff --git a/v2/samples/test_run_once.yml b/v2/samples/test_run_once.yml new file mode 100644 index 00000000000..2b42f7af68e --- /dev/null +++ b/v2/samples/test_run_once.yml @@ -0,0 +1,6 @@ +- hosts: all + gather_facts: no + tasks: + - ping: + run_once: yes + - add_host: name=foo