Templates & looping on json data structure

4,222 views
Skip to first unread message

Smain Kahlouch

unread,
Oct 24, 2013, 5:19:53 AM10/24/13
to ansible...@googlegroups.com
Hello,

I have a data structure gathered from a postgres db (external inventory) with a list of json data structures as follows :

[
{'name': conf1, 'myvar1': 1, 'myvar2': blabla},
{'name': conf2, 'myvar1': 44, 'myvar2': hello}
]

And i would like to use a kind of loop to create a file like :

[conf1]
param1 = 1
param2 = blabla

[conf2]
param1 = 44
param2 = hello


I've read in the ansible 
"
ansible allows Jinja2 loops and conditionals in templates, but in playbooks, we do not use them. Ansible templates are pure machine-parseable YAML. This is an rather important feature as it means it is possible to code-generate pieces of files, or to have other ecosystem tools read Ansible files. Not everyone will need this but it can unlock possibilities.
"

Is there a way to perform that kind of loops please ?

Regards,
Smana

Smain Kahlouch

unread,
Oct 24, 2013, 9:58:31 AM10/24/13
to ansible...@googlegroups.com
my current script generates this output :

{
    "databases": {
        "hosts": [], 
        "vars": {}
    }, 
    "webservers": {
        "hosts": [
            "10.18.2.1", 
            "10.18.2.2", 
        ], 
        "vars": {}
    }, 
    "Site": {
        "children": [
            "databases", 
            "webservers"
        ], 
        "vars": [
            {
                "id": 4912, 
                "path": "test1.mp3", 
            }
            {
                "id": 4913, 
                "path": "test2.mp3", 
            }
        ]
    }
}

But i got this error when executing ansible :

/etc/ansible/services/mysite 0 # ansible all -i ./inventory.py
Traceback (most recent call last):
  File "/usr/bin/ansible", line 157, in <module>
    (runner, results) = cli.run(options, args)
  File "/usr/bin/ansible", line 79, in run
    inventory_manager = inventory.Inventory(options.inventory)
  File "/usr/lib/python2.7/dist-packages/ansible/inventory/__init__.py", line 101, in __init__
    self.parser = InventoryScript(filename=host_list)
  File "/usr/lib/python2.7/dist-packages/ansible/inventory/script.py", line 47, in __init__
    self.groups = self._parse(stderr)
  File "/usr/lib/python2.7/dist-packages/ansible/inventory/script.py", line 92, in _parse
    for k, v in data['vars'].iteritems():
AttributeError: 'list' object has no attribute 'iteritems'

I need to iterate on a list of several variables.
Do you think that's possible ?

Regards,
Smana

Smain Kahlouch

unread,
Oct 25, 2013, 7:11:30 AM10/25/13
to ansible...@googlegroups.com
Any idea please ?

Regards,
Smana

Kesten Broughton

unread,
Oct 25, 2013, 1:03:54 PM10/25/13
to ansible...@googlegroups.com
Smain,

Although full jinja2 templating is not available in tasks/playbooks, other features like pipes/filters are.
Try piping your datastructure with to_json like   

 - name: do a thing
   copy: src="{{item}}" dest=/foo
   with_items: {{output | to_json}}

I used something like this as well, where _requested_hosts was a dict passed in to a task like
 [main.yml]
 - include: { playbook.yml, _requested_hosts: {{some_dict_defined_in_vars_main.yml}} }
 
[playbook.yml]
 - debug: msg="{{_requested_hosts | to_json | capture(requested_hosts)}}"

The problem is that passing structures between playbook files seems to serialize it, and this deserializes it.

kesten

Michael DeHaan

unread,
Oct 25, 2013, 7:49:02 PM10/25/13
to ansible...@googlegroups.com
I don't endorse the above suggestion by Kesten, and I'm pretty sure it's not going to work :)

You have a list data structure so iteritems isn't a method on a list.

If you just want to generate the config file I would do this as a template module:

(WARNING: untested general idea)

{% for entry in foo %}
    {% for (k,v) in entry.iteritems() %}
          {% if k == 'name' %}
[{{ v }}]
          {% endif %}
    {% endfor %}

   {% for (k,v) in entry.iteritems() %}
          {% if k != 'name' %}
{{ k }}={{ v }}
          {% endif %}
    {% endfor %}
{% endfor %}

- template: src=foo.j2 dest=wherever.out

Note that I'm answering your first question and not your second -- your variable structures are different in each.




--
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.
For more options, visit https://groups.google.com/groups/opt_out.



--
Michael DeHaan <mic...@ansibleworks.com>
CTO, AnsibleWorks, Inc.
http://www.ansibleworks.com/

Smain Kahlouch

unread,
Oct 29, 2013, 5:06:29 AM10/29/13
to ansible...@googlegroups.com
Hello all,

For information i managed to do what i wanted.
i just changed my list into a dictionnary :

mylist = [ {'name': conf1, 'myvar1': 1, 'myvar2': blabla}, {'name': conf2, 'myvar1': 44, 'myvar2': hello} ]
mydict = { mylist }

then i used this dictionnary in my playbook as follows :

- name: foo configuration
  template: src=foo.cfg dest=/etc/foo/{{ item['name'] }}.sh
  with_items: $mydict

the "item" variable can also be use inside the template.

Thanks for your help.

Regards

Michael DeHaan

unread,
Oct 29, 2013, 9:50:44 PM10/29/13
to ansible...@googlegroups.com
No need for the legacy variable here, BTW, just do this:

with_items: mydict



--
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.
For more options, visit https://groups.google.com/groups/opt_out.
Reply all
Reply to author
Forward
0 new messages