Problem with: yum module, with_items, composite-variable

Variable resolution seems to fail on the second iteration of a with_items used with yum, but only if the variable is “composite” i.e. an expression involving other variables.

Here is the playbook:

vars:
pkg_list:

  • jdk
  • httpd

pkg_versions: { “jdk”: “1.6.0_31-fcs”, “httpd”: “2.2.15-15.el6.centos.1” }

tasks:

  • name: Composite-Variable resolution works fine with the copy module
    action: copy src=/tmp/${item}-${pkg_versions.${item}} dest=/tmp
    with_items: ${pkg_list}

  • name: … but does not work with the yum module
    action: yum pkg=${item}-${pkg_version.${item}} state=installed ## This doesn’t work
    #action: yum pkg=${item} state=installed ## This works
    with_items: ${pkg_list}

The output is:

[ansible@dev-autoserver-01 ~]$ ansible-playbook /etc/ansible/sys-plays/testyumbug.yml -v

PLAY [Test Playbook] *********************

GATHERING FACTS *********************
ok: [10.120.210.61]

TASK: [Composite-Variable resolution works fine with the copy module] *********************
ok: [10.120.210.61] => (item=jdk) => {“changed”: false, “dest”: “/tmp/jdk-1.6.0_31-fcs”, “group”: “root”, “item”: “jdk”, “md5sum”: “d41d8cd98f00b204e9800998ecf8427e”, “mode”: “0644”, “owner”: “root”, “src”: “/home/ansible/.ansible/tmp/ansible-1362162055.67-15730396780203/jdk-1.6.0_31-fcs”, “state”: “file”}
ok: [10.120.210.61] => (item=httpd) => {“changed”: false, “dest”: “/tmp/httpd-2.2.15-15.el6.centos.1”, “group”: “root”, “item”: “httpd”, “md5sum”: “d41d8cd98f00b204e9800998ecf8427e”, “mode”: “0644”, “owner”: “root”, “src”: “/home/ansible/.ansible/tmp/ansible-1362162056.13-226327540995443/httpd-2.2.15-15.el6.centos.1”, “state”: “file”}

TASK: [… but does not work with the yum module] *********************
failed: [10.120.210.61] => (item=jdk,httpd) => {“changed”: true, “failed”: true, “item”: “jdk,httpd”, “rc”: 0, “results”: [“\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n jdk x86_64 2000:1.6.0_38-fcs srt 56 M\n\nTransaction Summary\n================================================================================\nInstall 1 Package(s)\n\nTotal download size: 56 M\nInstalled size: 121 M\nUnpacking JAR files…\n\trt.jar…\n\tjsse.jar…\n\tcharsets.jar…\n\ttools.jar…\n\tlocaledata.jar…\n\tplugin.jar…\n\tjavaws.jar…\n\tdeploy.jar…\n\nInstalled:\n jdk.x86_64 2000:1.6.0_38-fcs \n\n”]}
msg: No Package matching ‘httpd-${pkg_version.${item}}’ found available, installed or updated

FATAL: all hosts have already failed – aborting

I don’t have a workaround yet, other than to either try to debug the yum module, or code my own replacement module.
(The above is a completely contrived example just to reproduce the issue - the actual use-case is more involved and involves proprietary rpm’s; it’s not feasible to unroll the loop - I need to work with a list )

It just looks like something is missing in your hash to me. Here's a
much much cleaner way to do that:

vars:
  packages:
      - { name: 'foo', version: 'xyz' }
      - { name: 'bar', version: 'xyz' }

tasks:
   - yum: name=${item.name}-${item.version} state=installed
     with_items: $packages

In general, when you start to use variables in ways where they all
nest like that, it's time to simplify things. Ansible playbooks
should be very easy to read.

I’m on 1.0, and I experimented with your suggestion:

vars:
pkgs:

  • { name: “jdk”, version: “1.6.0_31-fcs” }
  • { name: “httpd”, version: “2.2.15-15.el6.centos.1” }

tasks:

  • name: New style variable resolution
    action: copy src=/tmp/${item.name}-${item.version} dest=/tmp
    with_items: $pkgs

  • name: … but does not work with the yum module
    action: yum name=${item.name}-${item.version} state=installed
    with_items: $pkgs

I got the following:

LAY [Test Playbook] *********************

GATHERING FACTS *********************
ok: [10.120.210.61]

TASK: [New style variable resolution] *********************
ok: [10.120.210.61] => (item={‘version’: ‘1.6.0_31-fcs’, ‘name’: ‘jdk’}) => {“changed”: false, “dest”: “/tmp/jdk-1.6.0_31-fcs”, “group”: “root”, “item”: {“name”: “jdk”, “version”: “1.6.0_31-fcs”}, “md5sum”: “d41d8cd98f00b204e9800998ecf8427e”, “mode”: “0644”, “owner”: “root”, “src”: “/home/ansible/.ansible/tmp/ansible-1362168335.16-67188730965918/jdk-1.6.0_31-fcs”, “state”: “file”}
ok: [10.120.210.61] => (item={‘version’: ‘2.2.15-15.el6.centos.1’, ‘name’: ‘httpd’}) => {“changed”: false, “dest”: “/tmp/httpd-2.2.15-15.el6.centos.1”, “group”: “root”, “item”: {“name”: “httpd”, “version”: “2.2.15-15.el6.centos.1”}, “md5sum”: “d41d8cd98f00b204e9800998ecf8427e”, “mode”: “0644”, “owner”: “root”, “src”: “/home/ansible/.ansible/tmp/ansible-1362168335.63-42039443662886/httpd-2.2.15-15.el6.centos.1”, “state”: “file”}

TASK: [… but does not work with the yum module] *********************

fatal: [10.120.210.61] => Traceback (most recent call last):
File “/usr/lib/python2.6/site-packages/ansible/runner/init.py”, line 237, in _executor
exec_rc = self._executor_internal(host)
File “/usr/lib/python2.6/site-packages/ansible/runner/init.py”, line 287, in _executor_internal
inject[‘item’] = “,”.join(items)
TypeError: sequence item 0: expected string, dict found

FATAL: all hosts have already failed – aborting

Could this be a 1.0 vs 1.1 issue?

There is no 1.1 issue, I just gave you a bad example syntax.

name="foo=1.0"

Not a dash.

Asad Khan wrote:

I'm on 1.0, and I experimented with your suggestion:

  vars:
     pkgs:
       - { name: "jdk", version: "1.6.0_31-fcs" }
       - { name: "httpd", version: "2.2.15-15.el6.centos.1" }

  tasks:
     - name: New style variable resolution
       action: copy src=/tmp/${item.name}-${item.version} dest=/tmp
       with_items: $pkgs

    - name: .. but does not work with the yum module
      action: yum name=${item.name}-${item.version} state=installed
      with_items: $pkgs

I got the following:

LAY [Test Playbook] *********************

GATHERING FACTS *********************
ok: [10.120.210.61]

TASK: [New style variable resolution] *********************
ok: [10.120.210.61] => (item={'version': '1.6.0_31-fcs', 'name': 'jdk'})
=>
{"changed": false, "dest": "/tmp/jdk-1.6.0_31-fcs", "group": "root",
"item": {"name": "jdk", "version": "1.6.0_31-fcs"}, "md5sum":
"d41d8cd98f00b204e9800998ecf8427e", "mode": "0644", "owner": "root",
"src":
"/home/ansible/.ansible/tmp/ansible-1362168335.16-67188730965918/jdk-1.6.0_31-fcs",
"state": "file"}
ok: [10.120.210.61] => (item={'version': '2.2.15-15.el6.centos.1', 'name':
'httpd'}) => {"changed": false, "dest":
"/tmp/httpd-2.2.15-15.el6.centos.1", "group": "root", "item": {"name":
"httpd", "version": "2.2.15-15.el6.centos.1"}, "md5sum":
"d41d8cd98f00b204e9800998ecf8427e", "mode": "0644", "owner": "root",
"src":
"/home/ansible/.ansible/tmp/ansible-1362168335.63-42039443662886/httpd-2.2.15-15.el6.centos.1",
"state": "file"}

TASK: [.. but does not work with the yum module] *********************
fatal: [10.120.210.61] => Traceback (most recent call last):
  File "/usr/lib/python2.6/site-packages/ansible/runner/__init__.py", line
237, in _executor
    exec_rc = self._executor_internal(host)
  File "/usr/lib/python2.6/site-packages/ansible/runner/__init__.py", line
287, in _executor_internal
    inject['item'] = ",".join(items)
TypeError: sequence item 0: expected string, dict found

This is from the yum/apt optimization for with_items. Can you file an issue
on Github for it?

Daniel

This is from the yum/apt optimization for with_items. Can you file an issue
on Github for it?

While this is the root cause, it doesn't detract from that being
something we can simplify and not even get into that corner :slight_smile:

OK, doing so

Ticket here:

https://github.com/ansible/ansible/issues/2258

Fixed! Yeah the with_items optimization shouldn't run in this case.
And now it doesn't, you should be good to go!

Nice. I will test this out soon, thank you!

Hi,

I have a slightly different case - I’m trying to composite package name with full path to RPM

  • name: installing required packages
    action: yum name=$tmp_dir/$item state=installed
    with_items:
  • packageA.rpm
  • packageB.rpm
    sudo: yes

Name will be resolved as /tmp/packageA.rpm,packageB.rpm which correctly resolves only for the first package.

Is there any better way to install multiple rpm’s using absolute path?

The yum module automatically combines with_items to avoid multiple transactions, so no, that won’t work.

Since you are installing local RPMs, switch to the command or shell module and call RPM, perhaps.

–Michael

OR do this:
- name: installing required packages
  action: yum name=$item state=installed
  with_items:
    - $tmp_dir/packageA.rpm
    - $tmp_dir/packageB.rpm
  sudo: yes

-sv

Thanks for suggestions.
Initially i was using with_items: - $tmp_dir/packageA.rpm but this gives a lot of duplicated prefixes.
Also as I try to move all configuration items into vars files I ended up with nice sets which can be overriden on role level.

vars:


> set_version: 8
> rpmset_8:
>   - packageA.rpm
>   - packageB.rpm
> rpmset: $rpmset_{{ set_version }}

 
task:  

> - name: installing rpms
>   command: /bin/rpm -Uhv $tmp_dir/{{ item }}
>   with_items: $rpmset

Drawback - multiple transactions. Can yum module support something optional like chdir parameter?

No chdir parameter.

I would go with Seth’s suggestion above as it solves the with_items question, and also does one transaction.