Hello my friend,
In this article I’ll finalize the review of the Ansible for automation network tasks, which I’ve started the last week in the previous article. The main focus is the per-node parametrization of the playbooks, so that you can really successfully use Ansible for massive roll outs in networks, built with Nokia (Alcatel-Lucent) SR OS and Cisco IOS XR.
1 2 3 4 5 | No part of this blogpost could be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical or photocopying, recording, or otherwise, for commercial purposes without the prior permission of the author. |
Brief overview
The last week I was discussing Ansible in general and playbooks from my previous article with my colleague from customer’s service provider. He has made reasonable remark that my automation works but it’s necessary to change IPv4/IPv6 address at the interface afterwards. This fact eliminates the positive effect from the rollout.
I’ve bought wonderful book Ansible for DevOps and found a lot of interesting and useful information, which has helped me to add parametrization depending on particular node and turn usage of Ansible for network tasks from fancy toy to real helper in roll outs. So I want to share with you this information alongside with real playbooks that you can reuse.
What are we going to test?
We are continue deploying BGP/MPLS IP VPNs, so I’m going to solve the same task as in the first article about Ansible. My task is to create such Anisble’s playbook that will automatically create VPRN/VRF and all related parameters, just like it was previously. But newly configured interfaces must have different IPv4 and IPv6 addresses at different nodes. So main focus is to achieve such agility keeping playbook simple.
Topology
Physical topology is the same as it was in previous article:
Logical topology is also the same:
Management in our lab is also the same, as it was in the previous lab:
For explanation about lab topology and used protocols refer to the previous article about Ansible for Nokia (Alcatel-Lucent) SR OS and Cisco IOS XR.
Here you can find initial configuration files: xr3_initial xr4_initial linux_initial sr1_bof sr1_initial sr2_bof sr2_initial
Resolving node names
In the previous article we have created an inventory file that is used for gathering nodes, where playbooks or ad-hoc commands must be performed. The entries were IPv4 addresses. It’s possible, but in general not very convenient. In production environment you have an established DNS system, so you can just put in hosts names of your devise. I don’t have DNS server configured, so I’ll update system hosts file in order to map names to IP addresses.
[root@localhost /]# cat /etc/hosts |
The next step is to generate new SSH keys for these nodes, otherwise Ansible won’t able to connect to the devices. Just connect to them before playing playbooks:
[root@localhost ~]# ssh admin@SR1 |
It isn’t necessary, but it will make things more clear later.
BGP/MPLS IP VPN Service topology
At the picture above you see how the service that we are going to build looks like:
We follow the same guidelines for IP VPN service as in the previous lab. IP VPN supports both IPv4 and IPv6 address families; route-distinguisher is configured as IPv4_address_system_interface:100 and route-target is 65000:100. In each VRF/VPRN we have an interface with the same VLAN tag that is 444 and the same access-list that allows access only to certain subnet (IPv4: 192.168.0.0/24, IPv6: fc00::192:168:0:0/96) for attached nodes. But it must be possible to change all of these parameters (and some others) globally or per node easily without modifying playbook, so that by playing one book each node receives its specific values.
Short summary of Ansible’s variables
You remember that Ansible uses YAML as scripting language for its playbook. I’m pretty sure that each scripting language has possibility to define variables and to use them afterwards, and YAML isn’t something different. You may remember, we had the following code in the previous article:
[root@localhost ~]# cat new_nokia_new_service.yml |
There are dots in the beginning just to make structure visible. Omit them for the configuration.
Actually string “{{ inventory_hostname }}” is call of the variable, which has hostname of the node as it is in the inventory file. It’s a kind of built in variables, so we don’t define beforehand ourselves. But we’ve done it for one other variable, which is called “cli”:
[root@localhost ~]# cat new_nokia_new_service.yml | grep “cli” |
You might find such string in the previous playbooks. It’s actually call of variable “cli”, which was defined in the part “vars” in the beginning.
Another good information, which is crucial for us, is that we can keep variables in separate external files that are written in the same YAML format. These files are just mentioned in the beginning of the playbook. What is even better, the name of the file can be also variable, what allows us to call necessary variables depending on the node, which playbook is applied to.
Ok, basic theory regarding variables in YAML/Ansible is known to you, let’s use for building awesome playbooks.
Creating per node and per service configuration files
First of all let’s create files, where will be collected per node parameters, which must be applied during playbook execution. For the sake of convenience, I put them into separate folder in the folder, where the playbook lays:
[root@localhost ansible_test_scripts]# pwd |
Pay attention to the file names. They must have exactly the same name as nodes in the /etc/ansible/hosts.
After the files are created, we can put there whatever we want. For my task I’ve put there the following information:
[root@localhost nodes]# cat SR1.yml |
Later in the playbook I’ll be calling this parameters in the form “{{ node_var.rd_base }}” for example. It’s possible to remove that “node_var:” string and simply call “{{ rd_base }}”, but as I have a couple of external configuration files, it’s easier for me to understand reading the playbook, which file contains certain variable. Also you might spot that some parameters are different for Nokia (Alcatel-Lucent) SR OS and Cisco IOS XR, because in this certain case we need less parameters for Cisco IOS XR’s configuration.
The next folder that I want to create will contain connectivity information. As Nokia and Cisco has different credentials, I’m going to configure them:
[root@localhost nodes]# cd .. |
In these files I put information, what was previously contained in “vars” in the previous article:
[root@localhost login]# cat nokia_credentials.yml |
And the third folder and file that I’m going to create contains the variables, which are common for the whole service:
[root@localhost login]# cd .. |
It might be questionable, where to put VLAN_ID for the service: here or in per-node files. It’s absolutely up to you. From my experience, usually the VLAN id for customer or for service is the same across all routers in the network, just because it’s simple. On the other hand, PORT_IDs might be different depending on the router model. That’s why in my approach I put VLAN_ID in service template, whereas PORT_ID in the node template.
Modifying Ansible playbooks
Now it’s time to change Ansible playbooks for Nokia (Alcatel-Lucent) SR OS and Cisco IOS XR in the way they start to use variables instead of fixed values:
Remember, playbooks lay in the root folder comparing to creates sub folders
Ansible playbook | Ansible playbook |
Nokia (Alcatel-Lucent) SR OS | Cisco IOS XR |
[root@localhost ansible_test_scripts]# cat nokia_ip_vpn.yml |
[root@localhost ansible_test_scripts]# cat cisco_ip_vpn.yml |
Spaces before words is absolutely important for YAML. Make sure they are consistent like in my example. Remove “.” In the beginning of each string and you copy-paste this config.
These playbooks do just the same, what they have done previously: they create IP VPN service across network devices either Nokia (Alcatel-Lucent) SR OS based or Cisco IOS XR based.
For information about BGP/MPLS IP VPN refer to corresponding article.
You may see that here we don’t have any single parameter that is defined in these playbooks. All parameters are called from external files, whereas these files are defined in the beginning of the playbook within “vars_files” part. Of particular interest is how we call per node files. You see that they are called as “nodes/{{ inventory_hostname }}.yml”. This works as follows:
- playbook starts reading corresponding part of inventory file (hosts: nokia);
- it assigns the first read value “SR1” to the variable “inventory_hostname”;
- this effectively defines configuration file for node SR1: “nodes\SR1.yml”;
- variables “node_var.*” are loaded from that file the playbook;
- when the playbook is applied for the next node (i.e. SR2), its name is applied to “{{ inventory_hostname }}” and the process is done in the same way.
Let’s execute the playbook for Nokia (Alcatel-Lucent) SR OS routers and see what is going on:
[root@localhost ansible_test_scripts]# ansible-playbook nokia_ip_vpn.yml |
It looks like everything is configured properly. Let’s briefly compare created configuration for VPRN part:
Ansible playbook | Ansible playbook |
Nokia (Alcatel-Lucent) SR OS – SR1 | Nokia (Alcatel-Lucent) SR OS – SR2 |
A:SR1# configure service vprn 100500 |
A:SR2# configure service vprn 100500 |
It’s very impressive, isn’t it? We have just launched playbook once, and each Nokia (Alcatel-Lucent) VSR router has got configuration modified with its specific parameters. Basically we need to prepare playbook, node-specific and service-specific templates and launch our script. Rollout is done further automatically. Let’s briefly check the content of routing tables at Nokia (Alcatel-Lucent) SR OS routers and make ping verification:
A:SR1# show router 100500 route-table |
You don’t see any routes from Cisco IOS XR routers, because we haven’t executed that playbook and because I’m launching separately Nokia and Cisco routers due to limited resources at my laptop.
Let’s turn to Cisco IOS XR now. As playbook and all configuration templates are ready, I just have to execute it:
[root@localhost ansible_test_scripts]# ansible-playbook cisco_ip_vpn.yml |
This playbook has made the following changes at our Cisco IOS XR routers:
Ansible playbook | Ansible playbook |
Cisco IOS XR – XR3 | Cisco IOS XR – XR4 |
RP/0/0/CPU0:XR3#show configuration commit changes last 8 |
RP/0/0/CPU0:XR4#show configuration commit changes last 8 |
Our parametrization works quite well, so we make just the last check:
RP/0/0/CPU0:XR4#show route vrf ACME_100500 ipv6 |
Ok, for Cisco IOS XR our playbook works as well.
I’ve collected all my Anisble’s files from this lab in one tar archive so that it will be easier for you to test it and update for your needs. This archive contains also playbooks from the previous Ansible lab: ansible_nokia_cisco
Lessons learned
By the way, I haven’t tested in the details the initial configs in the previous lab and have missed one thing in Cisco IOS XR configuration. This thing is IPv6 addresses at loopback 0. They aren’t used anywhere in configuration, but they must be configured in order to bring ipv6/vpnv6 peering (though technically we use IPv4 address for BGP session and MPLS LSPs). This issue exists only in Cisco IOS XR, because in Cisco IOS / IOS XE there is no such problem. When I started to test pings between XR3 and XR4 I’ve found that I can perform then. After discovering that BGP is dead, I’ve configured IPv6 addresses at loopbacks 0 and everything started working.
Conclusion
Ansible is great thing to reduce the time for massive deployments. Personally I’ve started using it for reconfiguration of the devices for labs. With the parameterization its power only increases, as it comes from fancy-scripting-toys into something really useful for commercial operations. You can focus on learning new technologies or designing new solutions for your Nokia (Alcatel-Lucent) SR OS or Cisco IOS XR networks. Take care and good bye!
Support us
BR,
Anton Karneliuk
Hey Anton, great blog! Nice to see more Nokia out there.
Curious what your thoughts are on SROS support in Ansible versus old skool methods. I’ve been using ansible to control config generation prior to support for SROS. In the end I bypassed Ansible and just used jinja2 directly in python because it was more trouble to make a call to Ansible when I was just using it to template out correctly, something that can easily been done directly in a python app. I don’t see a huge benefit with SROS support when I can do whatever I need in python. Yes Ansible is slicker and more user friendly but I guess I don’t feel the need to go via Ansible when I already have tools that do the same thing. I still need to get variables in to Ansible to generate a config. How did you find the user friendliness of support? BTW Im not a programmer, I got python to do what I needed it to do 🙂
Hello Anton,
Again a great blog and very useful. But I want you to stress and talk more about getting the SSH connection work. What are the changes require in ansible.cfg file. I can ssh to my devices and keys are stored in ~known-host file but SSH fails as I mention connection type local. If I provide the SSH file location it work but in this case it is not scaling as one playbook can talk to one device only. Appreciate if you reveal more about how you got multiple ssh connection working.
Hi Amit,
Thanks for the feedback.
But it is common staff for SSH, that you have to obtain key before you can actively use it. And it’s done typically upon first login attempt. Or have I understood something wrong?
BR,
Anton
Hello Amit,
I’m wondering if you found a solution to this problem?