Passing nested arguments to a module

Hi,
I’m trying to extend the ZFS module to add support for user properties.

My plan was to add a new module argument, called (guess what)
`user_properties` which accepts a list of dictionaries:

[{key: 'my.module.name:key-name', value: 'whatever'}, ...]

However, I get this error message:

msg: this module requires key=value arguments

Even though I can pass an empty list or a list with string items, like
['a', 'b', 'c'].

Is there a way to do this?

Thank you.

(This is really a question for ansible-devel list)

In the argument_spec of the module that defines the argument, just do

type=‘dict’

It will accept complex arguments as well as quoted key=value pairs.

module_name:
foo:
x: 1
y: “bar”
baz: “glorp”

Is how complex args are passed

Or

module_name: ‘foo=“x=1 y=bar” baz=“glorp”’

This is exactly how the ec2 module in 1.4/1.5 processes and passes instance tags, if you want to take a look.

It also knows how to parse inline JSON, so it is very much a “do what you mean” kind of input.

Michael DeHaan <michael@ansibleworks.com> writes:

(This is really a question for ansible-devel list)

Thank you for pointing it out, I wasn’t sure about that.

In the argument_spec of the module that defines the argument, just do

type='dict'

It will accept complex arguments as well as quoted key=value pairs.

module_name:
  foo:
     x: 1
     y: "bar"
  baz: "glorp"

Is how complex args are passed

Or

module_name: 'foo="x=1 y=bar" baz="glorp"'

This is exactly how the ec2 module in 1.4/1.5 processes and passes instance
tags, if you want to take a look.

It also knows how to parse inline JSON, so it is very much a "do what you
mean" kind of input.

Super. Thanks a lot.

Michael DeHaan <michael@ansibleworks.com> writes:

In the argument_spec of the module that defines the argument, just do

type='dict'

It will accept complex arguments as well as quoted key=value pairs.

module_name:
  foo:
     x: 1
     y: "bar"
  baz: "glorp"

Is how complex args are passed

Or

module_name: 'foo="x=1 y=bar" baz="glorp"'

This is exactly how the ec2 module in 1.4/1.5 processes and passes instance
tags, if you want to take a look.

It also knows how to parse inline JSON, so it is very much a "do what you
mean" kind of input.

It works perfectly with a single item:

zfs:
   state: present
   name: ...
   ...
   user_properties:
       k: v
       ...

But I can’t find a way to iterate over it. Ideally, I’d like to write
something like:

zfs: {{ item }}
with_items: [{name: 'x', state: 'present', user_properties: {...}},
             {name: 'y', state: 'present', mountpoint: '/path/to/mnt', ...} ]

When the property keys are variable but properties are all strings I can
write something like this:

module_name: state={{ item.state }}
             name={{ item.name }}
             {{ 'prop=%s' % item.prop if item.prop is defined else '' }}
             ...

I don’t like it, it’s verbose, and there’s probably a better way to do
it, but it works, if only with simple properties.

So my question becames how to iterate over a list of dictionaries,
possibily nested, with different keys?

Thanks a lot.

You can definitely pass in with_items with a list of hashes.

module_name: foo={{ item.name }} bar={{ item.value }}
with_items:

  • { name: ‘x’, value: ‘y’ }
  • { name: ‘a’, value: ‘b’ }

etc

or with_items: alist_of_hashes

etc

or as you have it, with

with_items: [ { name: ‘x’, value: ‘y’ }, { name: ‘a’, value: ‘b’ } ]

It seems like in your question instead of

zfs: {{ item }}

you might need to pass something more specific?

zfs: {{ item.zfs }}

??

am I misunderstanding the question ?

Michael DeHaan <michael@ansibleworks.com> writes:

You can definitely pass in with_items with a list of hashes.

module_name: foo={{ item.name }} bar={{ item.value }}
with_items:
   - { name: 'x', value: 'y' }
   - { name: 'a', value: 'b' }

etc

or with_items: alist_of_hashes

etc

or as you have it, with

with_items: [ { name: 'x', value: 'y' }, { name: 'a', value: 'b' } ]

It seems like in your question instead of

zfs: {{ item }}

you might need to pass something more specific?

zfs: {{ item.zfs }}

??

am I misunderstanding the question ?

Maybe or, more probably, I can’t explain myself. :slight_smile:

Starting from your example, let’s add an attribute only in the second item:

module_name: foo={{ item.name }} bar={{ item.value }}
with_items:
   - { name: 'x', value: 'y' }
   - { name: 'a', value: 'b', extra: 'xtr' }

How should deal with it? Is adding

{{ 'extra=%s' % item.extra if item.extra is defined else '' }}

the preferred way?

Now let’s say that `extra` is a map:

module_name: foo={{ item.name }} bar={{ item.value }}
with_items:
   - { name: 'x', value: 'y' }
   - { name: 'a', value: 'b', extra: {k: 'v'} }

If I use the `defined` check shown above the module gets a string, I
believe.

I hope I didn’t mess up too much my argument :slight_smile:

Giorgio Valoti <giorgio_v@me.com> writes:

Michael DeHaan <michael@ansibleworks.com> writes:

am I misunderstanding the question ?

Maybe or, more probably, I can’t explain myself. :slight_smile:

Well, I guess I really can’t explain myself! :smiley: No suggestions on this?

Thank you in advance.