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
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.
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
Juniper
Vyatta
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!
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
LikeLike
Hi Marius, thanks for your tip. I’ll definitely try netmiko in my next lab 🙂
LikeLike
Another neat idea might be to add a vpn into the mix with a remote site. 🙂
LikeLike