diff --git a/lib/ansible/plugins/strategies/__init__.py b/lib/ansible/plugins/strategies/__init__.py index 39637fb7b67..3a7bb85dfb2 100644 --- a/lib/ansible/plugins/strategies/__init__.py +++ b/lib/ansible/plugins/strategies/__init__.py @@ -69,6 +69,7 @@ class StrategyBase: self._variable_manager = tqm.get_variable_manager() self._loader = tqm.get_loader() self._final_q = tqm._final_q + self._step = tqm._options.step self._display = display # internal counters @@ -443,3 +444,26 @@ class StrategyBase: self._notified_handlers[handler_name] = [] self._display.debug("done running handlers, result is: %s" % result) return result + + def _take_step(self, task, host=None): + + ret=False + if host: + msg = u'Perform task: %s on %s (y/n/c): ' % (task, host) + else: + msg = u'Perform task: %s (y/n/c): ' % task + resp = self._display.prompt(msg) + + if resp.lower() in ['y','yes']: + self._display.debug("User ran task") + ret = True + elif resp.lower() in ['c', 'continue']: + self._display.debug("User ran task and cancled step mode") + self._step = False + ret = True + else: + self._display.debug("User skipped task") + + self._display.banner(msg) + + return ret diff --git a/lib/ansible/plugins/strategies/free.py b/lib/ansible/plugins/strategies/free.py index 253937b0576..683a96ba7ee 100644 --- a/lib/ansible/plugins/strategies/free.py +++ b/lib/ansible/plugins/strategies/free.py @@ -46,7 +46,7 @@ class StrategyModule(StrategyBase): ''' # the last host to be given a task - last_host = 0 + last_host = 0 result = True @@ -84,7 +84,7 @@ class StrategyModule(StrategyBase): self._display.debug("this host has work to do") # check to see if this host is blocked (still executing a previous task) - if not host_name in self._blocked_hosts: + if not host_name in self._blocked_hosts or not self._blocked_hosts[host_name]: # pop the task, mark the host blocked, and queue it self._blocked_hosts[host_name] = True (state, task) = iterator.get_next_task_for_host(host) @@ -122,8 +122,10 @@ class StrategyModule(StrategyBase): self._blocked_hosts[host_name] = False else: - self._tqm.send_callback('v2_playbook_on_task_start', task, is_conditional=False) - self._queue_task(host, task, task_vars, play_context) + # handle step if needed, skip meta actions as they are used internally + if not self._step or self._take_step(task, host_name): + self._tqm.send_callback('v2_playbook_on_task_start', task, is_conditional=False) + self._queue_task(host, task, task_vars, play_context) # move on to the next host and make sure we # haven't gone past the end of our hosts list diff --git a/lib/ansible/plugins/strategies/linear.py b/lib/ansible/plugins/strategies/linear.py index a00a936cba5..6de217f3f1b 100644 --- a/lib/ansible/plugins/strategies/linear.py +++ b/lib/ansible/plugins/strategies/linear.py @@ -94,6 +94,7 @@ class StrategyModule(StrategyBase): rvals.append((host, noop_task)) return rvals + # if any hosts are in ITERATING_SETUP, return the setup task # while all other hosts get a noop if num_setups: @@ -142,6 +143,10 @@ class StrategyModule(StrategyBase): host_results = [] host_tasks = self._get_next_task_lockstep(hosts_left, iterator) + # skip control + skip_rest = False + choose_step = True + for (host, task) in host_tasks: if not task: continue @@ -149,6 +154,7 @@ class StrategyModule(StrategyBase): 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. @@ -183,6 +189,14 @@ class StrategyModule(StrategyBase): else: raise AnsibleError("invalid meta action requested: %s" % meta_action, obj=task._ds) else: + # handle step if needed, skip meta actions as they are used internally + if self._step and choose_step: + if self._take_step(task): + choose_step = False + else: + break + skip_rest = True + self._display.debug("getting variables") task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=task) task_vars = self.add_tqm_variables(task_vars, play=iterator._play) @@ -210,6 +224,10 @@ class StrategyModule(StrategyBase): if run_once: break + # go to next host/task group + if skip_rest: + continue + self._display.debug("done queuing things up, now waiting for results queue to drain") results = self._wait_on_pending_results(iterator) host_results.extend(results) diff --git a/lib/ansible/utils/display.py b/lib/ansible/utils/display.py index 40a3b2dbd20..224d1c3863c 100644 --- a/lib/ansible/utils/display.py +++ b/lib/ansible/utils/display.py @@ -201,3 +201,11 @@ class Display: self.display(new_msg, color='red', stderr=True) self._errors[new_msg] = 1 + def prompt(self, msg): + + if sys.stdout.encoding: + msg = to_bytes(msg, sys.stdout.encoding) + else: + msg = to_bytes(msg) + + return raw_input(msg)