diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index d738c19ce3..0732cb29ee 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -147,7 +147,14 @@ class TaskQueueManager: for listener in listeners: if listener not in self._listening_handlers: self._listening_handlers[listener] = [] - self._listening_handlers[listener].append(handler.get_name()) + + # if the handler has a name, we append it to the list of listening + # handlers, otherwise we use the uuid to avoid trampling on other + # nameless listeners + if handler.name: + self._listening_handlers[listener].append(handler.get_name()) + else: + self._listening_handlers[listener].append(handler._uuid) def load_callbacks(self): ''' diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 381e60af78..f2cf7844d2 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -241,26 +241,32 @@ class StrategyBase: def search_handler_blocks(handler_name, handler_blocks): for handler_block in handler_blocks: for handler_task in handler_block.block: - handler_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, task=handler_task) - templar = Templar(loader=self._loader, variables=handler_vars) - try: - # first we check with the full result of get_name(), which may - # include the role name (if the handler is from a role). If that - # is not found, we resort to the simple name field, which doesn't - # have anything extra added to it. - target_handler_name = templar.template(handler_task.name) - if target_handler_name == handler_name: - return handler_task - else: - target_handler_name = templar.template(handler_task.get_name()) + if handler_task.name: + handler_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, task=handler_task) + templar = Templar(loader=self._loader, variables=handler_vars) + try: + # first we check with the full result of get_name(), which may + # include the role name (if the handler is from a role). If that + # is not found, we resort to the simple name field, which doesn't + # have anything extra added to it. + target_handler_name = templar.template(handler_task.name) if target_handler_name == handler_name: return handler_task - except (UndefinedError, AnsibleUndefinedVariable): - # We skip this handler due to the fact that it may be using - # a variable in the name that was conditionally included via - # set_fact or some other method, and we don't want to error - # out unnecessarily - continue + else: + target_handler_name = templar.template(handler_task.get_name()) + if target_handler_name == handler_name: + return handler_task + except (UndefinedError, AnsibleUndefinedVariable): + # We skip this handler due to the fact that it may be using + # a variable in the name that was conditionally included via + # set_fact or some other method, and we don't want to error + # out unnecessarily + continue + else: + # if the handler name is not set, we check via the handlers uuid. + # this is mainly used by listening handlers only + if handler_name == handler_task._uuid: + return handler_task return None def parent_handler_match(target_handler, handler_name): @@ -415,6 +421,8 @@ class StrategyBase: listening_handler = search_handler_blocks(listening_handler_name, iterator._play.handlers) if listening_handler is not None: found = True + else: + continue 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,)) diff --git a/test/integration/targets/handlers/runme.sh b/test/integration/targets/handlers/runme.sh index b1af33b335..9b709de326 100755 --- a/test/integration/targets/handlers/runme.sh +++ b/test/integration/targets/handlers/runme.sh @@ -3,6 +3,7 @@ set -eux ansible-playbook test_handlers.yml -i inventory.handlers -v "$@" --tags scenario1 +ansible-playbook test_listening_handlers.yml -i inventory.handlers -v "$@" [ "$(ansible-playbook test_handlers.yml -i inventory.handlers -v "$@" --tags scenario2 -l A \ | egrep -o 'RUNNING HANDLER \[test_handlers : .*?]')" = "RUNNING HANDLER [test_handlers : test handler]" ] diff --git a/test/integration/targets/handlers/test_handlers.yml b/test/integration/targets/handlers/test_handlers.yml index 87d04b6c90..142eeb387d 100644 --- a/test/integration/targets/handlers/test_handlers.yml +++ b/test/integration/targets/handlers/test_handlers.yml @@ -18,6 +18,31 @@ - "'handler2_called' in hostvars[inventory_hostname]" tags: ['scenario1'] +- name: verify listening handlers + hosts: A + gather_facts: False + connection: local + tasks: + - name: notify some handlers + command: echo foo + notify: + - notify_listen + post_tasks: + - name: assert all defined handlers ran without error + assert: + that: + - "notify_listen_ran_1 is defined" + - "notify_listen_ran_2 is defined" + handlers: + - name: first listening handler has a name + set_fact: + notify_listen_ran_1: True + listen: notify_listen + # second listening handler does not + - set_fact: + notify_listen_ran_2: True + listen: notify_listen + - name: test handlers hosts: testgroup gather_facts: False diff --git a/test/integration/targets/handlers/test_listening_handlers.yml b/test/integration/targets/handlers/test_listening_handlers.yml new file mode 100644 index 0000000000..6500f02cfb --- /dev/null +++ b/test/integration/targets/handlers/test_listening_handlers.yml @@ -0,0 +1,25 @@ +--- +- name: verify listening handlers + hosts: A + gather_facts: False + connection: local + tasks: + - name: notify some handlers + command: echo foo + notify: + - notify_listen + post_tasks: + - name: assert all defined handlers ran without error + assert: + that: + - "notify_listen_ran_1 is defined" + - "notify_listen_ran_2 is defined" + handlers: + - name: first listening handler has a name + set_fact: + notify_listen_ran_1: True + listen: notify_listen + # second listening handler does not + - set_fact: + notify_listen_ran_2: True + listen: notify_listen