What is Jinja2
Jinja2 is a templating language that was originally used as part of the Flask python web framework. From the Jinja2 website
Jinja2 is a full featured template engine for Python. It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed
It was originally developed to help automatically generate HTML dynamically as part of the flask framework, more on that in another post, but it can also easily be used to help us generate our configuration files for our infrastructure devices.
This is going to be a very simple introduction to a few of the basic concepts of that jinja uses which, hopefully, will help to understand how Jinja can be used as a first step down the road of gaining automation skills.
We’ll take a look at a developing some intuition on how Jinja2 can be used to create basic network infrastructure device configurations. This is definitly not the modern method of interfacing directly into the control/data/management plane of devices using APIs, but it’s definitely a step in the right direction on understanding how a bit of code can help make your life better.
Prereqs
I’m assuming you’ve already got python installed on your system. You’re also going to need to run the pip install jinja2 command from a terminal window to get the latest version of jinja2 which should work just fine here.
Learning by Example
In this section we’ll start with a small example on how to create a few VLANs using the typical syntax from a modern networking OS. In this case, I used the HPE Comware syntax, but it would be easy enough to create this using a Cisco or Juniper configuration and you’re encouraged to try to get this working with your own network vendor.
Import required libraries
First We’ll import the required modules from the Jinja2 library. This is pretty much stolen directly from the jinja2 docs.
from jinja2 import Environment, FileSystemLoader, Template
Creating the VLANS
For this example, we’re going to create a python list of dicts which contains six different VLANS as listed in the tabel below.
A python dictionary is just a key-value pair, where the value for a specific key in the dictionary can be accessed using the key name.
- Name: Name of the VLAN
- Description: Descrition of the VLAN
- VLAN ID: Dot1q VLAN ID.
Name | Description | VLAN ID |
---|---|---|
Management | Management VLAN | 10 |
Users | Users VLAN | 15 |
Phones | Phones VLAN | 16 |
Servers | Servers VLAN | 20 |
Mobility | Mobility VLAN | 30 |
Guest | Guest VLAN | 40 |
vlans_list = [{'name': 'management', 'description': 'management vlan', 'id': '10'},
{'name': 'users', 'description': 'users vlan', 'id': '15'},
{'name': 'phones', 'description': 'phones vlan', 'id': '16'},
{'name': 'servers', 'description': 'servers vlan', 'id': '20'},
{'name': 'mobility', 'description': 'mobility vlan', 'id': '30'},
{'name': 'guest', 'description': 'guest vlan', 'id': '40'},
{'name': 'rob', 'description': 'guest vlan', 'id': '45'}
]
VLAN Jinja2 Template
In this step, we’re going to create a variable called text_file which will contain the content of a jinja2 template. This is a basic python string object which means, at this point, it’s just a bunch of text.
In normal circunstances, we would actually be reading this template from a file located on the hard drive, but for our purposes today, we’ll just put the templatein by hand.
What makes Jinja2 powerful is the control structures that allow it to perform programatic operations. In this example, we’re creating a For loop.
Following the code we will each vlan in the vlans object we created above and then render the template using the ‘id’ key for the first variable, the ‘name’ key for the second variable, and the ‘description’ key for the last variable.
Hopfully, this makes sense, but if not, just hold on and it should become clear before the end.
text_file = ('''
#vlan config
{% for vlan in vlans -%}
vlan {{ vlan['id'] }}
name {{ vlan['name'] }}
description {{ vlan['description'] }}
{% endfor %}''')
If I was to write the same as a traditional python iterator it would look something like this. You can see how they are related I hope?
for vlan in vlans_list:
print ('''vlan ''' +vlan['id']+
'''\n name '''+vlan['name']+
'''\n description '''+vlan['description'])
That’s a lot of work typing isn’t it?
You could ask
That’s more typing than I would do by hand? Why would I use this?
Great question. The point of automating anything is to cut down on the repetitive effort it takes to accomplish a given goal. In this case, we can simply count the number of key strokes it would take to create a single new VLAN on a switch.
count_chars = "vlan', 'name', 'description"
keystrokes = len(count_chars)
print (keystrokes)
Now let’s pretend we had to type that 10 times
keystrokes * 10
Or maybe we had to create 100 VLANs.
keystrokes * 100
Ir maybe we had to create the full 4094 VLANs available
keystrokes * 4094
Not sure about you, but if I don’t have to type 110,000 keystrokes, my fingers will love me at the end of the day. Not to mention the fact that it’s also repeated perfectly every single time, not a single typo in there.
Create the Template Object
Now that we’ve created the text_file string object, we need to transform it into a jinj2 template which will allow us to then render it. We will create a new object called vlan_template and assign an instance of the Template class using the text_file contents as the input.
vlan_template = Template(text_file)
Make the Magic Happen
We will now use the render method on the vlan_template that we created above. We have a single argument to pass into the function. In this case we are passing the vlans_list list of dictionaries we create above in to the function as the vlans variable.
vlan_template.render(vlans=vlans_list)
Hmmm What happened there?
That doesn’t look like a configuration file does it? The output of this file is actually a python string object. In python, we need someway to represent a carriage return (enter-key) and the \n just happens to have that honour.
Instead of running the template rendering directly, we can instead capture the output into a string object which we will then pass to the print command.
rendered_template = vlan_template.render(vlans=vlans_list)
print (rendered_template)
Clear?
Hopefully, this has shown you a bit of how a basic jinja control structure, like a For loop, can be used to cut down on a lot of key strokes, increase the accuracy of the configurations and help to streamline the operations.
In the next post, I’ll look at loading YAML files directly into python and using their contents as input into some more advanced jinja2 templates.