From 20c1a677111732412b73549202aa889c3c218f9a Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Tue, 2 Oct 2012 15:15:55 +0200 Subject: [PATCH] Abort a play at the start when no hosts matches, or no hosts are remaining This change makes a distinction between no_hosts_matched and no_hosts_remaining. In both cases we do not start facts-gathering, or run any tasks. In the case that there are no more hosts remaining, we abort running tasks and abort the playbook. I also cleaned up the leftovers from the previous patchsets, as these are no longer required. This closes #1187. Example playbook: ```yaml --- - hosts: emptygroup tasks: - action: command date - action: command false - hosts: all gather_facts: False tasks: - action: command ls - action: command false - action: command true - hosts: all tasks: - action: command true - action: command false - hosts: all tasks: - action: command pwd ``` --- lib/ansible/callback_plugins/noop.py | 6 ++++++ lib/ansible/callbacks.py | 8 ++++++++ lib/ansible/playbook/__init__.py | 21 +++++++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/ansible/callback_plugins/noop.py b/lib/ansible/callback_plugins/noop.py index e550e81e091..4b9bcd23816 100644 --- a/lib/ansible/callback_plugins/noop.py +++ b/lib/ansible/callback_plugins/noop.py @@ -63,6 +63,12 @@ class CallbackModule(object): def playbook_on_notify(self, host, handler): pass + def on_no_hosts_matched(self): + pass + + def on_no_hosts_remaining(self): + pass + def playbook_on_task_start(self, name, is_conditional): pass diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index dcf11a6b11e..2f03be99fc7 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -414,6 +414,14 @@ class PlaybookCallbacks(object): def on_notify(self, host, handler): call_callback_module('playbook_on_notify', host, handler) + def on_no_hosts_matched(self): + print stringc("no hosts matched", 'red') + call_callback_module('playbook_on_no_hosts_matched') + + def on_no_hosts_remaining(self): + print stringc("\nFATAL: all hosts have already failed -- aborting", 'red') + call_callback_module('playbook_on_no_hosts_remaining') + def on_task_start(self, name, is_conditional): msg = "TASK: [%s]" % name if is_conditional: diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index f3bb5044953..c318a5005b9 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -115,9 +115,6 @@ class PlayBook(object): (self.playbook, self.play_basedirs) = self._load_playbook_from_file(playbook) self.module_path = self.module_path + os.pathsep + os.path.join(self.basedir, "library") - # which task we are currently executing - self.task_counter = 0 - # ***************************************************** def _load_playbook_from_file(self, path): @@ -239,8 +236,6 @@ class PlayBook(object): # if not polling, playbook requested fire and forget, so don't poll results = self._async_poll(poller, task.async_seconds, task.async_poll_interval) - self.task_counter = self.task_counter + 1 - contacted = results.get('contacted',{}) dark = results.get('dark', {}) @@ -346,6 +341,11 @@ class PlayBook(object): self.callbacks.on_play_start(play.name) + # if no hosts matches this play, drop out + if not self.inventory.list_hosts(play.hosts): + self.callbacks.on_no_hosts_matched() + return True + # get facts from system self._do_setup_step(play) @@ -384,7 +384,15 @@ class PlayBook(object): # whether no hosts matched is fatal or not depends if it was on the initial step. # if we got exactly no hosts on the first step (setup!) then the host group # just didn't match anything and that's ok - return (self.task_counter <= 1) + return False + + host_list = [ h for h in self.inventory.list_hosts(play.hosts) + if not (h in self.stats.failures or h in self.stats.dark) ] + + # if no hosts remain, drop out + if not host_list: + self.callbacks.on_no_hosts_remaining() + return False # run notify actions for handler in play.handlers(): @@ -394,5 +402,6 @@ class PlayBook(object): self.inventory.lift_restriction() self.inventory.lift_also_restriction() + return True