First of all, I am a network engineer learning a bit of coding and attempting to evolve using this relatively new tech. I have searched the interwebs for many hours regarding the below question but most search results include Ansible which i don't want (at the moment).
I am using a python/jinja2 script which generates a configuration based on VARs called confplate: see https://github.com/verbosemode/confplate - i have done some testing and it works great. Super easy and fast.
However, I'm trying to use a variable (a network address) and increment it within the Jinja2 template to set an IP address.
See below Jinja2 snippet:
ip dhcp pool WLAN01
network {{ vlan400_network }} 255.255.255.0
domain-name abc.xyz
default-router {{ vlan400_network + 1 }}
lease 0 1
vlan400_network = 172.29.50.0
I want the default-router to be 172.29.50.1
- of course i cannot increment it using the above snippet.
Do i need to use regular expressions for this? It does not seem very easy using solely Jinja2. My preference is to get this done using Python/Jinja2 only. Is this possible? Thanks in advance!
As you discovered, you're not going to get very far with generating configurations for network devices if you just rely on Jinja2's default filters.
You have to write your own custom filters (in Python) and use them in your template files.
For the example that you have, do this:
Step 1: Design the template
I am going to provide the vlan network as a CIDR specification:
./confplate.py template.txt vlan400_network=21.22.23.0/24
As you can see, this is intuitive and flexible: if you like you can change the network to 21.22.23.192/26
and things will continue to work just fine, as we will shortly see.
The template for this looks like:
ip dhcp pool WLAN01
network {{ vlan400_network | network_from_cidr }}
domain-name abc.xyz
default-router {{ vlan400_network | gateway_from_cidr }}
lease 0 1
where network_from_cidr
and gateway_from_cidr
are custom Jinja2 filters that we will shortly create.
The idea is that the network_from_cidr
filter will take a string like "21.22.23.0/24" and return the string "21.22.23.0 255.255.255.0". Likewise, the gateway_from_cidr
filter will take the string "21.22.23.0/24" and return "21.22.23.1" (i.e. the first non-zero host address in the subnet).
Step 2: Write the Python code for the custom filters
Create a file named custom_filters.py
and put in the following code:
import ipaddress
def network_from_cidr (cidr):
ipn = ipaddress.ip_network(unicode(cidr), strict=True)
return '{} {}'.format(ipn.network_address, ipn.netmask)
def gateway_from_cidr (cidr):
ipn = ipaddress.ip_network(unicode(cidr), strict=True)
# By convention, the gateway has the first non-zero address in the subnet
first_address = list(ipn.hosts())[0]
return first_address
The code uses the ipaddress module. As you can see, we define two functions, one for each of the custom filters we want to create. The functions take one argument, which is the string that precedes the |
in the template.
Step 3: Tell confplate.py about your custom filters
Somewhere at the beginning of confplate.py
write import custom_filters
.
You then have to say:
env = Environment(loader=FileSystemLoader(self.templatepath), undefined=StrictUndefined)
# Load our custom filters
env.filters['network_from_cidr'] = custom_filters.network_from_cidr
env.filters['gateway_from_cidr'] = custom_filters.gateway_from_cidr
at appropriate points in the code (in confplate.py you actually have to do this twice, once in the get_template_vars
function, and once in render_template
)
Test
./confplate.py template.txt vlan400_network=21.22.23.0/24
ip dhcp pool WLAN01
network 21.22.23.0 255.255.255.0
domain-name abc.xyz
default-router 21.22.23.1
lease 0 1
./confplate.py template.txt vlan400_network=21.22.23.192/26
ip dhcp pool WLAN01
network 21.22.23.192 255.255.255.192
domain-name abc.xyz
default-router 21.22.23.193
lease 0 1