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!

6 comments

  1. Marius Cornea · August 25, 2015

    You can check netmiko[1] for simplified Paramiko connection to net devices. I think Vyatta is not supported but it’s good opportunity to contribute 🙂

    [1] https://github.com/ktbyers/netmiko

    Like

    • gabrielegerbino · August 25, 2015

      Hi Marius, thanks for your tip. I’ll definitely try netmiko in my next lab 🙂

      Like

  2. Pingback: Project:Me10 – 40% Completed | project:me10
  3. Pingback: Enabling Network Automation using NTC-Ansible | project:me10
  4. Pingback: So you want to start with Network Automation… | project:me10
  5. FredR · April 11, 2016

    Another neat idea might be to add a vpn into the mix with a remote site. 🙂

    Like

Leave a comment