NetDevOps

A Simple Jinja2 Template for Configuring Multiple Cisco Interfaces

A Simple Jinja2 Template for Configuring Multiple Cisco Interfaces
In: NetDevOps, Python, Cisco

In today's blog post, we're going to keep things straightforward and easy to understand. We're diving into a basic yet powerful example of using Jinja2, a tool I believe every Network Engineer should have in their toolkit.

The aim here is to keep our example as simple as possible, demonstrating how Jinja2 can make your life easier. While the possibilities with Jinja2 are vast, starting with a basic example helps in grasping its potential. As we walk through this example, you'll get a clearer picture of what Jinja2 is and how it can be a game-changer in network configuration and automation.

When to use Jinja2?

You might be wondering, "When should I use Jinja2?" Well, there isn't a one-size-fits-all answer, but let me start with a common scenario I often encounter.

Imagine you're tasked with making changes to multiple interfaces across several switches. Naturally, you'd want to prepare your configurations in advance, especially if they need to go through a change control process for approval. In such cases, I used to begin by creating a text file and manually adding the configurations. While this method works, it's not without its pitfalls.

The issue with manually preparing configurations is the risk of small, yet costly mistakes. How often have we all copied and pasted configs, only to forget to replace an interface name or VLAN? A single typo can lead to significant issues, which is something we all want to avoid.

This is where Jinja2 shines. Instead of manually typing out each configuration, you create a template. With your data fed into this template, Jinja2 effortlessly generates the full configuration for you. Let's see how this works in practice.

Using Python with Jinja2

Now, you might be wondering, "Why do we need Python in combination with Jinja2?" The key advantage of using Python here is its ability to handle data structures efficiently. In our scenario, we can maintain all our network configuration data in a Python data structure, like a dictionary or a list. This structure holds information about switches, interfaces, VLANs, and any other relevant configuration details.

💡
It's important to note that Python is not the only language that supports Jinja2; tools like Ansible can also be used for example.

Once we have our data neatly organized in Python, we can then pass it to our Jinja2 template. This process is akin to giving instructions to a smart machine, we provide the raw data (our Python data structure) and the template (Jinja2), which then processes this information to generate complete configurations.

Let's look at an example

To illustrate how Python and Jinja2 work together, let's walk through an example. We have a Python script and a corresponding Jinja2 template, each playing a crucial role in generating our desired network configurations.

Python Script

from jinja2 import Environment, FileSystemLoader

interfaces = {
    "switch-1": ['Gi1/0/8'],
    "switch-2": ['Gi1/0/10', 'Gi2/0/5'],
    "switch-3": ['Gi2/0/27', 'Gi2/0/28'],
    "switch-4": ['Gi1/0/1', 'Gi1/0/20', 'Gi1/0/22'],
    "switch-5": ['Gi1/0/9', 'Gi1/0/10'],
    "switch-6": ['Gi6/0/22'],
    "switch-7": ['Gi1/0/5'],
    "switch-8": ['Gi1/0/23', 'Gi1/0/27', 'Gi1/0/31']

}

env = Environment(loader=FileSystemLoader('.'), 
                  trim_blocks=True, lstrip_blocks=True)

template = env.get_template('template.j2')

cisco_config = template.render(interfaces=interfaces)

with open ('config.txt', 'w') as w:
    w.write(cisco_config)

Our Python script starts by importing the necessary Jinja2 components. It then defines a data structure, in this case, a dictionary named interfaces. This dictionary is structured with keys representing switch names and values being lists of interface identifiers. For instance, "switch-1": ['Gi1/0/8'] indicates that switch-1 has an interface Gi1/0/8 that needs modifying.

After defining the data, the script sets up the Jinja2 environment and loads the template file (in our case, template.j2). The magic happens when the render method of the template is called with our interfaces dictionary. This method processes the template with the provided data, generating the full configuration.

While setting up the Jinja2 environment, we use two specific parameters, trim_blocks=True and lstrip_blocks=True. These settings are important for controlling whitespace in the generated configuration files, ensuring they are neat and readable.

  • trim_blocks=True - This setting prevents the extra newline character after a template tag.
  • lstrip_blocks=True - This parameter strips leading spaces and tabs from the start of a line to the beginning of a block.

