Repository: ansible/ansible
Pull request: Allow "role_name : " prefix for notifying handler listen topics
##### SUMMARY

Allow notifying handler listen topics more similarly to handler names.

This handler:
```yaml
- name: handler name
  debug:
  listen: topic1
```
can be now be notified using `topic1`, `role : topic1` if the handler is in a standalone or collection role, and `ns.col.role : topic1` if the role is in a collection.

Fixes #82849

##### ISSUE TYPE

- Bugfix Pull Request

##### ADDITIONAL INFORMATION

<!--- Include additional information to help people understand the change here -->
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->

<!--- Paste verbatim command output below, e.g. before and after your change -->

```paste below

```


Problem statement:
Cannot locate handlers in different role when using 'listen:' instead of handler name
### Summary

I have two handlers with unique names, and a `listen:` statement for both with the same name, so I can call on both with a single statement. They are part of a "configure" role. When I run my "install" role, which imports the "configure" role as a task, I can call on "configure" handlers either directly or with the rolename prefix (eg. `notify: configure : handler_A` or `notify: handler_B`). However, if I try to call on the "listen" name, _only_ the version without the rolename prefix works:

`notify: both_handlers` works

`notify: configure: both_handlers` does _not_ work. It throws the following error:

`ERROR! The requested handler 'configure: both_handlers' was not found in either the main handlers list nor in the listening handlers list`

It seems to me like the prefix version should work exactly the same for actual handler task names as it does for the listener names?

### Issue Type

Bug Report

### Component Name

handlers

### Ansible Version

```console
$ ansible --version
ansible [core 2.16.4]
  config file = /home/maek48/git/anly-infra/ansible.cfg
  configured module search path = ['/home/maek48/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/maek48/git/anly-ansible-collections/.venv/lib/python3.11/site-packages/ansible
  ansible collection location = /home/maek48/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/maek48/git/anly-ansible-collections/.venv/bin/ansible
  python version = 3.11.3 (main, Jun  5 2023, 16:18:46) [GCC 9.4.0] (/home/maek48/git/anly-ansible-collections/.venv/bin/python3)
  jinja version = 3.1.3
  libyaml = True
```


### Configuration

```console
# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
CONFIG_FILE() = /home/REDACTED/git/reponame/ansible.cfg
DEFAULT_LOG_PATH(/home/REDACTED/git/reponame/ansible.cfg) = /app/REDACTED/var/log/ansible/ansible.log
DEFAULT_TIMEOUT(/home/REDACTED/git/reponame/ansible.cfg) = 100000
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /home/REDACTED/git/reponame/secrets/ansible.passwd
EDITOR(env: EDITOR) = vim
HOST_KEY_CHECKING(/home/REDACTED/git/reponame/ansible.cfg) = False

CONNECTION:
==========

paramiko_ssh:
____________
host_key_checking(/home/REDACTED/git/reponame/ansible.cfg) = False
timeout(/home/REDACTED/git/reponame/ansible.cfg) = 100000

ssh:
___
host_key_checking(/home/REDACTED/git/reponame/ansible.cfg) = False
timeout(/home/REDACTED/git/reponame/ansible.cfg) = 100000
```


### OS / Environment

WSL Ubuntu 20.04.6 LTS

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

```


### Expected Results

I expect handlers called by their "listen" name to work the same as when they're called by their actual task name

### Actual Results

```console
Task trying to notify the handler fails immediately with the following error:

`ERROR! The requested handler 'configure: both_handlers' was not found in either the main handlers list nor in the listening handlers list`
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

Hints from issue discussion:
Files identified in the description:

None

