Python

Jinja2 Templating Use Case with Palo Alto

Jinja2 Templating Use Case with Palo Alto
In: Python, NetDevOps, Palo Alto

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.

Network Device Templating using Jinja and Python
Recently, I came across a task of generating configurations for a handful of Cisco switches. The most important thing of the task was to ensure the base config is identical across all of the switches to maintain consistency. For example, all the switches should have identical NTP, SNMP, AAA, banner

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.

Written by
Suresh Vina
Tech enthusiast sharing Networking, Cloud & Automation insights. Join me in a welcoming space to learn & grow with simplicity and practicality.
Comments
More from Packetswitch
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Packetswitch.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.