Search code examples
pythonjinja2

Write to a yaml file using jinja2 template and python


I am trying to write data to a yaml file using a jinja2 template and python. Below is the jinja2 template

Jinja2 template:

name: eos-lab
topology:
  nodes:
    {{ switch_name }}:
      kind: ceos
      image: ceos:4.30.3M
      binds:
        - {{ switch_name }}.txt.json:/mnt/flash/EosIntfMapping.json:ro

Tried the below python code


import yaml
import jinja2
i = [{"switch_name": "a"}, {"switch_name": "b"}]
template_file = "eos-test-sb.clab.j2"
env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="."))
template = env.get_template(template_file)
for device in i:
    result = template.render(device)
    print(result)

This gives me output as

name: eos-lab
topology:
  nodes:
    a:
      kind: ceos
      image: ceos:4.30.3M
      binds:
        - a.txt.json:/mnt/flash/EosIntfMapping.json:ro
name: eos-lab
topology:
  nodes:
    b:
      kind: ceos
      image: ceos:4.30.3M
      binds:
        - b.txt.json:/mnt/flash/EosIntfMapping.json:ro

Desired Output:

name: eos-lab
topology:
  nodes:
    a:
      kind: ceos
      image: ceos:4.30.3M
      binds:
        - a.txt.json:/mnt/flash/EosIntfMapping.json:ro
    b:
      kind: ceos
      image: ceos:4.30.3M
      binds:
        - b.txt.json:/mnt/flash/EosIntfMapping.json:ro

Any suggestions greatly appreciated


Solution

  • The "right" (IMHO) way of handling a for loop in the Jinja template is... in the Jinja template itself, which is very capable of using for loops (see the docs) and of accessing dictionaries (see this other tutorial)

    With that in mind, modify the template to iterate over your list of devices:

    Option 1)

    name: eos-lab
    topology:
      nodes:
        {% for device in devices %}
          {{ device.switch_name }}:
            kind: ceos
            image: ceos:4.30.3M
            binds:
            - {{ device.switch_name }}.txt.json:/mnt/flash/EosIntfMapping.json:ro
        {%  endfor %}
    

    Option 2)

    name: eos-lab
    topology:
      nodes:
        {% for device in devices %}
          {{ device['switch_name'] }}:
            kind: ceos
            image: ceos:4.30.3M
            binds:
            - {{ device['switch_name'] }}.txt.json:/mnt/flash/EosIntfMapping.json:ro
        {%  endfor %}
    

    Notice the only difference in those two snippets of code is just how the device dictionary is read: either device.switch_name or device['switch_name']. As per the docs:

    You can use a dot (.) to access attributes of a variable in addition to the standard Python getitem “subscript” syntax ([]).

    The following lines do the same thing:

    {{ foo.bar }}

    {{ foo['bar'] }}

    Now your code just has to tell the renderer what devices is:

    import jinja2
    
    devices = [{"switch_name": "a"}, {"switch_name": "b"}]
    template_file = "eos-test-sb.clab.j2"
    env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="."))
    template = env.get_template(template_file)
    
    result = template.render(devices=devices)
    print(result)
    

    And you'll see:

    name: eos-lab
    topology:
      nodes:
        
          a:
            kind: ceos
            image: ceos:4.30.3M
            binds:
            - a.txt.json:/mnt/flash/EosIntfMapping.json:ro
        
          b:
            kind: ceos
            image: ceos:4.30.3M
            binds:
            - b.txt.json:/mnt/flash/EosIntfMapping.json:ro