From 930d0905074ccf6537d0ecf14259a7da6a9559fb Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Wed, 6 Jul 2016 14:40:11 -0500 Subject: [PATCH] Fix the way handlers are compiled and found/notified * Instead of rebuilding the handler list all over the place, we now compile the handlers at the point the play is post-validated so that the view of the play in the PlayIterator contains the definitive list * Assign the dep_chain to the handlers as they're compiling, just as we do for regular tasks * Clean up the logic used to find a given handler, which is greatly simplified by the above changes Fixes #15418 --- lib/ansible/executor/task_queue_manager.py | 7 ++--- lib/ansible/playbook/play.py | 2 +- lib/ansible/playbook/role/__init__.py | 18 ++++++++++-- lib/ansible/plugins/strategy/__init__.py | 34 +++++++++------------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index 58b1409d1b..8c09151f26 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -122,10 +122,6 @@ class TaskQueueManager: inventory hostnames for those hosts triggering the handler. ''' - handlers = play.handlers - for role in play.roles: - handlers.extend(role.get_handler_blocks()) - # Zero the dictionary first by removing any entries there. # Proxied dicts don't support iteritems, so we have to use keys() self._notified_handlers.clear() @@ -141,7 +137,7 @@ class TaskQueueManager: return temp_list handler_list = [] - for handler_block in handlers: + for handler_block in play.handlers: handler_list.extend(_process_block(handler_block)) # then initialize it with the given handler list @@ -220,6 +216,7 @@ class TaskQueueManager: new_play = play.copy() new_play.post_validate(templar) + new_play.handlers = new_play.compile_roles_handlers() + new_play.handlers self.hostvars = HostVars( inventory=self._inventory, diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index a85ab7fe64..2031465eef 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -265,7 +265,7 @@ class Play(Base, Taggable, Become): if len(self.roles) > 0: for r in self.roles: - block_list.extend(r.get_handler_blocks()) + block_list.extend(r.get_handler_blocks(play=self)) return block_list diff --git a/lib/ansible/playbook/role/__init__.py b/lib/ansible/playbook/role/__init__.py index 6793feb343..3f1908c754 100644 --- a/lib/ansible/playbook/role/__init__.py +++ b/lib/ansible/playbook/role/__init__.py @@ -301,12 +301,24 @@ class Role(Base, Become, Conditional, Taggable): def get_task_blocks(self): return self._task_blocks[:] - def get_handler_blocks(self): + def get_handler_blocks(self, play, dep_chain=None): block_list = [] + + # update the dependency chain here + if dep_chain is None: + dep_chain = [] + new_dep_chain = dep_chain + [self] + for dep in self.get_direct_dependencies(): - dep_blocks = dep.get_handler_blocks() + dep_blocks = dep.get_handler_blocks(play=play, dep_chain=new_dep_chain) block_list.extend(dep_blocks) - block_list.extend(self._handler_blocks) + + for task_block in self._handler_blocks: + new_task_block = task_block.copy() + new_task_block._dep_chain = new_dep_chain + new_task_block._play = play + block_list.append(new_task_block) + return block_list def has_run(self, host): diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 8864d57736..d7441b0908 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -349,33 +349,27 @@ class StrategyBase: # dependency chain of the current task (if it's from a role), otherwise # we just look through the list of handlers in the current play/all # roles and use the first one that matches the notify name - target_handler = None - if original_task._role: - target_handler = search_handler_blocks(handler_name, original_task._role.get_handler_blocks()) - if target_handler is None: - target_handler = search_handler_blocks(handler_name, iterator._play.handlers) - if target_handler is None: - if handler_name in self._listening_handlers: - for listening_handler_name in self._listening_handlers[handler_name]: - listening_handler = None - if original_task._role: - listening_handler = search_handler_blocks(listening_handler_name, original_task._role.get_handler_blocks()) - if listening_handler is None: - listening_handler = search_handler_blocks(listening_handler_name, iterator._play.handlers) - if listening_handler is None: - raise AnsibleError("The requested handler listener '%s' was not found in any of the known handlers" % listening_handler_name) + if handler_name in self._listening_handlers: + for listening_handler_name in self._listening_handlers[handler_name]: + listening_handler = search_handler_blocks(listening_handler_name, iterator._play.handlers) + if listening_handler is None: + raise AnsibleError("The requested handler listener '%s' was not found in any of the known handlers" % listening_handler_name) - if original_host not in self._notified_handlers[listening_handler]: - self._notified_handlers[listening_handler].append(original_host) - display.vv("NOTIFIED HANDLER %s" % (listening_handler_name,)) - else: - raise AnsibleError("The requested handler '%s' was found in neither the main handlers list nor the listening handlers list" % handler_name) + if original_host not in self._notified_handlers[listening_handler]: + self._notified_handlers[listening_handler].append(original_host) + display.vv("NOTIFIED HANDLER %s" % (listening_handler_name,)) else: + target_handler = search_handler_blocks(handler_name, iterator._play.handlers) + if target_handler is None: + raise AnsibleError("The requested handler '%s' was not found in any of the known handlers" % handler_name) + if target_handler in self._notified_handlers: if original_host not in self._notified_handlers[target_handler]: self._notified_handlers[target_handler].append(original_host) # FIXME: should this be a callback? display.vv("NOTIFIED HANDLER %s" % (handler_name,)) + else: + raise AnsibleError("The requested handler '%s' was found in neither the main handlers list nor the listening handlers list" % handler_name) elif result[0] == 'register_host_var': # essentially the same as 'set_host_var' below, however we