Repository: ansible/ansible

Issue:
handler: mixing notify listen and name behavior changed
<!---
Please do not report issues/requests related to Ansible modules here !!

Report them to the appropriate modules-core or modules-extras project:
  - https://github.com/ansible/ansible-modules-core/issues
  - https://github.com/ansible/ansible-modules-extras/issues

Also verify first that your issue/request is not already reported in GitHub
-->
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Bug Report
##### COMPONENT NAME

handler
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
2.2
```
##### CONFIGURATION

N/A
##### OS / ENVIRONMENT

N/A
##### SUMMARY

In the first implementation of listen (see ca6ee4c78980707a6ff83176d0e019a9b0ef052f but use 17447ff for tests) it was defined that if there are identical handler names and listen names, notifying a name has prio and listen would be ignored (keep backwards compatibly)

In the current state, listen has prio and handler name is ignored.
##### STEPS TO REPRODUCE

<!--- Paste example playbooks or commands between quotes below -->

``` yaml

---
- name: test case 4
  hosts: localhost
  gather_facts: false
  connection: local
  tasks:
    - name: test notify handlers name and identical listen
      command: uptime
      notify:
        - notify_listen
    - meta: flush_handlers
    - name: verify test notify handlers name and identical listen
      assert:
        that:
        - "notify_handler_name_ran is defined"
        - "notify_listen_ran_1 is not defined"
        - "notify_listen_ran_2 is not  defined"
  handlers:
    - name: notify_listen
      set_fact:
        notify_handler_name_ran: True
    - name: notify_handler_ran_1
      set_fact:
        notify_listen_ran_1: True
      listen: notify_listen
    - name: notify_handler_ran_2
      set_fact:
        notify_listen_ran_2: True
      listen: notify_listen
```

<!--- You can also paste gist.github.com links for larger files -->
##### EXPECTED RESULTS

<!--- What did you expect to happen when running the steps above? -->

This is the result with commit 17447ff

```
PLAYBOOK: test_handlers_listen2.yml ********************************************
1 plays in test_handlers_listen2.yml

PLAY [test case 4] *************************************************************

TASK [test notify handlers name and identical listen] **************************
task path: /home/resmo/Projects/resmo/ansible/test/integration/test_handlers_listen2.yml:7
NOTIFIED HANDLER notify_listen
changed: [localhost] => {"changed": true, "cmd": ["uptime"], "delta": "0:00:00.004097", "end": "2016-10-02 11:27:58.646939", "rc": 0, "start": "2016-10-02 11:27:58.642842", "stderr": "", "stdout": " 11:27:58 up 1 day, 11:39,  4 users,  load average: 0.30, 0.30, 0.29", "stdout_lines": [" 11:27:58 up 1 day, 11:39,  4 users,  load average: 0.30, 0.30, 0.29"], "warnings": []}

RUNNING HANDLER [notify_listen] ************************************************
ok: [localhost] => {"ansible_facts": {"notify_handler_name_ran": true}, "changed": false}

TASK [verify test notify handlers name and identical listen] *******************
task path: /home/resmo/Projects/resmo/ansible/test/integration/test_handlers_listen2.yml:12
ok: [localhost] => {"changed": false, "msg": "all assertions passed"}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0   
```
##### ACTUAL RESULTS

This is the result with current devel (becb4765c348a7fc8219cc25c7db7025095ede70)

```
PLAYBOOK: test_handlers_listen2.yml ********************************************
1 plays in test_handlers_listen2.yml

PLAY [test case 4] *************************************************************

TASK [test notify handlers name and identical listen] **************************
task path: /home/resmo/Projects/resmo/ansible/test/integration/test_handlers_listen2.yml:7
NOTIFIED HANDLER notify_handler_ran_1
NOTIFIED HANDLER notify_handler_ran_2
changed: [localhost] => {"changed": true, "cmd": ["uptime"], "delta": "0:00:00.002681", "end": "2016-10-02 11:33:51.332106", "rc": 0, "start": "2016-10-02 11:33:51.329425", "stderr": "", "stdout": " 11:33:51 up 1 day, 11:45,  4 users,  load average: 0.37, 0.38, 0.33", "stdout_lines": [" 11:33:51 up 1 day, 11:45,  4 users,  load average: 0.37, 0.38, 0.33"], "warnings": []}

RUNNING HANDLER [notify_handler_ran_1] *****************************************
ok: [localhost] => {"ansible_facts": {"notify_listen_ran_1": true}, "changed": false}

RUNNING HANDLER [notify_handler_ran_2] *****************************************
ok: [localhost] => {"ansible_facts": {"notify_listen_ran_2": true}, "changed": false}

TASK [verify test notify handlers name and identical listen] *******************
task path: /home/resmo/Projects/resmo/ansible/test/integration/test_handlers_listen2.yml:12
fatal: [localhost]: FAILED! => {
    "assertion": "notify_handler_name_ran is defined", 
    "changed": false, 
    "evaluated_to": false, 
    "failed": true
}
    to retry, use: --limit @/home/resmo/Projects/resmo/ansible/test/integration/test_handlers_listen2.retry

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=1 
```



Discussion:

Resolving patch:
diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -393,6 +393,22 @@ def parent_handler_match(target_handler, handler_name):
                                 # 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 = search_handler_blocks(handler_name, iterator._play.handlers)
+                                if target_handler is not None:
+                                    found = True
+                                    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:
+                                    # As there may be more than one handler with the notified name as the
+                                    # parent, so we just keep track of whether or not we found one at all
+                                    for target_handler in self._notified_handlers:
+                                        if parent_handler_match(target_handler, handler_name):
+                                            self._notified_handlers[target_handler].append(original_host)
+                                            display.vv("NOTIFIED HANDLER %s" % (target_handler.get_name(),))
+                                            found = True
+
                                 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)
@@ -401,22 +417,6 @@ def parent_handler_match(target_handler, 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 not None:
-                                        found = True
-                                        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:
-                                        # As there may be more than one handler with the notified name as the
-                                        # parent, so we just keep track of whether or not we found one at all
-                                        for target_handler in self._notified_handlers:
-                                            if parent_handler_match(target_handler, handler_name):
-                                                self._notified_handlers[target_handler].append(original_host)
-                                                display.vv("NOTIFIED HANDLER %s" % (target_handler.get_name(),))
-                                                found = True
 
                                 # and if none were found, then we raise an error
                                 if not found:


Test patch: