A common experience for a network engineer starting a new job is spending time getting acclimated to the environment. How is the network designed? What tools are in place? How do we deploy new hardware? inevitably they are pointed to the repository of network configuration “templates”.
Sometimes this is a shared folder filled with text documents titled something similar to “access-switch-template (Copy 4) updated on 2019-02-28 by jim - USE THIS ONE.txt
” that have been handed down from engineer to engineer over the years.
Here’s a simple example of what one of those might look like:
hostname [HOSTNAME]
vlan [WORKSTATION VLAN ID] name Workstations
int range eth [RANGE OF EDGE PORTS] switchport mode access switchport access vlan [WORKSTATION VLAN ID]
int range eth [RANGE OF UPLINK PORTS] description Uplink channel-group [PORT CHANNEL GROUP NUMBER] mode on
int po [UPLINK PORT
CHANNEL NUMBER] description Uplink switchport mode trunk switchport
trunk allowed vlan all channel-group 10 mode on
int vlan [MANAGEMENT VLAN NUMBER] ip address [MANAGEMENT VLAN IP] [MANAGEMENT VLAN SUBNET]
While it can feel simple to keep a folder filled with text files, it enviably fails over time.
Taking advantage of Jinja2 templating has become surprisingly simple and is far superior to doing a search-and-replace in text documents. Generating switch templates using Jinja2, with NetBox as the source of truth, has been possible for quite some time. Here is a great example of this from 2021 showing off how templates can be generated in conjunction with NetBox. As of version 3.5, which shipped in late April, configuration templating is a native feature within NetBox.
For our example, we’re going to take a small switch and replicate the above template, using nothing but NetBox data and built-in features.
Our first step is going to be setting our HQ-IT-Staging-Switch up so Jinja2 has the right information to use when filling in our template.
Breaking our switch down we have:
Next, we’ll set up the first iteration of our Jinja2 template by navigating to Provisioning > Config Templates and then click the Add button.
From there, we’ll use the following code for our basic template.
hostname {{ device.name }}
{% for interface in
device.interfaces.all() %} interface {{ interface.name }} switchport
mode access switchport access vlan {{ interface.untagged_vlan.vid }} {%
endfor %}
Breaking down our template:
Once saved, we’ll go back to our device and select this new template for use.
Once we’ve applied the template, and choose Render Config from the device.
We’re going to see that template come to life and generate our basic configuration.
We can see this was successful for our access ports, but we have some clear issues here.
This means we need to improve our template to take all of these scenarios into consideration.
Jinja2 templating can be feel daunting at first. Rather than take a step by step approach to teaching all the ins-and-outs of templating, we’re going to jump directly into a fully created template. I’ll step through the template, each of its functions, what they are doing, and reference each action by line number.
hostname {{ device.name }}
{% for vl in device.site.vlans.all() %} vlan {{vl.vid }} name {{ vl.name }} {%- endfor %}
{% for interface in
device.interfaces.all() %} {%- if interface.mgmt_only == false %}
interface {{ interface.name }} {%- if interface.enabled == false %}
shutdown {%- elif interface.enabled == true %} no shutdown {%- endif
%} {%- if interface.mode == "access" %} switchport mode access
switchport access vlan {{ interface.untagged_vlan.vid }} {%- elif
interface.mode == "tagged" %} switchport mode trunk {%- for vlan in
interface.tagged_vlans.all() %} switchport trunk allowed vlan add {{
vlan.vid }} {%- endfor %} {%- elif "tagged-all" in interface.mode %}
switchport mode trunk switchport trunk allowed vlan all {%- else %} {%-
if interface.lag != None %} channel-group {% for char in
interface.lag.name %}{%- if char.isdigit() %}{{ char }}{%- endif %}{%-
endfor %} mode on {%- endif %} {%- endif %} {% else %} interface {{
interface.name }} description Management VLAN {%- if interface.enabled
== false %} shutdown {%- elif interface.enabled == true %} no
shutdown {%- endif %} ip address {{ device.primary_ip.address }} {{
device.primary_ip.address.netmask }} {%- endif %} {%- endfor %}
When we use this updated template, we now see a much better representation of a configuration.
And there you have it!
Once you’ve mastered this process you can create templates for different device functions, different device types, different stages of operation, and even totally different vendors.
White space with Jinja2 templating can be handled in multiple different ways. For our example I used manual whitespace trimming. Any place you see a percent sign combined with a minus sign {%- or -%} I’m manually trimming and setting spacing for the configuration so that it represents typical “show run” output