merge dictionary list in jinja2

3,751 views
Skip to first unread message

deewon

unread,
Feb 20, 2018, 4:46:59 AM2/20/18
to Ansible Project
Hi all,

I'm relatively new to using ansible and hoping someone can assist with some insight around this.

I'm trying to create a jinja2 template that generates authentication credentials for a database in json format 

I've defined the variables using the format below (using dictionaries)

vars:
    db_roles:
         user1:
           - { db: "lab2", privs: "READ,DELETE" }
         user2:
           - { db: "lab1", privs: "INSERT,DELETE" }
           - { db: "lab2", privs: "UPDATE" }


The idea is to loop through the users (user1 and user2 in this example) and assign the "privs" on the "db" for each user across multiple databases as needed



To do this, I created a jinja2 template that looks like the below:

{% for item in db_roles %}
        {% for dict_item in db_roles[item] %}
     ,
      { "db" : "admin", "userName" : "{{ item }}",
         "roles" : [
           {% for dbpriv in dict_item.privs.split(',') %}
          {
           "db" : "{{ dict_item.db }}",
           "role" : "{{ dbpriv }}"
          }
           {% if not loop.last %},{% endif %}
           {% endfor %}
                  ]
      }
         {% endfor %}
         
  {% endfor %}


Using dict_items, I'm able to access the values in each dictionary and the generated file looks this when executed:

,
      { "db" : "admin", "userName" : "user2",
         "roles" : [
                     {
           "db" : "lab1",
           "role" : "INSERT"
          }
           ,                     {
           "db" : "lab1",
           "role" : "DELETE"
          }
                                        ]
      }
         ,
      { "db" : "admin", "userName" : "user2",
         "roles" : [
                     {
           "db" : "lab2",
           "role" : "UPDATE"
          }
                                        ]
      }
                  ,
      { "db" : "admin", "userName" : "user1",
         "roles" : [
                     {
           "db" : "lab2",
           "role" : "READ"
          }
           ,                     {
           "db" : "lab2",
           "role" : "DELETE"
          }
                                        ]
      }



This is close to what I want with the exception of "user2"  whose privileges seem to have been split into 2 separate documents. All the privileges for each user should be defined within the roles array in the same document similar to the below.

{ "db" : "admin", "userName" : "user2",
         "roles" : [
         {
           "db" : "lab1",
           "role" : "INSERT"
          }
          ,
          {
           "db" : "lab1",
           "role" : "DELETE"
          }
          ,
          {
           "db" : "lab2",
           "role" : "UPDATE"
          }
          ]
       }
         ,
                             ,
       { "db" : "admin", "roleName" : "user1",
         "roles" : [
                     {
           "db" : "lab2",
           "role" : "READ"
          }
           ,
           {
           "db" : "lab2",
           "role" : "DELETE"
           }
          ]
        }


 I can't figure out how to merge the dictionary items for a single user across different "db" values into a  single dictionary that  allows me build the json document to look like the above

Any insights or suggestions will be highly appreciated :)

Regards

Kai Stian Olstad

unread,
Feb 20, 2018, 6:34:14 AM2/20/18
to ansible...@googlegroups.com
On 20.02.2018 10:46, 'deewon' via Ansible Project wrote:
> vars:
> db_roles:
> user1:
> - { db: "lab2", privs: "READ,DELETE" }
> user2:
> - { db: "lab1", privs: "INSERT,DELETE" }
> - { db: "lab2", privs: "UPDATE" }
>
>
> To do this, I created a jinja2 template that looks like the below:
>
> {% for item in db_roles %}
> {% for dict_item in db_roles[item] %}
> ,
> { "db" : "admin", "userName" : "{{ item }}",
> "roles" : [
> {% for dbpriv in dict_item.privs.split(',') %}
> {
> "db" : "{{ dict_item.db }}",
> "role" : "{{ dbpriv }}"
> }
> {% if not loop.last %},{% endif %}
> {% endfor %}
> ]
> }
> {% endfor %}
>
> {% endfor %}

You need to move your { "db" up one level.

{% for item in db_roles %}
,
{ "db" : "admin", "userName" : "{{ item }}",
{% for dict_item in db_roles[item] %}
"roles" : [
{% for dbpriv in dict_item.privs.split(',') %}
{
"db" : "{{ dict_item.db }}",
"role" : "{{ dbpriv }}"
}
{%- if not loop.last %},{% endif %}
{% endfor %}
{% endfor %}
]
}
{% endfor %}


I also added a "-" to the "if" so it comes after "}" and not on a
newline.


--
Kai Stian Olstad

George Shuklin

unread,
Feb 20, 2018, 8:24:26 AM2/20/18
to ansible...@googlegroups.com
Try to use 'combine' filter:

one: {foo: bar}
two: {baz: bazbaz }
three: '{{one|combine(two)}}'

Result: three: {foo: bar, baz: bazbaz}
--
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/83cd07c9-e67d-4186-9ac7-7433ba18fc35%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


deewon

unread,
Feb 21, 2018, 12:23:25 PM2/21/18
to Ansible Project
Thanks Kai.

The example you gave came pretty close (see result below for user2) but  it kind of pointed me in the right direction and I think I've fixed it

{ "db" : "admin", "userName" : "user2",

          "roles" : [

           {
            "db" : "lab1",
            "role" : "INSERT"
           },

           {
            "db" : "lab1",
            "role" : "DELETE"
           }


          "roles" : [

           {
            "db" : "lab2",
            "role" : "UPDATE"
           }

       }
  

From the above, I still get 2 separate roles array for a single user but in addition to the above, If I move the dictionary items lookup into the "roles: [ " section, I seem to achieve the desired result i.e.

{% for item in db_roles %}
      ,
       { "db" : "admin", "userName" : "{{ item }}",
          "roles" : [
            {% for dict_item in db_roles[item] %}
            {% for dbpriv in dict_item.privs.split(',') %}
           {
            "db" : "{{ dict_item.db }}",
            "role" : "{{ dbpriv }}"
           }
            {%- if not loop.last %},{% endif %}
            {% endfor %}
            {%- if not loop.last %},{% endif %}
          {% endfor %}
                   ]
       }
   {% endfor %}

Which gives me this:

 ,
       { "db" : "admin", "userName" : "user2",
          "roles" : [


           {
            "db" : "lab1",
            "role" : "INSERT"
           },

           {
            "db" : "lab1",
            "role" : "DELETE"
           }
            ,


           {
            "db" : "lab2",
            "role" : "UPDATE"
           }


                   ]
       }

      ,
       { "db" : "admin", "userName" : "user1",
          "roles" : [


           {
            "db" : "lab2",
            "role" : "READ"
           },

           {
            "db" : "lab2",
            "role" : "DELETE"
           }


                   ]
       }

Thank you :)

deewon

unread,
Feb 21, 2018, 12:25:33 PM2/21/18
to Ansible Project
Thanks for the hint
Reply all
Reply to author
Forward
0 new messages