Lab-10:Python for network automation -using Jinja2 template and YAML file

Jinja2 is a powerful templating language for Python. If you are in network automation business you are going to love it I guarantee it. Using Jinja2 you can build a template which uses variables instead of hard coded values, Jinja2 then automagically renders template using variable values. Variable values either comes from Python dictionary or yaml file. Jinja2 also allows you to add control statements like ‘for’ loop & ‘If’ statement to create logic in template file.

Some common Jinja2 statements:

Variable substitution:

  • Jinja2 variable substituting is done by enclosing it in ‘{{‘
    • example: {{ interface }}

For loop:

  • for statement begins with {% for statement %} and end with {% end %}
    • example: {% for interface in interfaces %}  … {% endfor %}

 If statement:

  • If statement begins with {% if statement %} and end with {% endif %}
    • example: {% if interface == ‘ge-0/0/2’  %}  …  {% endif %}

Comment:

  • comment starts with ‘{#’ and ends with ‘#}
    • example: {# set description only for interface ge-0/0/2 #}

 

 Pre-condition:

  • Install jinja2
    • pip install jinja2

Procedure:

  • Step-1: Create a Jinja2 template file, interfaces.j2. In this template interface name and description tags are created as variable and a for loop added to iterate thru multiple interfaces. I have created xml format file but it can be any format
<interfaces>
 {% for interface in interfaces %}
      <interface>{{interface}} 
          <description> {{description}} </description>
      </interface>
 {% endfor %}
</interfaces>
  • Step-2: Import packages, set environment and load jinja2 template by using get_template API. Here I am using ‘interfaces.j2’ template created in step-1

sjakhwal@rtxl3rld05:~$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import jinja2
>>> import yaml
>>> import os
>>> from lxml import etree
>>> templateFilePath = jinja2.FileSystemLoader(os.getcwd())
>>> jinjaEnv = jinja2.Environment(loader=templateFilePath,trim_blocks=True,lstrip_blocks=True)
>>> jTemplate = jinjaEnv.get_template(‘interfaces.j2’)

  • Step-3: Build variable dictionary in python. Here I am creating a dictionary for interfaces and description

config = {
‘interfaces’: [‘ge-0/0/1′,’ge-0/0/2′,’ge-0/0/3’],
‘description’: ‘interface configured using jinja2 template’
}

>>> print config
{‘interfaces’: [‘ge-0/0/1’, ‘ge-0/0/2’, ‘ge-0/0/3’], ‘description’: ‘interface configured using jinja2 template’}

  • Step-4: Last step is to render jinja2 template using dictionary . you can store new configuration to a file or push it to your device by writing simple Python code.

> print jTemplate.render(config)
<interfaces>
<nterface>ge-0/0/1
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/2
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/3
<description> interface configured using jinja2 template </description>
</interface>
</interfaces>

  • This is another way of achieving same result. Here instead of building dictionary of variables you pass them while rendering template. Note: This could be an issue when you have too many variables to pass

>> print jTemplate.render(interfaces = {‘ge-0/0/1′,’ge-0/0/2′},description=’interface configured using jinja2 template’)
<interfaces>
<nterface>ge-0/0/2
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/1
<description> interface configured using jinja2 template </description>
</interface>
</interfaces>

  • Using yaml file for variable values is a clean way of separating variables from Python code. User can change variable values without understanding of Python code.
  • Create yaml file, filename:interface.yaml. Yaml file always begins with ‘—‘ and ‘-‘ represents list element
  • Here I am creating values for two variables (interfaces and description) for jinja2 template ‘interfaces.j2’
---
interfaces:
- ge-0/0/1
- ge-0/0/2
- ge-0/0/3
description:
 "configured by using jinja2 & yaml"
  • Read yaml file in your Python

>>>templateVars = yaml.load(open(‘interfaces.yaml’).read())

  • Render jinja2 template using yaml file.

>>> templateVars = yaml.load(open(‘interfaces.yaml’).read())
>>> print jTemplate.render(templateVars)
<interfaces>
 <nterface>ge-0/0/1
 <description> configured by using jinja2 & yaml </description>
 </interface>
 <nterface>ge-0/0/2
 <description> configured by using jinja2 & yaml </description>
 </interface>
 <nterface>ge-0/0/3
 <description> configured by using jinja2 & yaml </description>
 </interface>
</interfaces>
>>>

  • Now lets try If control statement. Update ‘interfaces.j2’ file to add ‘if’ statement and comment statement. I added logic  to set description  only for interface ge-0/0/2

<interfaces>
 {% for interface in interfaces %}
{# set description only for interface ge-0/0/2 #}
 {% if interface == ‘ge-0/0/2’ %}
 <nterface>{{interface}}
 <description> {{description}} </description>
 </interface>
 {% endif %}
 {% endfor %}
</interfaces>

  • Render template and you see only ge-0/0/2 printed

>>> jTemplate = jinjaEnv.get_template(‘interfaces.j2’)
>>> print jTemplate.render(templateVars)
<interfaces>
<nterface>ge-0/0/2
<description> configured by using jinja2 & yaml </description>
</interface>
</interfaces>
>>>

  • There is a shortcut if you need to create template on the fly inside Python code. Here I am building same template by using jinja2 Template API

>>> from jinja2 import Template

>>> template = Template(‘<interfaces>{% for interface in interfaces %} <interface> \
… {{interface}} <description>{{description}}</description> </interface> {% endfor %} </interfaces>’)
>>> print template.render(templateVars)
<interfaces> <interface> ge-0/0/1 <description>configured by using jinja2 & yaml</description> </interface> <interface> ge-0/0/2 <description>configured by using jinja2 & yaml</description> </interface> <interface> ge-0/0/3 <description>configured by using jinja2 & yaml</description> </interface> </interfaces>
>>>

 

References:

  1. http://jinja.pocoo.org/docs/dev/