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
This commit is contained in:
parent
f101910167
commit
fbec2d9692
5 changed files with 40 additions and 17 deletions
|
@ -141,6 +141,11 @@ class PlaybookExecutor:
|
||||||
# and run it...
|
# and run it...
|
||||||
result = self._tqm.run(play=play)
|
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
|
# 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
|
# 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
|
# 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),
|
# if the last result wasn't zero or 3 (some hosts were unreachable),
|
||||||
# break out of the serial batch loop
|
# 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
|
break
|
||||||
|
|
||||||
if break_play:
|
if break_play:
|
||||||
|
|
|
@ -58,6 +58,13 @@ class TaskQueueManager:
|
||||||
which dispatches the Play's tasks to hosts.
|
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):
|
def __init__(self, inventory, variable_manager, loader, options, passwords, stdout_callback=None, run_additional_callbacks=True, run_tree=False):
|
||||||
|
|
||||||
self._inventory = inventory
|
self._inventory = inventory
|
||||||
|
|
|
@ -120,7 +120,7 @@ class StrategyBase:
|
||||||
def run(self, iterator, play_context, result=True):
|
def run(self, iterator, play_context, result=True):
|
||||||
# save the failed/unreachable hosts, as the run_handlers()
|
# save the failed/unreachable hosts, as the run_handlers()
|
||||||
# method will clear that information during its execution
|
# 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()
|
unreachable_hosts = self._tqm._unreachable_hosts.keys()
|
||||||
|
|
||||||
display.debug("running handlers")
|
display.debug("running handlers")
|
||||||
|
@ -128,18 +128,20 @@ class StrategyBase:
|
||||||
|
|
||||||
# now update with the hosts (if any) that failed or were
|
# now update with the hosts (if any) that failed or were
|
||||||
# unreachable during the handler execution phase
|
# 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())
|
unreachable_hosts = set(unreachable_hosts).union(self._tqm._unreachable_hosts.keys())
|
||||||
|
|
||||||
# return the appropriate code, depending on the status hosts after the run
|
# return the appropriate code, depending on the status hosts after the run
|
||||||
if len(unreachable_hosts) > 0:
|
if not isinstance(result, bool) and result != self._tqm.RUN_OK:
|
||||||
return 3
|
return result
|
||||||
|
elif len(unreachable_hosts) > 0:
|
||||||
|
return self._tqm.RUN_UNREACHABLE_HOSTS
|
||||||
elif len(failed_hosts) > 0:
|
elif len(failed_hosts) > 0:
|
||||||
return 2
|
return self._tqm.RUN_FAILED_HOSTS
|
||||||
elif not result:
|
elif isinstance(result, bool) and not result:
|
||||||
return 1
|
return self._tqm.RUN_ERROR
|
||||||
else:
|
else:
|
||||||
return 0
|
return self._tqm.RUN_OK
|
||||||
|
|
||||||
def get_hosts_remaining(self, play):
|
def get_hosts_remaining(self, play):
|
||||||
return [host for host in self._inventory.get_hosts(play.hosts)
|
return [host for host in self._inventory.get_hosts(play.hosts)
|
||||||
|
|
|
@ -271,7 +271,7 @@ class StrategyModule(StrategyBase):
|
||||||
if not work_to_do and len(iterator.get_failed_hosts()) > 0:
|
if not work_to_do and len(iterator.get_failed_hosts()) > 0:
|
||||||
display.debug("out of hosts to run on")
|
display.debug("out of hosts to run on")
|
||||||
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
|
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
|
||||||
result = False
|
result = self._tqm.RUN_ERROR
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -284,7 +284,7 @@ class StrategyModule(StrategyBase):
|
||||||
variable_manager=self._variable_manager
|
variable_manager=self._variable_manager
|
||||||
)
|
)
|
||||||
except AnsibleError as e:
|
except AnsibleError as e:
|
||||||
return False
|
return self._tqm.RUN_ERROR
|
||||||
|
|
||||||
include_failure = False
|
include_failure = False
|
||||||
if len(included_files) > 0:
|
if len(included_files) > 0:
|
||||||
|
@ -354,13 +354,15 @@ class StrategyModule(StrategyBase):
|
||||||
failed_hosts.append(res._host.name)
|
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 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:
|
for host in hosts_left:
|
||||||
# don't double-mark hosts, or the iterator will potentially
|
# don't double-mark hosts, or the iterator will potentially
|
||||||
# fail them out of the rescue/always states
|
# fail them out of the rescue/always states
|
||||||
if host.name not in failed_hosts:
|
if host.name not in failed_hosts:
|
||||||
self._tqm._failed_hosts[host.name] = True
|
self._tqm._failed_hosts[host.name] = True
|
||||||
iterator.mark_host_failed(host)
|
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("done checking for any_errors_fatal")
|
||||||
|
|
||||||
display.debug("checking for max_fail_percentage")
|
display.debug("checking for max_fail_percentage")
|
||||||
|
@ -374,12 +376,14 @@ class StrategyModule(StrategyBase):
|
||||||
if host.name not in failed_hosts:
|
if host.name not in failed_hosts:
|
||||||
self._tqm._failed_hosts[host.name] = True
|
self._tqm._failed_hosts[host.name] = True
|
||||||
iterator.mark_host_failed(host)
|
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")
|
display.debug("done checking for max_fail_percentage")
|
||||||
|
|
||||||
except (IOError, EOFError) as e:
|
except (IOError, EOFError) as e:
|
||||||
display.debug("got IOError/EOFError in task loop: %s" % e)
|
display.debug("got IOError/EOFError in task loop: %s" % e)
|
||||||
# most likely an abort, return failed
|
# 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
|
# run the base class run() method, which executes the cleanup function
|
||||||
# and runs any outstanding handlers which have been triggered
|
# and runs any outstanding handlers which have been triggered
|
||||||
|
|
|
@ -64,12 +64,17 @@ class TestStrategyBase(unittest.TestCase):
|
||||||
mock_tqm._options = MagicMock()
|
mock_tqm._options = MagicMock()
|
||||||
strategy_base = StrategyBase(tqm=mock_tqm)
|
strategy_base = StrategyBase(tqm=mock_tqm)
|
||||||
|
|
||||||
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context), 0)
|
mock_host = MagicMock()
|
||||||
self.assertEqual(strategy_base.run(iterator=mock_iterator, play_context=mock_play_context, result=False), 1)
|
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)
|
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)
|
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):
|
def test_strategy_base_get_hosts(self):
|
||||||
mock_hosts = []
|
mock_hosts = []
|
||||||
|
|
Loading…
Reference in a new issue