with_dict and Jinja templates - Error

4,958 views
Skip to first unread message

Matt Silverlock

unread,
Aug 16, 2014, 7:38:23 AM8/16/14
to ansible...@googlegroups.com
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.



Michael DeHaan

unread,
Aug 16, 2014, 11:14:44 AM8/16/14
to ansible...@googlegroups.com
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


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.



--
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-proje...@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.

Matt Silverlock

unread,
Aug 16, 2014, 8:55:02 PM8/16/14
to ansible...@googlegroups.com
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.

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.

Michael DeHaan

unread,
Aug 17, 2014, 8:59:58 AM8/17/14
to ansible...@googlegroups.com
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.




Reply all
Reply to author
Forward
0 new messages