From 50448d68e11e644341e349cdfa59174536eebbfb Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Wed, 26 Aug 2015 12:03:13 -0400 Subject: [PATCH] Implement max_fail_percentage and any_errors_fatal support Fixes #11997 --- lib/ansible/executor/playbook_executor.py | 20 +++++++++++++++++--- lib/ansible/executor/task_queue_manager.py | 4 ++++ lib/ansible/playbook/task.py | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/ansible/executor/playbook_executor.py b/lib/ansible/executor/playbook_executor.py index 39b89de10b9..c88b4449907 100644 --- a/lib/ansible/executor/playbook_executor.py +++ b/lib/ansible/executor/playbook_executor.py @@ -128,15 +128,29 @@ class PlaybookExecutor: self._tqm.send_callback('v2_playbook_on_play_start', new_play) self._tqm.send_callback('v2_playbook_on_no_hosts_matched') break + # restrict the inventory to the hosts in the serialized batch self._inventory.restrict_to_hosts(batch) # and run it... result = self._tqm.run(play=play) - # if the last result wasn't zero, break out of the serial batch loop - if result != 0: + + # 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 + # batch failed + failed_hosts_count = len(self._tqm._failed_hosts) + len(self._tqm._unreachable_hosts) + if new_play.any_errors_fatal and failed_hosts_count > 0: + break + elif new_play.max_fail_percentage is not None and \ + int((new_play.max_fail_percentage)/100.0 * len(batch)) > int((len(batch) - failed_hosts_count) / len(batch) * 100.0): break + elif len(batch) == failed_hosts_count: + break + + # clear the failed hosts dictionaires in the TQM for the next batch + self._tqm.clear_failed_hosts() - # if the last result wasn't zero, break out of the play loop + # if the last result wasn't zero, break out of the serial batch loop if result != 0: break diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index b023200a512..59f48142b19 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -210,6 +210,10 @@ class TaskQueueManager: main_q.close() worker_prc.terminate() + def clear_failed_hosts(self): + self._failed_hosts = dict() + self._unreachable_hosts = dict() + def get_inventory(self): return self._inventory diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 7e6a5c68be2..a7c6b001314 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -39,6 +39,13 @@ from ansible.playbook.taggable import Taggable __all__ = ['Task'] +try: + from __main__ import display + display = display +except ImportError: + from ansible.utils.display import Display + display = Display() + class Task(Base, Conditional, Taggable, Become): """ @@ -193,6 +200,14 @@ class Task(Base, Conditional, Taggable, Become): return super(Task, self).preprocess_data(new_ds) + def _load_any_errors_fatal(self, attr, value): + ''' + Exists only to show a deprecation warning, as this attribute is not valid + at the task level. + ''' + display.deprecated("Setting any_errors_fatal on a task is no longer supported. This should be set at the play level only") + return None + def post_validate(self, templar): ''' Override of base class post_validate, to also do final validation on