Repository: ansible/ansible

Issue:
Ansible returns error in Yum module with state=absent and package is not installed
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE

 - Bug Report


##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->

Yum

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
2.4.3.0
```

##### CONFIGURATION


##### OS / ENVIRONMENT
Linux 

##### SUMMARY

Yum fails when it shouldn't

##### STEPS TO REPRODUCE

Given the following tasks:

```
- name: install elrepo gpg key
  rpm_key: key=" https://www.elrepo.org/RPM-GPG-KEY-elrepo.org" state=present

- name: install elrepo repository
  yum: name=" http://www.elrepo.org/elrepo-release-{{ elrepo_version }}.el7.elrepo.noarch.rpm" state=present

# for concourse to work correctly, kernel > 4.10 is required.

- name: uninstall default kernel and tools
  yum: name="{{ item }}" state=absent
  with_items:
    - kernel
    - kernel-tools
    - kernel-devel
    - kernel-tools-libs

- name: install elrepo mainline kernel
  yum: name="{{ item }}" state=present enablerepo="elrepo-kernel" update_cache=yes
  register: mainline_installed
  with_items:
    - kernel-ml
    - kernel-ml-tools
    - kernel-ml-devel
    - kernel-ml-tools-libs
```

Will result in an error when uninstalling kernel, kernel-tools, kernels-devel, and kernel-tools-libs

```
failed: [jeroen-k8-test-lb-01] (item=[u'kernel', u'kernel-tools', u'kernel-devel', u'kernel-tools-libs']) => {"changed": false, "failed": true, "item": ["kernel", "kernel-tools", "kernel-devel", "kernel-tools-libs"], "msg": "", "rc": 0, "results": ["kernel-devel is not installed", "Loaded plugins: fastestmirror, remove-with-leaves\nResolving Dependencies\n--> Running transaction check\n---> Package kernel.x86_64 0:3.10.0-514.26.2.el7 will be erased\n---> Package kernel-tools.x86_64 0:3.10.0-693.11.6.el7 will be erased\n---> Package kernel-tools-libs.x86_64 0:3.10.0-693.11.6.el7 will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package               Arch       Version                    Repository    Size\n================================================================================\nRemoving:\n kernel                x86_64     3.10.0-514.26.2.el7        @updates     148 M\n kernel-tools          x86_64     3.10.0-693.11.6.el7        @updates     259 k\n kernel-tools-libs     x86_64     3.10.0-693.11.6.el7        @updates      18 k\n\nTransaction Summary\n================================================================================\nRemove  3 Packages\n\nInstalled size: 148 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Erasing    : kernel-tools-3.10.0-693.11.6.el7.x86_64                      1/3 \n  Erasing    : kernel-3.10.0-514.26.2.el7.x86_64                            2/3 \n  Erasing    : kernel-tools-libs-3.10.0-693.11.6.el7.x86_64                 3/3 \n  Verifying  : kernel-tools-libs-3.10.0-693.11.6.el7.x86_64                 1/3 \n  Verifying  : kernel-tools-3.10.0-693.11.6.el7.x86_64                      2/3 \n  Verifying  : kernel-3.10.0-514.26.2.el7.x86_64                            3/3 \n\nRemoved:\n  kernel.x86_64 0:3.10.0-514.26.2.el7                                           \n  kernel-tools.x86_64 0:3.10.0-693.11.6.el7                                     \n  kernel-tools-libs.x86_64 0:3.10.0-693.11.6.el7                                \n\nComplete!\n"]}
```

If I understand correctly, it fails because kernel-devel is not installed. Since this matches the condition of being "absent", why is this considered an error?



Discussion:
Files identified in the description:
* [lib/ansible/modules/packaging/os/yum.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/packaging/os/yum.py)

If these files are inaccurate, 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/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Akasurde @berenddeschouwer @mkrizek @verm666
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Okay, this situation is worse than I initially thought. I thought by adding something like ignore_errors=true on the 3rd task, it would be fine, but that is certainly not the case.

When I run it a second time, I get this:

```
failed: [jeroen-k8-test-master-01] (item=[u'kernel', u'kernel-tools', u'kernel-devel', u'kernel-tools-libs']) => {"changed": false, "item": ["kernel", "kernel-tools", "kernel-devel", "kernel-tools-libs"], "msg": "Skipping the running kernel: kernel-ml-4.14.11-1.el7.elrepo.x86_64\n", "rc": 0, "results": ["kernel-tools is not installed", "kernel-tools-libs is not installed", "Loaded plugins: fastestmirror, remove-with-leaves\nResolving Dependencies\n--> Running transaction check\n---> Package kernel-ml-devel.x86_64 0:4.15.0-1.el7.elrepo will be erased\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package            Arch      Version                   Repository         Size\n================================================================================\nRemoving:\n kernel-ml-devel    x86_64    4.15.0-1.el7.elrepo       @elrepo-kernel     44 M\n\nTransaction Summary\n================================================================================\nRemove  1 Package\n\nInstalled size: 44 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Erasing    : kernel-ml-devel-4.15.0-1.el7.elrepo.x86_64                   1/1 \n  Verifying  : kernel-ml-devel-4.15.0-1.el7.elrepo.x86_64                   1/1 \n\nRemoved:\n  kernel-ml-devel.x86_64 0:4.15.0-1.el7.elrepo                                  \n\nComplete!\n"]}
```

So instead of uninstalling "kernel-devel" (which was no longer present), Ansible decides to uninstall the package " kernel-ml-devel" instead? How is this possible?


Hello and thank you for the report, I'm going to be looking into this but was curious if you wouldn't mind posting the contents of your `/etc/yum.conf` file, any files found in `/etc/yum/protected.d/`, and the `.repo` file that provides the `kernel-ml`  from the remote system?

I'd like to rule out the possibility of a yum configuration issue before attempting to triage the Ansible module further because I've seen similar behavior when external repo release rpms inject things into various configs.

needs_info

cc @kustodian
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@jeroenjacobs1205 `kernel-ml-devel` provides `kernel-devel` and because it's the only package which provides `kernel-devel` it's the one that is selected to be removed. This is a yum limitation and not a module limitation.

Regarding your failure when removing older kernels, the problem is with the `kernel` package, because the package of the running kernel cannot be removed (even thought I don't see that in your output). Yum checks after removing packages if they were actually removed, and because a running kernel cannot be removed, the module would fail, even though it doesn't print the real reason why it failed and even `rc = 0`, so we should probably change this. @maxamillion what do you think of just adding a simple `error` field with the message of the problem e.g. `Could not remove package(s): ['kernel-3.10.0-693.2.2.el7.x86_64']"`, and possible set `rc = 1` in this case?

