Search code examples
pythonjinja2templating

Issue creating a variable name dynamically from jinja2 for loop


I am trying to create set of templates with names and its content created dynamically based on certain list and dictionary.

Part of the code that is causing the issue is below.

{% for tenant in object_list %}
{%- set name = 'object_t' + tenant + 'rules' %}
{{name}}:
    - permit ip any {{ tenant_public_ip['object_t' + tenant | string +'_pub'['Subnets'][0] }}
{% endfor %}

# combine acls
complete_acls:
  - name: DEFAULT_ACL
    rules: {{'"{{ default_rules }}"'}}

  {% for tenant in object_list %}
  {% set item= 'object_t' + tenant + 'rules' %}
  - name: Tenant_{{tenant}}_ACCESS-VLANS
    rules: {{"{{item}}"}}
  {% endfor %} 

Got error: template error while templating string: expected token ',', got '{'.

Parameters being parsed:

object_list: [1, 2, 3, 4, 5]
 
tenant_public_ip: {u''object_t5_pub'': {u''Subnets'': [u''8.51.14.124/28''], u''Vlan_id'': u''5010''}, u''object_t2_pub'': {u''Subnets'': [u''8.51.14.128/28''], u''Vlan_id'': u''2010''}, u''object_t4_pub'': {u''Subnets'': [u''8.51.14.176/28''], u''Vlan_id'': u''4100''}, u''object_t1_pub'': {u''Subnets'': [u''8.51.14.160/28''], u''Vlan_id'': u''1100''}, u''object_t3_pub'': {u''Subnets'': [u''8.51.14.144/28''], u''Vlan_id'': u''3100''}} '

I am new to Jinja2 templating, what am i doing wrong?

One other issue that i ran into after initial one was when combining all the rules at the bottom after adding several modules like above.

I get the rules as the 'item' variable name split into letters - "o, b, j, e, c, t" instead of gathering the rules that were created earlier that were stored under "object_t_1rules" for example. I can access them individually by directly using a specific name that they were created with but not using a for loop. I know Jinja2 does not allow nested variable access. How else can i achieve what i want here ?


Solution

  • You have more than one error in your template/code, on the following lines to be precise:

    {%- set name = 'object_t' + tenant + 'rules' %}
      - permit ip any {{ tenant_public_ip[ip_{{tenant}}_pub][Subnets][0] }}
    

    Firstly, tenant is an integer, and you're trying to put it betwixt two strings. Jinja is less forgiving than vanilla Python, so you need a filter:

    {%- set name = 'object_t' + tenant | string + 'rules' %}
    

    Secondly, Subnets should be a string; however, you're trying to access an object called Subnets (because of the lack of apostrophes):

      - permit ip any {{ tenant_public_ip[ip_{{tenant}}_pub]['Subnets'][0] }}
    

    Thirdly, you don't need the {{ and }} that are wrapped around tenant:

      - permit ip any {{ tenant_public_ip[ip_tenant_pub]['Subnets'][0] }}
    

    And finally, your tenant_public_ip example dictionary does not contain any keys called ip_<integer>_pub, only object_t<integer>_pub. In order for your code to work as intended, either modify your dictionary to this:

    tenant_public_ip: {'ip_1_pub': {'Subnets': ...}, ...}
    

    Or modify your template to this:

      - permit ip any {{ tenant_public_ip['object_t' + tenant | string + '_pub']['Subnets'][0] }}
    

    Personally, I'd choose the former (modifying the template).