A Cisco, a Juniper, a Vyatta Router…and an Ansible Playbook

Here we are, 2.5 months are passed since I started this challenge¬†and it’s time to do some kind of review and summary of what I’ve done during this time:

  • I’ve publised 9 blog posts (with this one being the 9th)
  • I’ve published 2 interviews with engineers from Google and Cisco (another interview is already completed and I’ll post it soon)
  • I’ve completed a Coursera’s course about Python
  • I’ve achieved the Juniper JNCIA certification
  • I’ve achieved the Vyatta BCVRE certification
  • I’ve started to study and practice network automation and Linux skills

I’m quite satisfied so far, but my last goal is still too far and I need to work harder if I want to reach it.

In regards to the list above, I decided to build a little virtual lab to make some practice (and have fun ūüôā ). Specifically, I’m going to build a network with a Cisco router, a Juniper router and a Vyatta one. Then, I’m going to automatically generate EBGP configurations for each of them using Ansible and, lastly, push them to¬†the devices using a Python library.


ANSIBLE

Ansible is a great tool used to automate many kind of tasks, including the configuration and maintainance of IT infrastructure. It can also be used to help network engineers to simplify their day-to-day work and what we’ll see during¬†this post is just a veeeeeery little use case. This is the first time I play with it but I’m really interested in digging deeper.

Ansible Playbook’s key components I’ve used are:

  • Roles: in my case, I’ve used only one role,¬†router.
  • Tasks:¬†once the role is defined, Ansible will look for any¬†tasks to be completed.
  • Templates: a template is, basically, a model. I this case it’ll be a router configuration model.
  • Vars:¬†Ansible will build the actual configuration based on the defined model, using some values identified as vars

I’m pretty sure that this explanation is far to be clear at this time, but you can find many useful information on the official documentation website.


LAB SCENARIO

lab

The network is built using GNS3 and its VirtuaBox integration. This network includes:

  • A Cisco router:¬†Cisco IOS Software, 3600 Software (C3640-JK9S-M), Version 12.4(16)
  • A Juniper router:¬†JunOS Olive 12.1R1.9 (in VirtualBox)
  • A Vyatta router:¬†Brocade Vyatta 5415 vRouter 6.7 R9T60 (in VirtualBox)
  • A Linux machine: Ubuntu (in VirtualBox)

The devices are minimally configured, as shown here below where only the important configuration section, including interface configurations, are highlighted. In addition to this, I’ve enabled and configured SSH access for the user gabriele.

CISCO CONFIGURATION

interface FastEthernet0/0
 ip address 172.16.2.1 255.255.255.0
 duplex auto
 speed auto
!
interface FastEthernet1/0
 ip address 172.16.3.1 255.255.255.0
 duplex auto
 speed auto
!
interface FastEthernet2/0
 ip address 172.16.1.1 255.255.255.0
 duplex auto
 speed auto
!

JUNIPER CONFIGURATION

[edit]
gabriele@Router1# show interfaces
em0 {
   unit 0 {
      family inet {
         address 172.16.4.1/24;
        }
    }
}
em1 {
   unit 0 {
      family inet {
         address 172.16.3.2/24;
        }
    }
}

VYATTA CONFIGURATION

[edit]
gabriele@vyatta# show interfaces
   ethernet eth0 {
   address 172.16.2.2/24
   duplex auto
   hw-id 08:00:27:d2:5f:38
   smp_affinity auto
   speed auto
 }
   ethernet eth1 {
   address 172.16.4.2/24
   duplex auto
   hw-id 08:00:27:58:e3:1c
   smp_affinity auto
   speed auto
 }

ANSIBLE CONFIGURATION

My project’s directory tree looks like this:


gabriele@gabriele-VirtualBox:~/Desktop/BGP$ find . -type d
.
./roles
./roles/router
./roles/router/vars
./roles/router/tasks
./roles/router/templates

Inside the tasks subdirectory there is a file named main.yml


---
- name: Generate configuration files
 template: src=router_cisco.j2 dest=/home/gabriele/Desktop/BGP/{{item.hostname}}.txt
 with_items: cisco_template

- name: Generate configuration files
 template: src=router_juniper.j2 dest=/home/gabriele/Desktop/BGP/{{item.hostname}}.txt
 with_items: juniper_template

- name: Generate configuration files
 template: src=router_vyatta.j2 dest=/home/gabriele/Desktop/BGP/{{item.hostname}}.txt
 with_items: vyatta_template

The “—” pattern at the beginning of the file indicates it is a YAML file. The field called¬†name¬† indicates the name of the tasks that have to be execute: generation of 3 template based on the structure of something called¬†cisco_template, juniper_template¬†and¬† vyatta_template.¬†Where are those models?

They resides inside the template directory..


gabriele@gabriele-VirtualBox:~/Desktop/BGP/roles/router/templates$ ls
router_cisco.j2 router_juniper.j2 router_vyatta.j2


..and they appear as follows.

router_cisco.j2


configure terminal
{% for interface in cisco_loopback %}
interface {{interface.name}}
ip address {{interface.address}} {{interface.mask}}
{% endfor %}

router bgp {{item.as}}
{% for neighbor in cisco_neighbors %}
neighbor {{neighbor.id}} remote-as {{neighbor.as}}
{% endfor %}
{% for loopback in cisco_loopback %}
network {{loopback.network}} mask {{loopback.mask}}
{% endfor %}


juniper_template.j2


configure
set protocols bgp group external-peers type external
set routing-options autonomous-system {{item.as}}
{% for neighbor in juniper_neighbors %}
set protocols bgp group external-peers neighbor {{neighbor.id}} peer-as {{neighbor.as}}
{% endfor %}
{% for loopback in juniper_loopback %}
set interface lo0 unit 0 family inet address {{loopback.address}}
set policy-options prefix-list Loopback {{loopback.network}}
{% endfor %}
set policy-options policy-statement ebgp term 1 from prefix-list Loopback
set policy-options policy-statement ebgp term 1 then accept
set protocols bgp group external-peers export ebgp
commit


vyatta_template.j2


configure
set protocols bgp {{item.as}}
{% for neighbor in vyatta_neighbors %}
set protocols bgp {{item.as}} neighbor {{neighbor.id}} remote‚Äźas {{neighbor.as}}
{% endfor %}
{% for loopback in vyatta_loopback %}
set interface loopback lo address {{loopback.address}}
set protocols bgp {{item.as}} network {{loopback.network}}
{% endfor %}
commit


These are Jinja2 files and, basically, are a set of configuration commands with something “strange”¬†inside. In fact, all the things inside curly braces are variable that Ansible will use to build the actual models.

So, for exampe, the snippet below means that somewhere (inside the vars directory) exists an iterable called vyatta_loopback which can be looped and its values as and network are assigned to the template.


{% for loopback in vyatta_loopback %}
set interface loopback lo address {{loopback.address}}
set protocols bgp {{item.as}} network {{loopback.network}}
{% endfor %}

One last thing should be examinated: the vars directory. Anyway, in this stage it is empty. Why? Because I decided to let the user to dinamically configure it using a Python script.


BGP Script

The whole project, including the Python script can be found here.

We can choose to execute the AutomateBGP.py in 2 ways:

  • including some options like¬†username¬†and¬†password¬†to be used for the SSH connection to the devices,¬†and a file_name¬†containing devices’ IP addresses and platform.
  • if we execute it without including any parameters, the script will ask us to insert all the missing values.

After this, the user will be asked to insert some information about Loopback interfaces (address, network and mask), BGP AS and BGP neighbors.

screen

Now that we have all the information we needed, we can go back to the vars directory, where the script creates a new main.yml file containing all the variables needed by Ansible to build the templates.


---
cisco_template:
- { hostname: cisco_template, as: 10 }

cisco_loopback:
- { name: lo0, address: 1.1.1.1, network: 1.1.1.0, mask: 255.255.255.0 }
- { name: lo1, address: 11.11.11.11, network: 11.11.11.0, mask: 255.255.255.0 }

cisco_neighbors:
- { id: 172.16.2.2, as: 30 }
- { id: 172.16.3.2, as: 20 }

juniper_template:
- { hostname: juniper_template, as: 20 }

juniper_loopback:
- { name: 1, address: 2.2.2.2/24, network: 2.2.2.0/24 }
- { name: 2, address: 22.22.22.22/24, network: 22.22.22.0/24 }

juniper_neighbors:
- { id: 172.16.3.1, as: 10 }
- { id: 172.16.4.2, as: 30 }

vyatta_template:
- { hostname: vyatta_template, as: 30 }

vyatta_loopback:
- { name: 1, address: 3.3.3.3/24, network: 3.3.3.0/24 }

vyatta_neighbors:

- { id: 172.16.2.1, as: 10 }
- { id: 172.16.4.1, as: 20 }

The next step is to let Ansible generate all the configuration template.


os.system("ansible-playbook site.yml")

This line of code has the effect to create 3 new text files: cisco_template.txt, juniper_template.txt, vyatta_template.txt.


PLAY [Generate router configuration files] ************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [router | Generate configuration files] *********************************
changed: [localhost] => (item={'as': 10, 'hostname': 'cisco_template'})

TASK: [router | Generate configuration files] *********************************
changed: [localhost] => (item={'as': 20, 'hostname': 'juniper_template'})

TASK: [router | Generate configuration files] *********************************
changed: [localhost] => (item={'as': 30, 'hostname': 'vyatta_template'})

PLAY RECAP ********************************************************************
localhost : ok=4 changed=3 unreachable=0 failed=0

cisco_template.txt


configure terminal
interface lo0
ip address 1.1.1.1 255.255.255.0
interface lo1
ip address 11.11.11.11 255.255.255.0

router bgp 10
neighbor 172.16.2.2 remote-as 30
neighbor 172.16.3.2 remote-as 20
network 1.1.1.0 mask 255.255.255.0
network 11.11.11.0 mask 255.255.255.0

juniper_template.txt


configure
set protocols bgp group external-peers type external
set routing-options autonomous-system 20
set protocols bgp group external-peers neighbor 172.16.3.1 peer-as 10
set protocols bgp group external-peers neighbor 172.16.4.2 peer-as 30
set interface lo0 unit 0 family inet address 2.2.2.2/24
set policy-options prefix-list Loopback 2.2.2.0/24
set interface lo0 unit 0 family inet address 22.22.22.22/24
set policy-options prefix-list Loopback 22.22.22.0/24
set policy-options policy-statement ebgp term 1 from prefix-list Loopback
set policy-options policy-statement ebgp term 1 then accept
set protocols bgp group external-peers export ebgp
commit

vyatta_template.txt


configure
set protocols bgp 30
set protocols bgp 30 neighbor 172.16.2.1 remote‚Äźas 10
set protocols bgp 30 neighbor 172.16.4.1 remote‚Äźas 20
set interface loopback lo address 3.3.3.3/24
set protocols bgp 30 network 3.3.3.0/24
commit


PUSH IT!

One last step remains to complete the lab: pushing the configuration to the devices. I used Paramiko¬†to do so. It is a useful Python library to interact with network devices. I’ve written a¬†run_command()¬†function to split each template in single commands and then pushing them. Once the configuration is completed, the script informs you about the accomplishment.


def run_commands(ip_address, user, password, commandList, platform, buffer=5000):
    """ this function runs the specified commands on the node. """
    print "Configuring " + ip_address
    remote_conn_pre = paramiko.SSHClient()
    remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    remote_conn_pre.connect(ip_address, username=user, password=password)
    remote_conn = remote_conn_pre.invoke_shell()
    if platform == "cisco":
        remote_conn.send("enable\n")
        time.sleep(1)
        remote_conn.send(password+'\n')
        time.sleep(1)
    commands = commandList.split('\n')
    for com in commands:
        remote_conn.send(com+'\n')
        time.sleep(1)
        output = remote_conn.recv(buffer)

LET’S VERIFY

Now we can verify that everything has gone well (click the images to watch them larger)

Cisco

show

Juniper

show1

Vyatta

show2


CONCLUSIONS

It’s been really funny to code for this lab. If you look at the code, you’ll see it isn’t well optimized at all and it can be improved to catch exception, errors, adding use cases and so on. Anyway, my goal with this lab was to start playing with Ansible and to write some code, but it’d be too lenghty trying to optimize it at the moment.

I’m really interested in this kind of stuff so I’m always looking for new ideas to make some practice. Any kind of suggestion, tips or feedbacks are highly appreciated ūüôā

Bye!

Summer update

After a little break due to university exams and other tasks, here we are again with the challenge.

As I said before, my plan is to improve my Linux and Automation skills during August, delaying the start of my JNCIS studies. Based on all this, the plan includes:

  • Studying Linux systems. In order to have a structured approach to the subject I’ll start reading LPIC-1 stuff too. The reading list includes¬†LPI Linux Certification in a Nutshell¬†and¬†The Linux Command Line: A Complete Introduction.
  • Go automate. The commitment is to start thinking and working on personal projects about¬†network automation (I need some ispiration and ideas, so suggestions are more than¬†welcome ūüôā ) and I’ll also work on a project shared with a friend. More updates in next posts.
  • Publish at least two more interviews to network engineer interns.

This will be a practical month, so expect labs, simulations and other fancy stuff. It’ll be fun ūüėÄ


P.S.

Facebook has published a new video about Career at FB and I find it absolutely stunning. It makes me think “Shut up and take my resume!” ūüėÄ


“When you find an opportunity grab it, run with it, make it your own”

First Milestone Reached: JNCIA Certified

Good news over here: today I passed the JNCIA exam, the first milestone of my Project:Me10 journey! ūüėÄ

I’m reeeeeally happy about that! I had no experience with Juniper products and at first I found it a little bit difficult to start typing in Junos (I used to think in IOS ūüôā ) but, once this first obstacle has been overcome, the preparation has been pretty easy.

Junos OS is an interesting platform and the JNCIA exam is focused on convincing you about this (succeeding). A nice thing about the Juniper Network Certification Program, is that you can obtain a 50% voucher over the exam prize passing a practice test on their website. That’s great! I only spent 50$ for my JNCIA exam!

My only complaint¬†is about the exam blueprint¬†which¬†lacks of focus on¬†major networking protocols and technologies. The certification is designed to be an introduction to Juniper’s world, but I thought I was gonna find more¬†CCNA-like contents. That’s why I can’t wait to start my JNCIS journey (which is the second milestone of the Project), in which I’ll find some interesting topics like IS-IS, BGP and High Availability (among many others).

But first I’ll take a little break and I’ll work on some Linux and Network Automation skills (and projects ūüôā ).

Stay tuned.

P.S.

Few weeks ago I’ve also won a 10$ Amazon Gift Card solving a game posted by David Swafford¬†(Datacenter Network Engineer at Facebook) in a network automation focused group on Facebook, which required to parse this command output in order to obtain a dictionary of port-channel numbers to their member interfaces (my solution here).

Nothing special, but something is better than nothing ūüėÄ

Update

Here it is a little update about my jouney.

As stated into the first post, my first milestone for¬†this challenge will be achieving the Juniper JNCIA certification and during the past days I’ve started to¬†study. In order to do so, I’m using several resources:

  • Junos as a Second Language-WBT:¬†I’ve already completed this mini-course whose focus is to¬†make easier the transition from other vendor based CLI (Cisco IOS) to Junos.
  • CBT Nuggets: Interesting video lectures held by Scott Morris discussing all the JNCIA exam objects.
  • Juniper Fast Track¬†stuff, including a free JNCIA study guide from Juniper itself.
  • GNS3 with Olive support, in order to emulate some simple network and get used to¬†the new configuration environment and syntaxt.

Although I’ve not yet dived deep into the subject, I think Junos has some really cool features like the separation between Control Plane and Data Plane, inner modularity with processes memory separation (so that no single fail would affect the whole system), FreeBSD based (so that it feel like working with Linux).

At the same time, I’m using this¬†Coursera course¬†as a refresher for my Python not-that-awesome skills.