Your use case is very particular. If this is a one time playbook which removes an old kernel, installs a new one, and reboots the hosts, your playbook should be fine, but you should probably remove the old kernel after a reboot. If you want to make it idempotent, you will have to do some queries with e.g. `rpm -qa kernel-devel` and depending on the output uninstall kernel-devel or not.


@jeroenjacobs1205 This issue is waiting for your response. Please respond or the issue will be closed.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: needs_info_base --->
@kustodian I think your proposed error reporting improvement to handle this scenario is a good idea. +1
@maxamillion I'll make a PR for that change then.

Resolving patch:
diff --git a/lib/ansible/modules/packaging/os/yum.py b/lib/ansible/modules/packaging/os/yum.py
--- a/lib/ansible/modules/packaging/os/yum.py
+++ b/lib/ansible/modules/packaging/os/yum.py
@@ -1073,6 +1073,10 @@ def remove(self, items, repoq):
                     installed = self.is_installed(repoq, pkg)
 
                 if installed:
+                    # Return a mesage so it's obvious to the user why yum failed
+                    # and which package couldn't be removed. More details:
+                    # https://github.com/ansible/ansible/issues/35672
+                    res['msg'] = "Package '%s' couldn't be removed!" % pkg
                     self.module.fail_json(**res)
 
             res['changed'] = True
@@ -1306,7 +1310,6 @@ def latest(self, items, repoq):
         return res
 
     def ensure(self, repoq):
-
         pkgs = self.names
 
         # autoremove was provided without `name`


Test patch: