Expand return code values returned by TQM and strategies

This allows the PlaybookExecutor to receive more information regarding
what happened internal to the TaskQueueManager and strategy, to determine
things like whether or not the play iteration should stop.

Fixes #15523

(cherry picked from commit fbec2d9692)
This commit is contained in:
James Cammarata 2016-06-08 10:11:34 -05:00
parent d4c78b84f0
commit 2e003adbc8
5 changed files with 40 additions and 17 deletions

View file

@ -141,6 +141,11 @@ class PlaybookExecutor:
# and run it...
result = self._tqm.run(play=play)
# break the play if the result equals the special return code
if result == self._tqm.RUN_FAILED_BREAK_PLAY:
result = self._tqm.RUN_FAILED_HOSTS
break_play = True
# check the number of failures here, to see if they're above the maximum
# failure percentage allowed, or if any errors are fatal. If either of those
# conditions are met, we break out, otherwise we only break out if the entire
@ -159,7 +164,7 @@ class PlaybookExecutor:
# if the last result wasn't zero or 3 (some hosts were unreachable),
# break out of the serial batch loop
if result not in (0, 3):
if result not in (self._tqm.RUN_OK, self._tqm.RUN_UNREACHABLE_HOSTS):
break
if break_play:

View file

@ -58,6 +58,13 @@ class TaskQueueManager:
which dispatches the Play's tasks to hosts.
'''
RUN_OK = 0
RUN_ERROR = 1
RUN_FAILED_HOSTS = 2
RUN_UNREACHABLE_HOSTS = 3
RUN_FAILED_BREAK_PLAY = 4
RUN_UNKNOWN_ERROR = 255
def __init__(self, inventory, variable_manager, loader, options, passwords, stdout_callback=None, run_additional_callbacks=True, run_tree=False):
self._inventory = inventory

View file

@ -120,7 +120,7 @@ class StrategyBase:
def run(self, iterator, play_context, result=True):
# save the failed/unreachable hosts, as the run_handlers()
# method will clear that information during its execution
failed_hosts = self._tqm._failed_hosts.keys()
failed_hosts = iterator.get_failed_hosts()
unreachable_hosts = self._tqm._unreachable_hosts.keys()
display.debug("running handlers")
@ -128,18 +128,20 @@ class StrategyBase:
# now update with the hosts (if any) that failed or were
# unreachable during the handler execution phase
failed_hosts = set(failed_hosts).union(self._tqm._failed_hosts.keys())
failed_hosts = set(failed_hosts).union(iterator.get_failed_hosts())
unreachable_hosts = set(unreachable_hosts).union(self._tqm._unreachable_hosts.keys())
# return the appropriate code, depending on the status hosts after the run
if len(unreachable_hosts) > 0:
return 3
if not isinstance(result, bool) and result != self._tqm.RUN_OK:
return result
elif len(unreachable_hosts) > 0:
return self._tqm.RUN_UNREACHABLE_HOSTS
elif len(failed_hosts) > 0:
return 2
elif not result:
return 1
return self._tqm.RUN_FAILED_HOSTS
elif isinstance(result, bool) and not result:
return self._tqm.RUN_ERROR
else:
return 0
return self._tqm.RUN_OK
def get_hosts_remaining(self, play):
return [host for host in self._inventory.get_hosts(play.hosts)

View file

@ -271,7 +271,7 @@ class StrategyModule(StrategyBase):
if not work_to_do and len(iterator.get_failed_hosts()) > 0:
display.debug("out of hosts to run on")
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
result = False
result = self._tqm.RUN_ERROR
break
try:
@ -284,7 +284,7 @@ class StrategyModule(StrategyBase):
variable_manager=self._variable_manager
)
except AnsibleError as e:
return False
return self._tqm.RUN_ERROR
include_failure = False
if len(included_files) > 0:
@ -354,13 +354,15 @@ class StrategyModule(StrategyBase):
failed_hosts.append(res._host.name)
# if any_errors_fatal and we had an error, mark all hosts as failed
if any_errors_fatal and len(failed_hosts) > 0:
if any_errors_fatal and (len(failed_hosts) > 0 or len(self._tqm._unreachable_hosts.keys()) > 0):
for host in hosts_left:
# don't double-mark hosts, or the iterator will potentially
# fail them out of the rescue/always states
if host.name not in failed_hosts:
self._tqm._failed_hosts[host.name] = True
iterator.mark_host_failed(host)
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
return self._tqm.RUN_FAILED_BREAK_PLAY
display.debug("done checking for any_errors_fatal")
display.debug("checking for max_fail_percentage")
@ -374,12 +376,14 @@ class StrategyModule(StrategyBase):
if host.name not in failed_hosts:
self._tqm._failed_hosts[host.name] = True
iterator.mark_host_failed(host)
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
return self._tqm.RUN_FAILED_BREAK_PLAY
display.debug("done checking for max_fail_percentage")
except (IOError, EOFError) as e:
display.debug("got IOError/EOFError in task loop: %s" % e)
# most likely an abort, return failed
return False
return self._tqm.RUN_UNKNOWN_ERROR
# run the base class run() method, which executes the cleanup function
# and runs any outstanding handlers which have been triggered

View file

@ -64,12 +64,17 @@ class TestStrategyBase(unittest.TestCase):
mock_tqm._options = MagicMock()
strategy_base = StrategyBase(tqm=mock_tqm)
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context), 0)
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), 1)
mock_host = MagicMock()
mock_host.name = 'host1'
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context), mock_tqm.RUN_OK)
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), mock_tqm.RUN_ERROR)
mock_tqm._failed_hosts = dict(host1=True)
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), 2)
mock_iterator.get_failed_hosts.return_value = [mock_host]
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), mock_tqm.RUN_FAILED_HOSTS)
mock_tqm._unreachable_hosts = dict(host1=True)
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), 3)
mock_iterator.get_failed_hosts.return_value = []
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), mock_tqm.RUN_UNREACHABLE_HOSTS)
def test_strategy_base_get_hosts(self):
mock_hosts = []