In this blog post, let's look at one of the real-world's use cases of Jinja2 Templating. If you are not familiar with the Jinja2 template, please check out my other blog post below to learn the fundamentals.
Overview
Recently, I was working on a Palo Alto migration project and as part of that work, I had to configure almost 40 sub-interface on the Palo Alto. I've used Ansible to configure the sub-interfaces but assigning the IP address had to wait till the day of the migration. On the day of the migration, I also had to add the interfaces to the OSPF.
Of course, I could have done this via the web GUI, but I always prefer to prepare the config in advance and push it to the device via CLI or any automation tool. I didn't want to configure all 40 interfaces via the GUI as it would have taken a lot of time and could be error-prone.
Configuring the IP address, assigning a management-profile and adding the interface to the OSPF require the following CLI commands for each interface.
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.15 ip 10.10.15.1/24
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.15 interface-management-profile allow_ping
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 passive yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.15 transit-delay 1
As you can see above there are a lot of commands just for a single interface and I have to generate the configs for all 40 interfaces. I was looking for a reliable and easy way to generate config for all of the 40 interfaces and decided to use Jinja2 for it.
Please note that the majority of the interfaces are `OSPF Passive Interfaces' and a couple of them aren't. We will use if/else
statement to configure them appropriately inside the template.
The Power of Jinja2
The idea with Jinja2 templating is two create two files, a template file and a variable file. The template file contains the base configuration and the variable file contains all the elements that go into the template such as interface IP and the OSPF info.
Finally, you can use Python or Ansible to merge both files to get the desired configuration output.
Jinja2 Value Substitution
Let's look at Jinja2 value substitution where a template contains variables (double curly braces) that get replaced with values when a template is rendered.
As you can see below (an example based on Cisco ISO config), I defined a template and the required variables. Merging them together generates a configuration file that can be pushed to the device.
File Structure
As you can see below, I have three files - the template file, the variables and the Python Script.
sureshv@mac:~/Documents/jinja2-use-case|⇒ tree
.
├── interfaces.json
├── py_script.py
└── template.j2
0 directories, 3 files
Variables / Interface Details
I've had all the Interface to IP mapping information on a JSON file (can be a CSV file as well) as shown below. The key passive
is referring to OSPF passive interface. I'm only showing 5 interfaces for this example, it wouldn't make sense to display all 40 interfaces.
#interfaces.json
{
"interfaces":
{
"ae1.1":
{
"ip": "192.168.10.1/30",
"passive": false
},
"ae1.2":
{
"ip": "192.168.10.5/30",
"passive": false
},
"ae1.10":
{
"ip": "10.1.10.1/24",
"passive": true
},
"ae1.11":
{
"ip": "10.1.11.1/24",
"passive": true
},
"ae1.12":
{
"ip": "10.1.12.1/24",
"passive": true
}
}
}
Template File
The major part of the template is static but some parts of the template are different for each interface that are interface name
, ip-address
and OSPF passive
interfaces, we will place them into double curly braces. I'm using for loop
and if/else
statements inside the template and let's look at how they function.
{% for k,v in interfaces.items() %}
set template branch_office config network interface aggregate-ethernet ae1 layer3 units {{ k }} ip {{ v['ip'] }}
set template branch_office config network interface aggregate-ethernet ae1 layer3 units {{ k }} interface-management-profile allow_ping
{% endfor %}
{% for k,v in interfaces.items() %}
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} enable yes
{% if v['passive'] -%}
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} passive yes
{% else -%}
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} passive no
{% endif -%}
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface {{ k }} transit-delay 1
{% endfor %}
Iterate over key-value pairs using for loop
By using a for loop
, we can iterate over each interface from the JSON file and generate a config for that specific interface. The for loop goes over each interface, generates the config and moves on to the next one.
if/else statements
As I mentioned before, we have a mixture of ospf passive interfaces. By using the if/else statement we can tell the template to generate the configs appropriately.
Python Script
The following Python Script merges two files and provides the configuration for all the interfaces.
from jinja2 import Template
import json
with open ('interfaces.json') as json_file:
config_items = json.load(json_file)
with open('template.j2') as f:
template = Template(f.read(), keep_trailing_newline=True)
palo_config = template.render(config_items)
print(palo_config)
Generated Output
As you can see below, we now have the fully generated configuration that I can add it to the device.
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.1 ip 192.168.10.1/30
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.1 interface-management-profile allow_ping
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.2 ip 192.168.10.5/30
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.2 interface-management-profile allow_ping
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.10 ip 10.1.10.1/24
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.10 interface-management-profile allow_ping
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.11 ip 10.1.11.1/24
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.11 interface-management-profile allow_ping
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.12 ip 10.1.12.1/24
set template branch_office config network interface aggregate-ethernet ae1 layer3 units ae1.12 interface-management-profile allow_ping
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 enable yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 passive no
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.1 transit-delay 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 enable yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 passive no
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.2 transit-delay 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 enable yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 passive yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.10 transit-delay 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 enable yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 passive yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.11 transit-delay 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 enable yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 passive yes
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 link-type broadcast
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 bfd profile Inherit-vr-global-setting
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 gr-delay 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 metric 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 priority 1
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 hello-interval 10
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 dead-counts 4
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 retransmit-interval 5
set template branch_office config network virtual-router default protocol ospf area 0.0.0.0 interface ae1.12 transit-delay 1
Closing Thoughts
Even though there are many ways to achieve the same end result, I always prefer to use Jinja templating across all vendors. As you can see, the process is very straightforward and you can use the same script again and again by simply changing the template and the input values.