Question about passing a list to a module

I’d like to enhance the ec2 module to support creating/attaching volumes.

Updating it was rather simple, but I am having trouble passing volumes to it, due to the way vars get parsed (i think).

In the playbook, I want to do something like

vars:

  • volumes:
  • { device: “/dev/sdf”, size: 1 }
  • { device: “/dev/sdg”, size: 1 }
  • { device: “/dev/sdh”, size: 1, otherattrib: value }
    tasks:
  • local_action: ec2 keypair=“test” group=“test” instance_type=“t1.micro” image=“ami-3d4ff254” wait=true volumes=$volumes

But that fails with:

File “/home/ubuntu/ansible/lib/ansible/utils/template.py”, line 222, in varReplace
replacement = “,”.join(replacement)
TypeError: sequence item 0: expected string, dict found

So I hacked lib/ansible/utils/template.py:

if expand_lists and isinstance(replacement, (list, tuple)):
if isinstance(replacement, (list)):
parts =
for item in replacement:
if item is dict:
pass
else:
parts.append(item)
replacement = parts.str()
else:
replacement = “,”.join(replacement)

Which was enough to let me move on… but I still am dealing with a string instead of a list of dictionaries like I was hoping for. So I wound up using ast.literal_eval.

So my question is, is there a way to pass a variable (which is a list) without it getting converted to a string?

New ec2 module code:

I'd like to enhance the ec2 module to support creating/attaching volumes.

You may be interested to see the ec2vol module I just pushed today
from Lester Wade!

Updating it was rather simple, but I am having trouble passing volumes to
it, due to the way vars get parsed (i think).

In the playbook, I want to do something like

  vars:
  - volumes:
    - { device: "/dev/sdf", size: 1 }
    - { device: "/dev/sdg", size: 1 }
    - { device: "/dev/sdh", size: 1, otherattrib: value }
  tasks:
  - local_action: ec2 keypair="test" group="test" instance_type="t1.micro"
image="ami-3d4ff254" wait=true volumes=$volumes

But that fails with:

  File "/home/ubuntu/ansible/lib/ansible/utils/template.py", line 222, in
varReplace
    replacement = ",".join(replacement)
TypeError: sequence item 0: expected string, dict found

Us exposing tracebacks and not errors for humans isn't cool, but it's
true it didn't expect a list there. In general you can't do that.

What you want is:

- location_action: foo arg=$item
  with_items: $listvar

And yes, that will call your module multiple times.

You can't pass complex variables to modules -- that's coming super
soon now (this weekend) - although even then the module has to be
coded to do something with that data.

So I hacked lib/ansible/utils/template.py:

        if expand_lists and isinstance(replacement, (list, tuple)):
            if isinstance(replacement, (list)):
                parts =
                for item in replacement:
                    if item is dict:
                        pass
                    else:
                        parts.append(item)
                replacement = parts.__str__()
            else:
                replacement = ",".join(replacement)

Which was enough to let me move on... but I still am dealing with a string
instead of a list of dictionaries like I was hoping for. So I wound up using
ast.literal_eval.

So my question is, is there a way to pass a variable (which is a list)
without it getting converted to a string?

with_items is what you want.

Of course I started coding right before your merges… that’s life.

The ec2_vol module will work, though not as I had intended. I will now be provisioning the volumes in my setup playbook, instead of my provisioning playbook.

To provision, I have different values for instances and volumes based on whether this is dev, staging or prod, but it is basically something like:

vars:

  • instances:
  • { name: db1, type: db, instance: $instance_type, image: $image_type }
  • { name: db2, type: db, instance: $instance_type, image: $image_type }
  • { name: dbx, type: db, instance: $instance_type, image: $image_type }
  • volumes:
  • { device: “/dev/sdf”, size: 1 }
  • { device: “/dev/sdg”, size: 1 }
  • { device: “/dev/sdh”, size: 1, otherattrib: value }

tasks:

  • local_action: ec2 …
    register: instres
    with_items: ${instances}

  • local_action: wait_for host=${item.public_dns_name} port=22 delay=20 timeout=300
    with_items: ${instres.results[0].instances}

So to use ec2_vol here wouldn’t work since I would need to loop over 2 lists, which I don’t think is possible.

  • local_action: ec2_vol …
    with_items: ${instres.results[0].instances}

with_items: ${volumes}

won’t work.

I was trying not to introduce yet another module. Namespaces/subdirectories will be really nice.

Complex variables sounds awesome. Can’t wait to look.