Finally, the script writes this generated configuration to a file called config.txt.

Jinja2 Template

{% for k,v in interfaces.items() %}
---------
{{ k }}
---------
vlan 10
 name users
!
vlan 20
 name servers
!
{% for item in v %}
interface {{ item }}
 switchport trunk allowed vlan add 10,20
!
{% endfor %}

{% endfor %}

Moving to the Jinja2 template, it's designed to iterate over each item in our interfaces dictionary. For each switch, it generates the necessary VLAN configuration and then loops through the interfaces of that switch to apply specific settings.

The template uses Jinja2 syntax to control the flow – like iterating with for loops and using placeholders ({{ }}) for variables. The result is a dynamic template that adapts based on the data passed from the Python script.

The for loop plays a crucial role in processing each element of the data provided by the Python script. The syntax for k, v in interfaces.items() iterates over the interfaces dictionary passed from the Python script. Here, k represents the key (the switch name), and v represents the value (the list of interfaces for that switch).

Within this loop, the template dynamically generates the configuration for each switch and its interfaces. The placeholders {{ }} are used to insert the values of k (switch name) and item (individual interface) into the configuration text. This looping mechanism ensures that every switch and interface in the dictionary is addressed, allowing the template to adapt and generate configurations based on the specific data it receives.

Generated config

After running our Python script with the Jinja2 template, we get a series of configurations generated for each switch. Here's a breakdown of what these configurations mean.

---------
switch-1
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/8
 switchport trunk allowed vlan add 10,20
!

---------
switch-2
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/10
 switchport trunk allowed vlan add 10,20
!
interface Gi2/0/5
 switchport trunk allowed vlan add 10,20
!

---------
switch-3
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi2/0/27
 switchport trunk allowed vlan add 10,20
!
interface Gi2/0/28
 switchport trunk allowed vlan add 10,20
!

---------
switch-4
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/1
 switchport trunk allowed vlan add 10,20
!
interface Gi1/0/20
 switchport trunk allowed vlan add 10,20
!
interface Gi1/0/22
 switchport trunk allowed vlan add 10,20
!

---------
switch-5
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/9
 switchport trunk allowed vlan add 10,20
!
interface Gi1/0/10
 switchport trunk allowed vlan add 10,20
!

---------
switch-6
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi6/0/22
 switchport trunk allowed vlan add 10,20
!

---------
switch-7
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/5
 switchport trunk allowed vlan add 10,20
!

---------
switch-8
---------
vlan 10
 name users
!
vlan 20
 name servers
!
interface Gi1/0/23
 switchport trunk allowed vlan add 10,20
!
interface Gi1/0/27
 switchport trunk allowed vlan add 10,20
!
interface Gi1/0/31
 switchport trunk allowed vlan add 10,20
!
  1. Header for Each Switch - Each section of the configuration starts with a header, like --------- switch-1 ---------. This clearly identifies which part of the configuration applies to which switch.
  2. VLAN Configuration - For each switch, there's a standard VLAN configuration.
  3. Interface Configuration - Under each switch, we then configure the specific interfaces. For example, for switch-1, the configuration interface Gi1/0/8 switchport trunk allowed vlan add 10,20 is applied. This command adds VLANs 10 and 20 to the interface Gi1/0/8
  4. Consistency Across Switches -The pattern repeats for each switch, with the corresponding interfaces getting the appropriate configuration.
  5. Error Reduction - By using this automated approach, the chances of making a mistake, like a typo or incorrect VLAN assignment, are significantly reduced. Each interface on every switch is configured consistently and accurately.
  6. Customizability - While this example uses a specific set of VLANs and interfaces, the template and script can be easily modified to suit different network configurations, making this approach highly versatile.

Closing thoughts

Adding new VLANs or modifying existing ones is a breeze with this setup. All you have to do is amend the Jinja2 template file. For instance, if you want to introduce a new VLAN or change the name of an existing one, you simply update the VLAN section in the template. This change automatically reflects across all the configurations generated for each switch, maintaining consistency and saving you the hassle of manually editing each switch's configuration.

For those looking to take automation a step further, integrating tools like Netmiko is an excellent option. Netmiko can be used to push these configurations directly to your switches. This means you can go from generating configurations to applying them on your network devices, all within a streamlined, automated process.

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.