Repository: ansible/ansible-modules-core

Issue:
Yum module's state=present with URL/path of RPM won't update a package (behavior change)
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

yum
##### ANSIBLE VERSION

```
ansible 2.1.2.0 (stable-2.1 fc3efdb057) last updated 2016/08/25 08:46:54 (GMT +1000)
  lib/ansible/modules/core: (detached HEAD edbd108b15) last updated 2016/08/25 11:37:11 (GMT +1000)
  lib/ansible/modules/extras: (detached HEAD 503158a82c) last updated 2016/08/25 09:05:59 (GMT +1000)
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

all default
##### OS / ENVIRONMENT

Host: RHEL 7.2 workstation
Target: RHEL 6.8 Server
##### SUMMARY

yum module with state=present, name=<url> will no longer install the RPM from the URL if a package of the same name is already installed.  Instead, it considers there are no changes to be made.

It worked on some earlier versions of ansible, so a playbook which previously worked fine may now silently not install packages when expected.
##### STEPS TO REPRODUCE
- ensure bash older than bash-4.1.2-40.el6.x86_64.rpm is installed on target system
- try yum module against target system like:

```
ansible <host args> -m yum -a 'state=present name=http://mirror.centos.org/centos/6/os/x86_64/Packages/bash-4.1.2-40.el6.x86_64.rpm'
```
##### EXPECTED RESULTS

bash-4.1.2-40.el6.x86_64.rpm is downloaded from URL and installed
##### ACTUAL RESULTS

Package isn't installed:

```
<hostname> | SUCCESS => {
    "changed": false, 
    "msg": "", 
    "rc": 0, 
    "results": []
}
```
##### ADDITIONAL INFO

The problem goes away if I revert c8222bc8db31c4b46645912c0f13c1632333eae4.

The docs are a little vague on the expected behavior, but based on the commit and referenced issue I don't think it was an intentional behavior change.



Discussion:
<!-- @rohanpm: Thanks for filing this bug report! -->

@ansible ping, this issue is waiting for your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_bug_new --->

I haven't confirmed this properly, but I have a similar issue when installing a package from a local file. So the bug may affect installing from a local file as well as installing from a url.

Demonstration playbook for a vanilla centos 7 host:

```
- hosts: centos1

  tasks:
  - yum:
      name: "http://mirror.centos.org/centos/7/os/x86_64/Packages/bash-4.2.46-19.el7.x86_64.rpm"
      state: present

  - get_url:
      url: "http://mirror.centos.org/centos/7/os/x86_64/Packages/bash-4.2.46-19.el7.x86_64.rpm"
      dest: /tmp/bash-4.2.46-19.el7.x86_64.rpm

  - yum:
      name: /tmp/bash-4.2.46-19.el7.x86_64.rpm
      state: present
```

Results:

```
$ ansible-playbook yum_update.yml -vv
Using /home/wthames/tmp/ansible/ansible.cfg as config file

PLAYBOOK: yum_update.yml *******************************************************
1 plays in yum_update.yml

PLAY [centos1] *****************************************************************

TASK [setup] *******************************************************************
ok: [centos1]

TASK [yum] *********************************************************************
task path: /home/wthames/tmp/ansible/yum_update.yml:4
ok: [centos1] => {"changed": false, "msg": "", "rc": 0, "results": []}

TASK [get_url] *****************************************************************
task path: /home/wthames/tmp/ansible/yum_update.yml:8
changed: [centos1] => {"changed": true, "checksum_dest": null, "checksum_src": "474448fbf44e1cdc665ed679c8c26d8d17a6d6ac", "dest": "/tmp/bash-4.2.46-19.el7.x86_64.rpm", "gid": 1000, "group": "vagrant", "md5sum": "4a9402c44666824430f297ea2d31ed92", "mode": "0664", "msg": "OK (1034452 bytes)", "owner": "vagrant", "secontext": "unconfined_u:object_r:user_tmp_t:s0", "size": 1034452, "src": "/tmp/tmp5P3nxa", "state": "file", "uid": 1000, "url": "http://mirror.centos.org/centos/7/os/x86_64/Packages/bash-4.2.46-19.el7.x86_64.rpm"}


TASK [yum] *********************************************************************
task path: /home/wthames/tmp/ansible/yum_update.yml:12
ok: [centos1] => {"changed": false, "msg": "", "rc": 0, "results": []}

PLAY RECAP *********************************************************************
centos1                    : ok=4    changed=1    unreachable=0    failed=0   
```

No upgrade actually occurred in spite of two yum tasks asserting that there was no need to do so:

```
$ ansible -m command -a 'rpm -q bash' centos1
centos1 | SUCCESS | rc=0 >>
bash-4.2.46-12.el7.x86_64
```

Attention @brkim :)


Resolving patch:
diff --git a/packaging/os/yum.py b/packaging/os/yum.py
--- a/packaging/os/yum.py
+++ b/packaging/os/yum.py
@@ -507,24 +507,11 @@ def local_nvra(module, path):
     finally:
         os.close(fd)
 
-    return '%s-%s-%s.%s' % (header[rpm.RPMTAG_NAME], 
+    return '%s-%s-%s.%s' % (header[rpm.RPMTAG_NAME],
                             header[rpm.RPMTAG_VERSION],
                             header[rpm.RPMTAG_RELEASE],
                             header[rpm.RPMTAG_ARCH])
-    
-def local_name(module, path):
-    """return package name of a local rpm passed in"""
 
-    ts = rpm.TransactionSet()
-    ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
-    fd = os.open(path, os.O_RDONLY)
-    try:
-        header = ts.hdrFromFdno(fd)
-    finally:
-        os.close(fd)
-
-    return header[rpm.RPMTAG_NAME]
-    
 def pkg_to_dict(pkgstr):
 
     if pkgstr.strip():
@@ -599,10 +586,10 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
                 res['msg'] += "No Package file matching '%s' found on system" % spec
                 module.fail_json(**res)
 
-            pkg_name = local_name(module, spec)
+            nvra = local_nvra(module, spec)
 
             # look for them in the rpmdb
-            if is_installed(module, repoq, pkg_name, conf_file, en_repos=en_repos, dis_repos=dis_repos):
+            if is_installed(module, repoq, nvra, conf_file, en_repos=en_repos, dis_repos=dis_repos):
                 # if they are there, skip it
                 continue
             pkg = spec
@@ -611,8 +598,8 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
         elif '://' in spec:
             # download package so that we can check if it's already installed
             package = fetch_rpm_from_url(spec, module=module)
-            pkg_name = local_name(module, package)
-            if is_installed(module, repoq, pkg_name, conf_file, en_repos=en_repos, dis_repos=dis_repos):
+            nvra = local_nvra(module, package)
+            if is_installed(module, repoq, nvra, conf_file, en_repos=en_repos, dis_repos=dis_repos):
                 # if it's there, skip it
                 continue
             pkg = package


Test patch: