Repository: ansible-collections/community.general

Issue:
yum_versionlock not idempotent when version provided
### Summary

When I specify that `yum_versionlock` ensures that a package is locked to a specific version, and the package is already locked to that version. `yum_versionlock` reports a state change, even though `yum versionlock list` shows no change. This appears to be a regression, because if I revert back to `community.general` version 3.2, `yum_versionlock` doesn't report a state change.

Here's an example task.

```yaml
---
name: lock iCommands to version 4.2.8
community.general.yum_versionlock:
  name: irods-icommands-4.2.8
  state: present
```


### Issue Type

Bug Report

### Component Name

yum_versionlock

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.12.4]
  config file = /home/tedgin/.ansible.cfg
  configured module search path = ['/home/tedgin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/tedgin/.local/lib/python3.8/site-packages/ansible
  ansible collection location = /home/tedgin/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/tedgin/.local/bin/ansible
  python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
  jinja version = 3.0.2
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /home/tedgin/.local/lib/python3.8/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 4.7.0  

# /home/tedgin/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 4.7.0  
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
ANSIBLE_PIPELINING(/home/tedgin/.ansible.cfg) = True
DEFAULT_TIMEOUT(/home/tedgin/.ansible.cfg) = 20
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /home/tedgin/.ansible_vault_pass.txt
INTERPRETER_PYTHON(/home/tedgin/.ansible.cfg) = /usr/bin/python3
RETRY_FILES_ENABLED(/home/tedgin/.ansible.cfg) = False
```


### OS / Environment

Ansible control node is running either CentOS 7 or Ubuntu 20.04. The managed node is running CentOS 7.

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
---
- name: demo issue
  hosts: provider_configured.dstesting
  tasks:
    - name: lock iCommands to version 4.2.8
      community.general.yum_versionlock:
        name: irods-icommands-4.2.8
        state: present
```


### Expected Results

I expect `yum_versionlock` to respond with `ok`.

### Actual Results

```console (paste below)
$ # Here's what yum thinks is locked before running the task.
$ ssh provider_configured.dstesting yum versionlock list
Loaded plugins: fastestmirror, ovl, versionlock
0:irods-netcdf-icommands-4.2.8.0-1.*
versionlock list done
$ 
$ ansible-playbook demo.yml

PLAY [demo] ********************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************
ok: [provider_configured.dstesting]

TASK [lock iCommands to version 4.2.8] *****************************************************************************
changed: [provider_configured.dstesting]

PLAY RECAP *********************************************************************************************************
provider_configured.dstesting : ok=1   changed=1    unreachable=0    failed=0    skipped=9    rescued=0    ignored=0 

$ # To ensure nothing change, here's what `yum versionlock` thinks is locked after running the task.
$ ssh provider_configured.dstesting yum versionlock list
Loaded plugins: fastestmirror, ovl, versionlock
0:irods-netcdf-icommands-4.2.8.0-1.*
versionlock list done
```


### Code of Conduct

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


Discussion:
Files identified in the description:
* [`plugins/modules/packaging/os/yum_versionlock.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/packaging/os/yum_versionlock.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @aminvakil @florianpaulhoberg
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [`plugins/modules/yum_versionlock.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/yum_versionlock.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @gyptazy
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
any news on this?
There has been no functional change to this module since April 2022, so I guess this hasn't been fixed. There are no active maintainers for it, so if someone wants to contribute a fix, that's definitely welcome!
Hey,

just added PR #6861 which should care about version suffixes, now. Please give it a try and let me know if it works for you. Now, it should be able to handle the plain package name, as well as the package name including its version as a suffix properly.

Cheers,
gyptazy
While looking at the PR I noticed that the module never mentions that passing a version is actually allowed input. So from that point of view, this isn't a bug, but a feature request. That this worked in an earlier version of the module was a random coincidence, also in community.general 3.2.0 the documentation and examples didn't mention that providing a version is supported (see https://github.com/ansible-collections/community.general/blob/3.2.0/plugins/modules/packaging/os/yum_versionlock.py for the code from that version, which includes the documentation and the examples).
Fair enough, I've just adjusted the PR. This should fit and cover this, now.

Resolving patch:
diff --git a/plugins/modules/yum_versionlock.py b/plugins/modules/yum_versionlock.py
--- a/plugins/modules/yum_versionlock.py
+++ b/plugins/modules/yum_versionlock.py
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
-# Copyright (c) 2018, Florian Paul Azim Hoberg <florian.hoberg@credativ.de>
+# Copyright (c) 2018, Florian Paul Azim Hoberg (@gyptazy) <gyptazy@gyptazy.ch>
 #
 # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
 # SPDX-License-Identifier: GPL-3.0-or-later
@@ -25,7 +25,8 @@
 options:
   name:
     description:
-      - Package name or a list of package names with optional wildcards.
+      - Package name or a list of package names with optional version or wildcards.
+      - Specifying versions is supported since community.general 7.2.0.
     type: list
     required: true
     elements: str
@@ -50,7 +51,14 @@
 - name: Prevent Apache / httpd from being updated
   community.general.yum_versionlock:
     state: present
-    name: httpd
+    name:
+    - httpd
+
+- name: Prevent Apache / httpd version 2.4.57-2 from being updated
+  community.general.yum_versionlock:
+    state: present
+    name:
+    - httpd-0:2.4.57-2.el9
 
 - name: Prevent multiple packages from being updated
   community.general.yum_versionlock:
@@ -111,22 +119,30 @@ def get_versionlock_packages(self):
     def ensure_state(self, packages, command):
         """ Ensure packages state """
         rc, out, err = self.module.run_command([self.yum_bin, "-q", "versionlock", command] + packages)
+        # If no package can be found this will be written on stdout with rc 0
+        if 'No package found for' in out:
+            self.module.fail_json(msg=out)
         if rc == 0:
             return True
         self.module.fail_json(msg="Error: " + to_native(err) + to_native(out))
 
 
 def match(entry, name):
+    match = False
     m = NEVRA_RE_YUM.match(entry)
     if not m:
         m = NEVRA_RE_DNF.match(entry)
     if not m:
         return False
-    return fnmatch(m.group("name"), name)
+    if fnmatch(m.group("name"), name):
+        match = True
+    if entry.rstrip('.*') == name:
+        match = True
+    return match
 
 
 def main():
-    """ start main program to add/remove a package to yum versionlock"""
+    """ start main program to add/delete a package to yum versionlock """
     module = AnsibleModule(
         argument_spec=dict(
             state=dict(default='present', choices=['present', 'absent']),


Test patch: