GIT and Jinja – Like Peanut butter and Pickles!

Thanks to @mierdin for point this out. It looks like the wordpress format is causing some strange word-wrap issues. For a better view please click here to see the full post without presentation issues. 

 

Using GITHub to build our Network Configs

As I wrote in this post, one of my goals for this year is to be able to compltely automate the build of my lab environment programatically.

In the last couple of jinja posts, I wrote about the basics of Jinja2 templates and how they can be applied to building network configurations.

In this post, I’m going to take the next step and move those files from my local hard drive out to…

 

duh duh dahhhhhhhhhh

The cloud.

The cloud

 

Before we get started…

We’re going to go over some basics on the tools we’re using to make sure everyone’s on the same page. cool?

What’s GIT?

Git is a widely-used source code management system for software development. It is a distributed revision control system with an emphasis on speed, data integrity, and support for distributed, non-linear workflows. wikipedia

Huh?

GIT is a piece of software that allows you to track changes to files over time.

So what’s GITHub?

“Where software is built Powerful collaboration, code review, and code management for open source and private projects. Public projects are always free. “Github.com

GITHub is like facebook for developers. It’s a place where you can sync your local GIT repository to a central location, and then sync that central location to other local repositories.

Different people can connect to the same repository allowing multiple people to work on the same project.

What’s a repository?

A repository is essentially a collection of files that make up a project. You could think of it like a folder or directory. That analogy is not exact as it’s possible for a repository to have multiple sub-folders or directories, but it’s close enough for our purposes.

Is GIT only for Code?

GIT was definitely designed for software developers to as a versioning control system while developing software, but you can use it for tracking changes to things other than

You could use it for anything text format that you want to track changes to over time. For example

  • grocery lists
  • contact list
  • tracking your weight

There are a lot of interesting uses for GIT, one of those that we’re going to use today is looking at storing our Jinja2 templates on a public GIT repository and loading them directly into our python script as part of the code.

 

Import Required Libraries

Unles you’ve already got them, you’ll need to  pip install jinj2  and  pip install requests these two libraries before loading them into your running environment.

In [1]:
import requests
import yaml
import githubuser
from jinja2 import Environment, FileSystemLoader, Template
 

Loading Templates from GITHub

Like with most things in python, if it’s useful enough, chances are there’s probably someone else who already put a library together for that. In our case, we’re going to use the python request library to handle loading files directly from our Github repository.

 

The first thing we’ll do is load the HPE comware switch template from that we used in this post. If you wanted to take a look at this directly on github, it can be found here. All we have to do is to copy and paste the URL from our browser directly into the first input of the requests.get function.

note: The requests function will return a whole object that has various attributes. the ” .text ” at the end of this tells the function to just give us the contents of the file, not of the other information, like the HTTP status_code.

Simple, right?

In [75]:
comware_template = requests.get('https://github.com/netmanchris/Jinja2-Network-Configurations-Scripts/blob/master/simple_comware.j2').text
 

Looking at the output

So now that we’ve loaded the contents of the simple_comware.j2 template directly from the Github site into the comware_template variable. Let’s take a look to make sure that we have what we need.

In [76]:
print (comware_template)
 
<!DOCTYPE html>
<html lang="en" class="">
  <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# object: http://ogp.me/ns/object# article: http://ogp.me/ns/article# profile: http://ogp.me/ns/profile#">
    <meta charset='utf-8'>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="Content-Language" content="en">
    <meta name="viewport" content="width=1020">
    
    
    <title>Jinja2-Network-Configurations-Scripts/simple_comware.j2 at master · netmanchris/Jinja2-Network-Configurations-Scripts · GitHub</title>
    <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
    <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
    <link rel="apple-touch-icon" href="/apple-touch-icon.png">
   
...
 

Hmmmmm. That’s not right?

The requests library is reaching out and grabbing whatever we put into that first variable. If we look at the print contents, we can see the first line is<!DOCTYPE html> . So it looks like we’re grabbing the rendered webpage, not just the contents of the file. Thankfully, looking at the GITHub website, there’s an option to look at any of your files in raw mode. So let’s grab that URL and try this again, ok?

In [77]:
comware_template = requests.get('https://raw.githubusercontent.com/netmanchris/Jinja2-Network-Configurations-Scripts/master/simple_comware.j2').text
In [78]:
print (comware_template)
 
#sysname config
sysname {{ simple['hostname'] }}
#vlan config
{% for vlan in simple['vlans'] -%}
vlan {{ vlan['id'] }}
    name {{ vlan['name'] }}
    description {{ vlan['description'] }}
{% endfor %}#snmp_config
snmp-agent
snmp-agent community read {{ simple['snmp']['read'] }}
snmp-agent community write {{ simple['snmp']['write'] }}
snmp-agent sys-info contact {{ simple['snmp']['syscontact']  }}
snmp-agent sys-info location {{ simple['snmp']['syslocation'] }}
snmp-agent sys-info version all
 

Ahhhh… That’s better.

 

Loading Network Specific Values from GITHub

Now we’re going to load our network specific values which were stored in the YAML file in this post. But this time, we’re going to load them directly from a private github repository.

The free GITHub accounts allow you to have public repositories, which means everyone can see what you’re doing, but if you have a paid version, you can get private repositories for as little as five dollars a month.

The private repositories are secured and can only be accessed by someone with a GIThub username and password who has explicitly been given access to this repository.

I would say that it’s probably a bad idea for us to keep any secure information like usernames, passwords, or SNMP strings in a online repository. But for my purposes, I don’t have anythng of value in this lab environment so I’m not too worried about it.

note: Before you put any sensitive data into an online repository of any kind, be sure to check with your companies data policies to see if you’re breaking any corporate rules.

 

Creating an Auth Object

First, I’m going to create an auth object, which is basically a single object that represents the username and password for my github account. In my case, I’ve got a file on my local hard drive that will automatically create the auth object for me.

In case you’re interested, the file is called githubuser.py and contains the following code. 

 

from requests.auth import HTTPBasicAuth

def gitcreds(): auth = HTTPBasicAuth('netmanchris', 'my_secret_password') return auth

In [79]:
auth = githubuser.gitcreds() #you didn't think I was going to give you my password did you?
 

Loading simple.yaml

We’ll now load the simple.yaml file like we did in this post but instead of opening it from a local file, we’re going to load it directly from the raw version of the file on github. I’d give you the link but it’s in a private repository, so you won’t be able to access it anyways.

Thigs I want to point out

  • yaml.load: takes the response and processes the yaml content directly into a python data structure ( dictionary )
  • .text: takes the “.text” attribute from the requests object which is the content of the page.
  • auth = auth: takes the auth object we created above and passes it as the username and password during the HTTP request.

Make sense?

In [80]:
simple = yaml.load(requests.get('https://raw.githubusercontent.com/netmanchris/PrivateRepo/master/simple_config.yaml', auth=auth).text)
In [81]:
simple
Out[81]:
{'hostname': 'testswitch',
 'ip': '10.101.0.221',
 'snmp': {'read': 'supersecret',
  'syscontact': 'admin.lab.local',
  'syslocation': 'lab',
  'trap': [{'target': '10.101.0.200'},
   {'target': '10.101.0.201'},
   {'target': '10.101.0.202'}],
  'write': 'macdonald'},
 'vlans': [{'description': 'management vlan',
   'id': '10',
   'name': 'management'},
  {'description': 'users vlan', 'id': '15', 'name': 'users'},
  {'description': 'phones vlan', 'id': '16', 'name': 'phones'},
  {'description': 'servers vlan', 'id': '20', 'name': 'servers vlan'}]}
 

Putting it all together

So looking at our list

  • download simple_comware.j2 template from Github public repo: **Check!**
  • download simple.yaml values file from Github private repo: **Check!**
  • rendered templates: **Nope**

So I guess we know what comes next, right?

 

Rendering the final config

We use the Template function to create a jinja2 template object and then we use the simple variable we created during the yaml section as input into the cw_template object.

In [82]:
cw_template = Template(comware_template)
type(cw_template)
Out[82]:
jinja2.environment.Template
In [83]:
print (cw_template.render(simple=simple))
 
#sysname config
sysname testswitch
#vlan config
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers vlan
    description servers vlan
#snmp_config
snmp-agent
snmp-agent community read supersecret
snmp-agent community write macdonald
snmp-agent sys-info contact admin.lab.local
snmp-agent sys-info location lab
snmp-agent sys-info version all
 

Writing the Config to Disk

So far we’ve only been rendering and printing configurations, but it would be kinda nice to be able to have these on disk so that we can open them in our favorite editor before we cut and paste them into a telnet session to our network device.

The next two commands simply write the rendered template to disk with the filename comware.cfg and then we open and print the file to screen just to make sure it worked.

In [84]:
with open('comware.cfg', "w") as file:
    file.write(cw_template.render(simple=simple))
In [85]:
with open('comware.cfg') as file:
    print (file.read())
 
#sysname config
sysname testswitch
#vlan config
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers vlan
    description servers vlan
#snmp_config
snmp-agent
snmp-agent community read supersecret
snmp-agent community write macdonald
snmp-agent sys-info contact admin.lab.local
snmp-agent sys-info location lab
snmp-agent sys-info version all
 

What’s next?

So far, we’ve come pretty far. We’ve written a couple of jinja templates, we’ve figure out how to store those files in a centralized control versioning system, but we’re still cut’ing and past’ing those configurations ourselves which is not ideal.

In the next post, we’ll look at using APIs to push the configuraiton directly to a configuraiton management tool.

Questions or comments? Feel free to post below!

@netmanchris

OpenSwitch in an OVA

 

First, disclaimer: I’m an HPE employee. Hewlett Packard Enterprise is a major contributor to the OpenSwitch project. Just thought you should know in case you think that affects my opinion here.

If you need more info on the OpenSwitch project, you can check out the other post in this series here and here

Network Engineers Don’t Like Learning New Things

Got your attention, didn’t I?  After the first couple of posts on OpenSwitch and a lot of discussions about this cool new project at some recent events, there was one piece of feedback that came back fairly consistently from the traditional engineers. OpenSwitch is hard to get running because there’s so many new things to learn.

When released in November of last year, the initial demonstration environment was actually pretty simple and streamlined to get up and running, as long as you’re a developer.  

The process involved the standard set of dev tools:

  • Virtual Box
  • Vagrant
  • DockerToolbox
  • Docker

 For anyone involved in a development environment, these tools are like an old hoody on a cold winter day. Welcome and familiar. 

But for the majority of network engineers who are far more comfortable with a console cable and a telnet session, it appears that the barrier to entry was just too high for people to start getting their hands dirty. 

 

I was able to bring this feedback to the OpenSwitch engineering team and I’m happy to bring the news that OpenSwich is now available in a OVA format that you can run natively on VirtualBox. 

 

Read the Docs

I’m going to go the long way around to get this up and running, but I’ve heard that the OVA file may be prebuilt and available on the OpenSwitch website in the near future. *I’ll try and come back to edit this post with a direct link if that happens*

The build process for OpenSwitch is actually well documented here.  Depending on the OS you’re running, there are some specific dependencies that are well documented. I won’t cover those since they are already there, but make sure you do check the docs carefully when you’re creating you’re build system as it won’t work unless you follow them.

Getting the Code

Since we’re going to be simply creating an OVA image, we don’t need the entire OPS GIT repo, we only need the ops-build portion. The first thing we’re going to do is to get to a terminal window on your linux ubuntu 14.04 host, create a directory called opsova and then GIT clone the ops-build repository using the following command. This command will copy the contents of the ops-build directory on GITHUB into a local directory called ops-build on your local machine.

git clone https://git.openswitch.net/openswitch/ops-build
 

 Selecting the Build

Now that we’ve cloned the necessary code to our local machines. We’re going to select the type of OpenSwitch build that we’d like to create. If you were pushing this to a supported white box switch you would use the following commands

make configure genericx86-64

But since we’re going to be creating an OVA so that we can import directly into Oracle Virtual box (Because it’s free!) we’re going to configure the appliance build

make configure appliance
 

Creating the OVA

Now for the final-ish step of the build. We’re going to run the make command to actually create the OVA file.

Warning: If you’re doing this in a VM. You want to give it lots of CPU for this step or it could take quite a long time. Remember burning CD’s on a 1x speed burner? Yeah… it feels like that. 

make
 

Running the OVA

Now that we’ve successfully created the OVA, the next step is to move it out of the VM to the host machine where you have Oracle VirtualBox installed.. This, of course, is assuming that you followed my example and you were doing this in a Ubuntu VM rather than a bare metal machine. From here, we follow a typical deployment and import the OVA using the following steps.

