Search code examples
pythontemplatesnetwork-programmingjinja2cisco

Jinja2 Network Automation - increasing a variable (subnet +1)


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!


Solution

  • 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