If these files are incorrect, please update the `component name` section of the description or use the [component](https://github.com/ansible/ansibotmini#commands) bot command.

<!--- boilerplate: components_banner --->

Commit message: Allow role name prefix for handler task listen topics

For example,

- name: handler name
  debug:
  listen: topic1

can be notified using `topic1`, `role : topic1` if the handler is in a
standalone or collection role, and `ns.col.role: topic1` if the role is
in a collection, the same way handler names work.

changelog

Patch to review:
diff --git a/changelogs/fragments/fix-role-name-handler-prefix-listen.yml b/changelogs/fragments/fix-role-name-handler-prefix-listen.yml
new file mode 100644
index 00000000000000..c7744885fd18ae
--- /dev/null
+++ b/changelogs/fragments/fix-role-name-handler-prefix-listen.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - Fix notifying role handlers by listen keyword topics with the "role_name : " prefix (https://github.com/ansible/ansible/issues/82849).
diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py
index 2fe9313089e6a0..efd69efe9b45a1 100644
--- a/lib/ansible/plugins/strategy/__init__.py
+++ b/lib/ansible/plugins/strategy/__init__.py
@@ -553,12 +553,19 @@ def search_handlers_by_notification(self, notification: str, iterator: PlayItera
         seen = []
         for handler in handlers:
             if listeners := handler.listen:
-                if notification in handler.get_validated_value(
+                listeners = handler.get_validated_value(
                     'listen',
                     handler.fattributes.get('listen'),
                     listeners,
                     templar,
-                ):
+                )
+                if handler._role is not None:
+                    for listener in listeners.copy():
+                        listeners.extend([
+                            handler._role.get_name(include_role_fqcn=True) + ' : ' + listener,
+                            handler._role.get_name(include_role_fqcn=False) + ' : ' + listener
+                        ])
+                if notification in listeners:
                     if handler.name and handler.name in seen:
                         continue
                     seen.append(handler.name)
diff --git a/test/integration/targets/handlers/collections/ansible_collections/ns/col/roles/test_handlers_listen/handlers/main.yml b/test/integration/targets/handlers/collections/ansible_collections/ns/col/roles/test_handlers_listen/handlers/main.yml
new file mode 100644
index 00000000000000..0658f7e117d05a
--- /dev/null
+++ b/test/integration/targets/handlers/collections/ansible_collections/ns/col/roles/test_handlers_listen/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: test notifying listen namespaced by collection + role
+  set_fact:
+    notify_listen_in_specific_collection_role: True
+  listen: notify_specific_collection_role_listen
diff --git a/test/integration/targets/handlers/roles/test_handlers_listen/handlers/main.yml b/test/integration/targets/handlers/roles/test_handlers_listen/handlers/main.yml
index 3bfd82a25464a2..d5cdbac2825686 100644
--- a/test/integration/targets/handlers/roles/test_handlers_listen/handlers/main.yml
+++ b/test/integration/targets/handlers/roles/test_handlers_listen/handlers/main.yml
@@ -8,3 +8,8 @@
   set_fact:
     notify_listen_in_role_4: True
   listen: notify_listen_in_role
+
+- name: test notifying listen namespaced by the role
+  set_fact:
+    notify_listen_in_specific_role: True
+  listen: notify_specific_role_listen
diff --git a/test/integration/targets/handlers/test_handlers_listen.yml b/test/integration/targets/handlers/test_handlers_listen.yml
index dd2cd87dd9a506..3baaab6ee6ece9 100644
--- a/test/integration/targets/handlers/test_handlers_listen.yml
+++ b/test/integration/targets/handlers/test_handlers_listen.yml
@@ -99,6 +99,7 @@
   gather_facts: false
   roles:
     - role: test_handlers_listen
+    - role: ns.col.test_handlers_listen
   tasks:
     - name: test notify handlers listen in roles
       command: uptime
@@ -113,6 +114,20 @@
           - "notify_listen_ran_4_3 is defined"
           - "notify_listen_in_role_4 is defined"
           - "notify_listen_from_role_4 is defined"
+    - name: test notifying handlers using the role name prefix
+      command: /bin/true
+      notify:
+        - 'test_handlers_listen : notify_specific_role_listen'
+        - 'test_handlers_listen : notify_specific_collection_role_listen'
+    - meta: flush_handlers
+    - assert:
+        that:
+          - notify_listen_in_specific_collection_role is defined
+          - notify_listen_in_specific_role is defined
+    - name: test notifying the collection listener by the role's FQCN also works
+      command: /bin/true
+      notify:
+        - 'ns.col.test_handlers_listen : notify_specific_collection_role_listen'
   handlers:
     - name: notify_listen_ran_4_1
       set_fact:


Code review comments:
diff_hunk: @@ -113,6 +114,20 @@
           - "notify_listen_ran_4_3 is defined"
           - "notify_listen_in_role_4 is defined"
           - "notify_listen_from_role_4 is defined"
+    - name: test notifying handlers using the role name prefix
+      command: /bin/true
path: test/integration/targets/handlers/test_handlers_listen.yml
text: @author:
```suggestion
      command: uptime
```

diff_hunk: @@ -113,6 +114,20 @@
           - "notify_listen_ran_4_3 is defined"
           - "notify_listen_in_role_4 is defined"
           - "notify_listen_from_role_4 is defined"
+    - name: test notifying handlers using the role name prefix
+      command: /bin/true
+      notify:
+        - 'test_handlers_listen : notify_specific_role_listen'
+        - 'test_handlers_listen : notify_specific_collection_role_listen'
+    - meta: flush_handlers
+    - assert:
+        that:
+          - notify_listen_in_specific_collection_role is defined
+          - notify_listen_in_specific_role is defined
+    - name: test notifying the collection listener by the role's FQCN also works
+      command: /bin/true
path: test/integration/targets/handlers/test_handlers_listen.yml
text: @author:
```suggestion
      command: uptime
```