Finding the OVA

Once the make process, finishes ( there may be a couple of warnings, but it should build successfully  ), you will navigate to the ./images folder where you will find a symbolic link to OVA file. Following the symbolic link, the actual OVA was located in ./ops-build/build/tmp/deploy/images/appliance.  

Screen Shot 2016 01 19 at 1 14 15 PM

Now you need to get it off of your VM host and move it over to the machine where you are running VirtualBox.  ( I’m assuming you are comfortable with moving files between two machines and I’m not covering that here. Please feel free to point out in the comments if I’ve made a false assumption ).


Importting the OVA into Virtual Box

 

Now that we’ve moved this over to the host machine where you’re running VirtualBox, you simply choose File\import Appliance and navigate to the directory where you stored the OVA click next a couple of times and you should be good to go.

Screen Shot 2016 01 19 at 1 18 25 PM

 

Logging into OpenSwitch

In the last part of this post, we’re going to login to the OPS image.  The default username for the appliance build is root with no password.  Simply type in the username and you should be in the system

If you want to jump ahead of the next post, you would now type vtysh at the command prompt to pop into the quagga network shell which is where us network types will find ourselves most at home.

 

 

Screen Shot 2016 01 19 at 1 24 48 PM

What’s Next

In the next post, I’ll be looking at some basic configuration tasks, like adding an IP address and establishing basic network connectivity. If you have any issues getting this running, please feel free to post in the comments below, or even better, get involved in the OPS community by using the mailing list or the IRC Channels ( You can find information on all the ways to participate in the OPS community here

 

@netmanchris

More Jinja – Working with YAM as an Input

 

Jinja2 Simple YAML Example


We’re now going to take a look at grabbing a file from the hard drive written in YAML syntax. YAML is arguably the most human readable data serialization format which makes it really easy for coders and non-coders alike to work with.

We’re going to build on the last Jinja2 example. Instead of creating the templates and variables directly in python. We’re going to load them instead from files on our computer.

This may seem like a small detail, but this allows us to deconstruct the building of our configurations, meaning that different people can be responsible for different components of the configuration. As with anything, if you can break a complex process down into several smalller less complex tasks, the whole thing starts to feel easier.

Loading Libraries

We’ll start by loading the required libraries

In [2]:
import yaml
from jinja2 import Environment, FileSystemLoader, Template
 

Set the Environment

Essentially, this set’s the path which will define the directory where the templates will be loaded from. In this case, I’m setting it to load from the same directory.

In [3]:
ENV = Environment(loader=FileSystemLoader('./'))
 

Printing the YAML File

In this step we’re going to use the with open() as file: way of opening the file and just printing it out so you can have a look at what the YAML syntax actually looks like. The advantage of using the with open as… way of opening the file is that there’s no need to go back and close the file when you’re done. May not sound like a big deal, but trust me, files don’t like to be orphaned in an open pyton variable somewhere.

If you’re comfortable with configuring a network device by hand, you can PROBABLY figure out exactly what these variables are doing. The hostname istestswitch, the SNMP configuration are in the snmp section. The read string is supersecret, the vlans are in the vlans section, etc…

Really easy to understand exactly what’s going on here, right?

In [15]:
with open('simple_config.yaml') as file:
    print (file.read())
 
hostname: testswitch

ip: 10.101.0.221


snmp:
    read: supersecret
    write: macdonald
    syscontact: admin.lab.local
    syslocation: lab
    trap:
    - {target: 10.101.0.200}
    - {target: 10.101.0.201}
    - {target: 10.101.0.202}



vlans:
- {description: management vlan, id: '10', name: management}
- {description: users vlan, id: '15', name: users}
- {description: phones vlan, id: '16', name: phones}
- {description: servers vlan, id: '20', name: servers vlan }
 

Loading the YAML File

In this next step, we’ll use the same with open as… way of opening the file, except rather than just reading the file this time, we’re going to use the yaml.loadmethod to load the contents of that file into a python variable called simple.

The nice thing is that the YAML library takes care of all the hard work for us. Automation is supposed to make things easier, right?

In [12]:
with open("simple_config.yaml") as simple:
    simple =  yaml.load(simple)
 

Looking at the simple variable

So now that we’ve loaded the contents of simple.yaml into the simple variable, let’s take a look at what python sees. We’ll run the type() command first to see what kind of object simple has been created as, and then we’ll print out the contents of simple so you can see how the YAML library has transformed it from the text in the YAML file to something which is easier to work with in python.

In [16]:
type(simple)
Out[16]:
dict
In [11]:
simple
Out[11]:
{'hostname': 'testswitch',
 'ip': '10.101.0.221',
 'snmp': {'read': 'supersecret',
  'syscontact': 'admin.lab.local',
  'syslocation': 'lab',
  'trap': [{'target': '10.101.0.200'},
   {'target': '10.101.0.201'},
   {'target': '10.101.0.202'}],
  'write': 'macdonald'},
 'vlans': [{'description': 'management vlan',
   'id': '10',
   'name': 'management'},
  {'description': 'users vlan', 'id': '15', 'name': 'users'},
  {'description': 'phones vlan', 'id': '16', 'name': 'phones'},
  {'description': 'servers vlan', 'id': '20', 'name': 'servers vlan'}]}
 

Templates


Printing the simple_cisco template

In this step we’re going to take a look at the simple_cisco.j2 template that I’ve created. This is a very simple configuration just to show the power of jinja for making your network configurations.

If you look closely, you’ll see a couple of {% for…%} tags in here. This is a control structure called an interator, commonly known as a For loop. Essentially it’s saying, take the list of things and do this one action to each of the things in the list.

One thing to note here is that j2 is not a jinja2 specific file extention, but just something that I, and many others I’m sure, use to designate their template files.

In [28]:
with open('simple_cisco.j2') as file:
    print (file.read())
 
#hostname config
hostname {{ simple['hostname'] }}
#vlan config
{% for vlan in simple['vlans'] -%}
vlan {{ vlan['id'] }}
    name {{ vlan['name'] }}
    description {{ vlan['description'] }}
{% endfor %}#snmp config
snmp-server community {{ simple['snmp']['read'] }} RO
snmp-server community {{ simple['snmp']['write'] }} RW
snmp-server ifindex persist
snmp-server location {{ simple['snmp']['syslocation'] }}
snmp-server contact {{ simple['snmp']['syscontact'] }}
{% for trap in simple['snmp']['trap'] -%}
snmp-server host {{ trap['target'] }}  public
{% endfor %}
 

Rendering the simple_cisco template

In this last step of working with the simple_cisco template, we’re going to now pass this through the jinja rendering engine. Because we’ve got dynamic parts in the template, we’re going to have to supply a source for the variables to fill the dynamic part in. If you look below, we’re saying that anytime you see the wordsimple in the template, you should look in the variable simple we created above and see if there’s the appropriate information there to fill it in.

In simpler terms, we’re going to take the simple.yaml file we loaded above as the input values into this template.

We then render the template and…

In [29]:
template = ENV.get_template("simple_cisco.j2")
print (template.render(simple=simple))
 
#hostname config
hostname testswitch
#vlan config
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers vlan
    description servers vlan
#snmp config
snmp-server community supersecret RO
snmp-server community macdonald RW
snmp-server ifindex persist
snmp-server location lab
snmp-server contact admin.lab.local
snmp-server host 10.101.0.200  public
snmp-server host 10.101.0.201  public
snmp-server host 10.101.0.202  public

 

Look familiar right? Minimal typing. Alll the VLANS are there, etc… and the best thing is if I run this a thousand times, it will always come out the same way.

 

But I have a multi-vendor network!!!!

This is the true power of jinja for me. I happen to run multiple vendors in my lab, but I’d like to have the ability to drive all of the configurations from a central location to make sure that they all have the same vlans, usernames and passwords, snmp strings, etc…

So now let’s take a look at running another vendor’s template using the same simple.yaml file as the input source.

 

Printing the simple_comware template

In this step we’re going to take a look at the simple_comware.j2 template that I’ve created. This is a very simple configuration just to show the power of jinja for making your network configurations.

You’ll notice that it’s very close to the simple_cisco.j2 file shown above. The real difference is the parts outside of the jinja2 variables. HPE Networking’s comware devices use the keyword sysname instead of hostname. They use the keyword snmp-agent instead of snmp-server. Minor differences in the syntax, but the actual values are exactly the same for both devices.

In [30]:
with open('simple_comware.j2') as file:
    print (file.read())
 
#sysname config
sysname {{ simple['hostname'] }}
#vlan config
{% for vlan in simple['vlans'] -%}
vlan {{ vlan['id'] }}
    name {{ vlan['name'] }}
    description {{ vlan['description'] }}
{% endfor %}#snmp_config
snmp-agent
snmp-agent community read {{ simple['snmp']['read'] }}
snmp-agent community write {{ simple['snmp']['write'] }}
snmp-agent sys-info contact {{ simple['snmp']['syscontact']  }}
snmp-agent sys-info location {{ simple['snmp']['syslocation'] }}
snmp-agent sys-info version all
 

Rendering the simple_comware template

In this step, we’re going to render and print the template using the same simple variable that we used for the simple_cisco.j2 template above. Because of the difference in the simple_comware.j2 templates, it will render with the proper syntax for the HPE devices.

In [31]:
template = ENV.get_template("simple_comware.j2")
print (template.render(simple=simple))
 
#sysname config
sysname testswitch
#vlan config
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers vlan
    description servers vlan
#snmp_config
snmp-agent
snmp-agent community read supersecret
snmp-agent community write macdonald
snmp-agent sys-info contact admin.lab.local
snmp-agent sys-info location lab
snmp-agent sys-info version all
 

Wrap Up

I’m sure you’ll agree this is a much better way of creating your configurations than grabbing a console cable and typing them all out by hand, right? But there’s still a lot of room for improvement! Currently, we’re going to render the template and then cut and paste them into a console cable. So although we’ve made some gains… it could be better.

We’ll take a look at that next time.

@netmanchris

 

Intro to Jinja2

 
 

What is Jinja2

Jinja2 is a templating language that was originally used as part of the Flask python web framework. From the Jinja2 website

Jinja2 is a full featured template engine for Python. It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed

It was originally developed to help automatically generate HTML dynamically as part of the flask framework, more on that in another post, but it can also easily be used to help us generate our configuration files for our infrastructure devices.

This is going to be a very simple introduction to a few of the basic concepts of that jinja uses which, hopefully, will help to understand how Jinja can be used as a first step down the road of gaining automation skills.

We’ll take a look at a developing some intuition on how Jinja2 can be used to create basic network infrastructure device configurations. This is definitly not the modern method of interfacing directly into the control/data/management plane of devices using APIs, but it’s definitely a step in the right direction on understanding how a bit of code can help make your life better.

Prereqs

I’m assuming you’ve already got python installed on your system. You’re also going to need to run the pip install jinja2 command from a terminal window to get the latest version of jinja2 which should work just fine here.

 

Learning by Example

In this section we’ll start with a small example on how to create a few VLANs using the typical syntax from a modern networking OS. In this case, I used the HPE Comware syntax, but it would be easy enough to create this using a Cisco or Juniper configuration and you’re encouraged to try to get this working with your own network vendor.

 

Import required libraries

First We’ll import the required modules from the Jinja2 library. This is pretty much stolen directly from the jinja2 docs.

In [1]:
from jinja2 import Environment, FileSystemLoader, Template
 

Creating the VLANS

For this example, we’re going to create a python list of dicts which contains six different VLANS as listed in the tabel below.

A python dictionary is just a key-value pair, where the value for a specific key in the dictionary can be accessed using the key name.

  • Name: Name of the VLAN
  • Description: Descrition of the VLAN
  • VLAN ID: Dot1q VLAN ID.
Name Description VLAN ID
Management Management VLAN 10
Users Users VLAN 15
Phones Phones VLAN 16
Servers Servers VLAN 20
Mobility Mobility VLAN 30
Guest Guest VLAN 40
In [5]:
vlans_list = [{'name': 'management', 'description': 'management vlan', 'id': '10'},
         {'name': 'users', 'description': 'users vlan', 'id': '15'},
         {'name': 'phones', 'description': 'phones vlan', 'id': '16'},
         {'name': 'servers', 'description': 'servers vlan', 'id': '20'},
         {'name': 'mobility', 'description': 'mobility vlan', 'id': '30'},
         {'name': 'guest', 'description': 'guest vlan', 'id': '40'},
         {'name': 'rob', 'description': 'guest vlan', 'id': '45'}
         ]
 

VLAN Jinja2 Template

In this step, we’re going to create a variable called text_file which will contain the content of a jinja2 template. This is a basic python string object which means, at this point, it’s just a bunch of text.

In normal circunstances, we would actually be reading this template from a file located on the hard drive, but for our purposes today, we’ll just put the templatein by hand.

What makes Jinja2 powerful is the control structures that allow it to perform programatic operations. In this example, we’re creating a For loop.

Following the code we will each vlan in the vlans object we created above and then render the template using the ‘id’ key for the first variable, the ‘name’ key for the second variable, and the ‘description’ key for the last variable.

Hopfully, this makes sense, but if not, just hold on and it should become clear before the end.

In [6]:
text_file = ('''
#vlan config
{% for vlan in vlans -%}
vlan {{ vlan['id'] }}
 name {{ vlan['name'] }}
 description {{ vlan['description'] }}
{% endfor %}''')
 

If I was to write the same as a traditional python iterator it would look something like this. You can see how they are related I hope?

In [7]:
for vlan in vlans_list:
    print ('''vlan ''' +vlan['id']+
           '''\n name '''+vlan['name']+
           '''\n description '''+vlan['description'])
 
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers
    description servers vlan
vlan 30
    name mobility
    description mobility vlan
vlan 40
    name guest
    description guest vlan
vlan 45
    name rob
    description guest vlan
 

That’s a lot of work typing isn’t it?

You could ask

That’s more typing than I would do by hand? Why would I use this?

Great question. The point of automating anything is to cut down on the repetitive effort it takes to accomplish a given goal. In this case, we can simply count the number of key strokes it would take to create a single new VLAN on a switch.

In [6]:
count_chars = "vlan', 'name', 'description"
keystrokes = len(count_chars)
print (keystrokes)
 
27
 

Now let’s pretend we had to type that 10 times

In [7]:
keystrokes * 10
Out[7]:
270
 

Or maybe we had to create 100 VLANs.

In [10]:
keystrokes * 100
Out[10]:
2700

Ir maybe we had to create the full 4094 VLANs available

In [11]:
keystrokes * 4094
Out[11]:
110538
 

Not sure about you, but if I don’t have to type 110,000 keystrokes, my fingers will love me at the end of the day. Not to mention the fact that it’s also repeated perfectly every single time, not a single typo in there.

 

Create the Template Object

Now that we’ve created the text_file string object, we need to transform it into a jinj2 template which will allow us to then render it. We will create a new object called vlan_template and assign an instance of the Template class using the text_file contents as the input.

In [12]:
vlan_template = Template(text_file)
 

Make the Magic Happen

We will now use the render method on the vlan_template that we created above. We have a single argument to pass into the function. In this case we are passing the vlans_list list of dictionaries we create above in to the function as the vlans variable.

In [13]:
vlan_template.render(vlans=vlans_list)
Out[13]:
'\n#vlan config\nvlan 10\n    name management\n    description management vlan\nvlan 15\n    name users\n    description users vlan\nvlan 16\n    name phones\n    description phones vlan\nvlan 20\n    name servers\n    description servers vlan\nvlan 30\n    name mobility\n    description mobility vlan\nvlan 40\n    name guest\n    description guest vlan\n'
 

Hmmm What happened there?

That doesn’t look like a configuration file does it? The output of this file is actually a python string object. In python, we need someway to represent a carriage return (enter-key) and the \n just happens to have that honour.

Instead of running the template rendering directly, we can instead capture the output into a string object which we will then pass to the print command.

In [14]:
rendered_template = vlan_template.render(vlans=vlans_list)
In [15]:
print (rendered_template)
 
#vlan config
vlan 10
    name management
    description management vlan
vlan 15
    name users
    description users vlan
vlan 16
    name phones
    description phones vlan
vlan 20
    name servers
    description servers vlan
vlan 30
    name mobility
    description mobility vlan
vlan 40
    name guest
    description guest vlan

 

Clear?

Hopefully, this has shown you a bit of how a basic jinja control structure, like a For loop, can be used to cut down on a lot of key strokes, increase the accuracy of the configurations and help to streamline the operations.

In the next post, I’ll look at loading YAML files directly into python and using their contents as input into some more advanced jinja2 templates.