Discussion:
with_dict and Jinja templates - Error
Matt Silverlock
2014-08-16 11:38:23 UTC
Permalink
Hi all,

Trying to pull together my understanding of Jinja's looping constructs,
Ansible's with_dict and from what I've seen out in the wild (i.e. on
GitHub). I've taken a look around at some other examples but can't seem to
adapt them to my approach
(i.e. https://github.com/timmahoney/ansible-redhat-extra-repos/blob/master/defaults/main.yml
is close to what I'm after)

At the moment I'm attempting to loop over a YAML dict of OS user <-> DB
user mappings (for pg_ident.conf) and am running into an error:

*# host_vars/<hostname>*
postgres_users:
baltar:
os: "{{ username }}"
db: "{{ db_name }}"
caprica:
os: "{{ username }}"
db: "{{ db_name }}"

*# pg_ident.conf.j2*

{% for user in postgres_users %}
{{ user.db }} {{ user.os }} {{ user.key }}
{% endfor %}

*# roles/postgresql/tasks/postgres.yml*

- name: copy postgres ident maps
template: src=pg_ident.conf.j2
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640
register: postgres_ident
with_dict: postgres_users

*# error message*

TASK: [postgres | copy postgres ident maps]
***********************************
fatal: [default] => {'msg': "AnsibleUndefinedVariable: One or more
undefined variables: 'str object' has no attribute 'db'", 'failed': True}
fatal: [default] => {'msg': 'One or more items failed.', 'failed': True,
'changed': False, 'results': [{'msg': "AnsibleUndefinedVariable: One or
more undefined variables: 'str object' has no attribute 'db'", 'failed':
True}]}

-----

I've also attempted the below:


*# host_vars/<hostname>*
# same as before

*# template*
{% for k, v in item %}
{{ v.db }} {{ v.os }} {{ k }}
{% endfor %}

*# task*
# same as before

This fails with "too many values to unpack".

Which is the functional and favored approach? I'd rather the map keys be
the names but can't seem to get this to work.
--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+***@googlegroups.com.
To post to this group, send email to ansible-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael DeHaan
2014-08-16 15:14:39 UTC
Permalink
With dict works a little differently than with_items.

with_items will return one item in the postgres_users list, one after
another, if it were a list of users.

postgres_users:
- name: boxey
db: cargo_ship

Now, when using with_dict, it works differently, returning keys as item.key
and values as item.value

http://docs.ansible.com/playbooks_loops.html#looping-over-hashes

I'd consider either passing a list as above or using with_dict, but noting
what it returns.

Meanwhile, this looks problematic:

template: src=pg_ident.conf.j2
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640

Why?

You're evaluating it multiple times in a loop, but there is no variable in
the destination path. The result is you've re-templated the file many times.

I think you don't need the loop, and just need to simply reference
postgres_users as a variable in the template, and templating the file once
is fine.

But you can't use a simple for loop, because postgres_users is not a list.

Try:

{% for (key, value) in postgres_users.iteritems() %}

{% endfor %}

if you want to keep things as a hash/dictionary.
Post by Matt Silverlock
Hi all,
Trying to pull together my understanding of Jinja's looping constructs,
Ansible's with_dict and from what I've seen out in the wild (i.e. on
GitHub). I've taken a look around at some other examples but can't seem to
adapt them to my approach (i.e.
https://github.com/timmahoney/ansible-redhat-extra-repos/blob/master/defaults/main.yml
is close to what I'm after)
At the moment I'm attempting to loop over a YAML dict of OS user <-> DB
*# host_vars/<hostname>*
os: "{{ username }}"
db: "{{ db_name }}"
os: "{{ username }}"
db: "{{ db_name }}"
*# pg_ident.conf.j2*
{% for user in postgres_users %}
{{ user.db }} {{ user.os }} {{ user.key }}
{% endfor %}
*# roles/postgresql/tasks/postgres.yml*
- name: copy postgres ident maps
template: src=pg_ident.conf.j2
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640
register: postgres_ident
with_dict: postgres_users
*# error message*
TASK: [postgres | copy postgres ident maps]
***********************************
fatal: [default] => {'msg': "AnsibleUndefinedVariable: One or more
undefined variables: 'str object' has no attribute 'db'", 'failed': True}
fatal: [default] => {'msg': 'One or more items failed.', 'failed': True,
'changed': False, 'results': [{'msg': "AnsibleUndefinedVariable: One or
True}]}
-----
*# host_vars/<hostname>*
# same as before
*# template*
{% for k, v in item %}
{{ v.db }} {{ v.os }} {{ k }}
{% endfor %}
*# task*
# same as before
This fails with "too many values to unpack".
Which is the functional and favored approach? I'd rather the map keys be
the names but can't seem to get this to work.
--
You received this message because you are subscribed to the Google Groups
"Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com
<https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+***@googlegroups.com.
To post to this group, send email to ansible-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/CA%2BnsWgzWzm7Sbrs12XScGgYxYkf1tATDnvXABiXBu_4CDfJoYQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Matt Silverlock
2014-08-17 00:55:02 UTC
Permalink
template: src=pg_ident.conf.j2
Post by Matt Silverlock
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640
Why?
Post by Matt Silverlock
You're evaluating it multiple times in a loop, but there is no variable in
the destination path. The result is you've re-templated the file many times.
I think you don't need the loop, and just need to simply reference
postgres_users as a variable in the template, and templating the file once
is fine.
I was originally under the impression that you needed to pass lists/dicts
to templates explicitly (i.e. for use by Jinja) - hadn't realised that they
were global.

You've solved my confusion though (and dropped in a BSG reference to boot!)
— thanks Michael.
Post by Matt Silverlock
With dict works a little differently than with_items.
with_items will return one item in the postgres_users list, one after
another, if it were a list of users.
- name: boxey
db: cargo_ship
Now, when using with_dict, it works differently, returning keys as
item.key and values as item.value
http://docs.ansible.com/playbooks_loops.html#looping-over-hashes
I'd consider either passing a list as above or using with_dict, but noting
what it returns.
template: src=pg_ident.conf.j2
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640
Why?
You're evaluating it multiple times in a loop, but there is no variable in
the destination path. The result is you've re-templated the file many times.
I think you don't need the loop, and just need to simply reference
postgres_users as a variable in the template, and templating the file once
is fine.
But you can't use a simple for loop, because postgres_users is not a list.
{% for (key, value) in postgres_users.iteritems() %}
{% endfor %}
if you want to keep things as a hash/dictionary.
Post by Matt Silverlock
Hi all,
Trying to pull together my understanding of Jinja's looping constructs,
Ansible's with_dict and from what I've seen out in the wild (i.e. on
GitHub). I've taken a look around at some other examples but can't seem to
adapt them to my approach (i.e.
https://github.com/timmahoney/ansible-redhat-extra-repos/blob/master/defaults/main.yml
is close to what I'm after)
At the moment I'm attempting to loop over a YAML dict of OS user <-> DB
*# host_vars/<hostname>*
os: "{{ username }}"
db: "{{ db_name }}"
os: "{{ username }}"
db: "{{ db_name }}"
*# pg_ident.conf.j2*
{% for user in postgres_users %}
{{ user.db }} {{ user.os }} {{ user.key }}
{% endfor %}
*# roles/postgresql/tasks/postgres.yml*
- name: copy postgres ident maps
template: src=pg_ident.conf.j2
dest=/etc/postgresql/9.3/main/pg_ident.conf owner=postgres group=postgres
mode=0640
register: postgres_ident
with_dict: postgres_users
*# error message*
TASK: [postgres | copy postgres ident maps]
***********************************
fatal: [default] => {'msg': "AnsibleUndefinedVariable: One or more
undefined variables: 'str object' has no attribute 'db'", 'failed': True}
fatal: [default] => {'msg': 'One or more items failed.', 'failed': True,
'changed': False, 'results': [{'msg': "AnsibleUndefinedVariable: One or
True}]}
-----
*# host_vars/<hostname>*
# same as before
*# template*
{% for k, v in item %}
{{ v.db }} {{ v.os }} {{ k }}
{% endfor %}
*# task*
# same as before
This fails with "too many values to unpack".
Which is the functional and favored approach? I'd rather the map keys be
the names but can't seem to get this to work.
--
You received this message because you are subscribed to the Google Groups
"Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an
<javascript:>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com
<https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+***@googlegroups.com.
To post to this group, send email to ansible-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/37231d1a-139f-46ab-96f6-685837132385%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Michael DeHaan
2014-08-17 12:59:55 UTC
Permalink
No problem!

"hadn't realised that they were global."

Not always global, some variables are scoped to the host because they are
set on the group or host (like group_vars, host_vars, facts, etc). But
yeah you don't have to pass anything explicitly to the template module
ever, it grabs everything that should be in scope, and takes care of
sorting variable precedence out automatically.
template: src=pg_ident.conf.j2 dest=/etc/postgresql/9.3/main/pg_ident.conf
owner=postgres group=postgres mode=0640
Why?
You're evaluating it multiple times in a loop, but there is no variable
in the destination path. The result is you've re-templated the file many
times.
I think you don't need the loop, and just need to simply reference
postgres_users as a variable in the template, and templating the file once
is fine.
I was originally under the impression that you needed to pass lists/dicts
to templates explicitly (i.e. for use by Jinja) - hadn't realised that they
were global.
You've solved my confusion though (and dropped in a BSG reference to
boot!) — thanks Michael.
With dict works a little differently than with_items.
with_items will return one item in the postgres_users list, one after
another, if it were a list of users.
- name: boxey
db: cargo_ship
Now, when using with_dict, it works differently, returning keys as
item.key and values as item.value
http://docs.ansible.com/playbooks_loops.html#looping-over-hashes
I'd consider either passing a list as above or using with_dict, but
noting what it returns.
template: src=pg_ident.conf.j2 dest=/etc/postgresql/9.3/main/pg_ident.conf
owner=postgres group=postgres mode=0640
Why?
You're evaluating it multiple times in a loop, but there is no variable
in the destination path. The result is you've re-templated the file many
times.
I think you don't need the loop, and just need to simply reference
postgres_users as a variable in the template, and templating the file once
is fine.
But you can't use a simple for loop, because postgres_users is not a list.
{% for (key, value) in postgres_users.iteritems() %}
{% endfor %}
if you want to keep things as a hash/dictionary.
Post by Matt Silverlock
Hi all,
Trying to pull together my understanding of Jinja's looping constructs,
Ansible's with_dict and from what I've seen out in the wild (i.e. on
GitHub). I've taken a look around at some other examples but can't seem to
adapt them to my approach (i.e. https://github.com/
timmahoney/ansible-redhat-extra-repos/blob/master/defaults/main.yml is
close to what I'm after)
At the moment I'm attempting to loop over a YAML dict of OS user <-> DB
*# host_vars/<hostname>*
os: "{{ username }}"
db: "{{ db_name }}"
os: "{{ username }}"
db: "{{ db_name }}"
*# pg_ident.conf.j2*
{% for user in postgres_users %}
{{ user.db }} {{ user.os }} {{ user.key }}
{% endfor %}
*# roles/postgresql/tasks/postgres.yml*
- name: copy postgres ident maps
template: src=pg_ident.conf.j2 dest=/etc/postgresql/9.3/main/pg_ident.conf
owner=postgres group=postgres mode=0640
register: postgres_ident
with_dict: postgres_users
*# error message*
TASK: [postgres | copy postgres ident maps]
***********************************
fatal: [default] => {'msg': "AnsibleUndefinedVariable: One or more
undefined variables: 'str object' has no attribute 'db'", 'failed': True}
fatal: [default] => {'msg': 'One or more items failed.', 'failed': True,
'changed': False, 'results': [{'msg': "AnsibleUndefinedVariable: One or
True}]}
-----
*# host_vars/<hostname>*
# same as before
*# template*
{% for k, v in item %}
{{ v.db }} {{ v.os }} {{ k }}
{% endfor %}
*# task*
# same as before
This fails with "too many values to unpack".
Which is the functional and favored approach? I'd rather the map keys be
the names but can't seem to get this to work.
--
You received this message because you are subscribed to the Google
Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send
To view this discussion on the web visit https://groups.google.com/d/
msgid/ansible-project/a1f23c94-398a-4043-96ed-
3a88feb7e03c%40googlegroups.com
<https://groups.google.com/d/msgid/ansible-project/a1f23c94-398a-4043-96ed-3a88feb7e03c%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/d/msgid/ansible-project/37231d1a-139f-46ab-96f6-685837132385%40googlegroups.com
<https://groups.google.com/d/msgid/ansible-project/37231d1a-139f-46ab-96f6-685837132385%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+***@googlegroups.com.
To post to this group, send email to ansible-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/CA%2BnsWgyFkeJB3OzuWYJN9Szp704rXax5JCfe7oKJLpT3-U9zBg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Loading...