Understanding Ansible – Network Automation made easy

In the last few posts, we have kept our focus on the the APIs that we can leverage to automate many functionalities on UC appliances. In this post, we would be shifting our focus to another major component of a UC/Network architecture – Network Devices and how their control & management can be automated through Ansible. From a UC engineer’s perspective, Cisco IOS based devices hold as much importance as an IP PBX application or a Voicemail or a Contact Center appliance. Traditionally, we’ve always had an option to automate the configuration & management of IOS devices through Python and libraries like Paramiko/Netmiko. This still continues to work well. In fact, Ansible is doing the exact same thing. It’s basically hiding the python stuff behind the veneer of an English language like structure in which we write plays in a playbook. So, if you are already doing it and comfortable with it then you don’t necessarily need to stop that practice. However, you’ll soon learn that it is much easier to perform network automation related tasks through Ansible.

Ansible was originally developed as a means to automate vast deployment of Linux machines. For such a use-case, it is not that different from other configuration management tools like Puppet and Chef. Where it differs from others is the way it does the actual work. Whereas other tools require an agent or a plugin to be installed on the remote machines in order to enable them to receive control instructions from the master/control node, Ansible mandates no such requirement.

As we know, IOS on Switches/Routers/VGs is locked which means we can’t install any agent application on them for them to be controlled by an external entity. This limits our options but fortunately, Ansible fits perfectly in such a scenario. You only need to have an SSH connectivity and login credentials from the “control” machine to the remote system and that’s it. You don’t need to install any local agent apps on the destination devices or do any special sort of configuration to prepare them for control. This is what makes Ansible ideal for UC/Network engineers and renders others useless for our purposes.


Basic Architecture

Before we delve deeper into Ansible’s usage in Network Management, we need to go over some of the core components that form this ecosystem.

Control node

A system on which Ansible is installed. This is most likely a Linux machine. This is where you run Ansible commands to execute Playbooks.

Managed Nodes/Hosts

A remote system, or host, or a collection of hosts that Ansible controls. For example, remote servers, computers or switches, routers, voice gateways in a UC/Network context.

Inventory

A list of managed nodes that are logically grouped. This inventory is created on the control node & it can specify information specific to each node, like IP address. You can think of it as a TXT file that lists the IP address of every remote machine/switch/router whose configuration you want to manage.

Playbooks

Playbooks contain Plays and each play, in turn, contains tasks. These are written in a language called YAML.

Tasks

This is the ‘action’ that needs to be applied to the managed host. Tasks are always contained in a Play.

Modules

The code that Ansible executes on each managed node to accomplish the action defined in each Task. Each module has a particular use, from administering users on a specific type of database to managing VLAN interfaces on a specific type of network device.


Network Automation with Ansible

Ansible can be used to manage a plethora of network devices and appliances from multiple different vendors ranging from Cisco to Juniper to Arista. For the purpose of this post, we will be restricting ourselves to only Cisco. With respect to Cisco IOS devices, the two common Ansible modules that are used quite extensively are given below.

  • ios_command : Sends arbitrary commands to an ios node and returns the results read from the device
  • ios_config : This pushes the configuration commands to the remote devices. In simple terms, this can be used to do what we do in the “config” mode.

You can have a look at all available Cisco-IOS modules on the following link

https://docs.ansible.com/ansible/latest/collections/cisco/ios/index.html#plugins-in-cisco-ios

If you want to go over the list of supported modules across vendors, then you can refer to the following link

https://docs.ansible.com/ansible/latest/collections/index.html#list-of-collections


Workflow of a Typical Ansible Job

Ansible workflow for Network Automation
  • The first thing that we need to do is create a host/inventory file that basically lists the IP addresses/hostnames of the network devices on which we want to perform a certain action.
  • Once the inventory is finalized, we will begin the task of writing the Playbook. The Playbooks contain Plays whose job is to define what actions we want to perform on devices whose details are given in the host file. For example, assign IP address to the Gig 0/1 interface on 200 voice gateways or create an access list on 200 switches. Within the playbook, we define following items-
    • Connection Type
    • Inventory file
    • Tasks
    • The module we plan to use within that task
    • Finally the commands we want to push. For example, access-list 10 permit tcp any any to add a standard access list

This completes a basic introduction of Ansible and how it can be extremely beneficial to UC/Network engineers when we are dealing with network devices in bulk. Now let’s apply what we have learned to execute a simple Ansible Playbook.


Sample Exercise – Cisco Router

  • Objective : Read Interface Details on a Cisco CSR Router
  • Connection Type : Network_CLI
  • Inventory File : Add CSR Router’s IP in the default host file at /etc/ansible/hosts
  • Module : IOS_Command
  • Commands : show ip interface brief

Host File

The following is the host file that contains details of the remote machines. Let’s go through it in detail.

[ssingh7@localhost ~]$ cat /etc/ansible/hosts
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts

[router]
10.0.0.115 ansible_network_os=ios ansible_user=cisco ansible_password=cisco

[ssingh7@localhost ~]$
  • We first create a group called “router”. This is denoted by square brackets. This is a good practice to follow if your objective is to execute operations on different types of devices but you don’t want to have different host files. For example, You want to create an access list on 200 Cisco switches and also want to get current call volume on 10 Cisco CUBEs. These are 2 different devices. The call volume command is not going to run on a switch. So, you can create two groups – [switch] containing details of the 200 switches and [cube] containing details of 10 CUBEs. How you will use them will be explained when we go through the Playbook.
  • We then specify the remote device’s details. This includes the following:
    • IP Address
    • The OS type on the device.
    • Username and Password required to SSH into the device

Playbook

Since we are now ready with the host/inventory, the next task is to build the actual playbook. The following playbook is what’s needed to meet our objective. Let’s go through it in detail.

[ssingh7@localhost ~]$ cat /etc/ansible/example.yml
- name: "Connecting to CSR Router"           
  connection: network_cli
  hosts: router
  gather_facts: no

  tasks:
    - name: Capture Interface Details
      ios_command:
        commands: show ip int brief
      register: result

    - name: display results
      debug:
        var: result.stdout_lines
[ssingh7@localhost ~]$
  • We start off with giving a name to the play (Connecting to Cisco CSR Router)
  • We specify the Connection Type (network_cli)
  • We specify the name of the group from the host file. If we go back to our Switch and CUBE example, you’ll see that if I wanted to get call volume details then I could just specify the [cube] group here or if my objective was to create access lists on 200 switches then I could just specify the [switch] group here.
  • We create a task and give it a name (Capture Interface Details)
  • We then use the “ios_command” module
  • We issue the actual command that we want to execute on the remote router (show ip int brief) and store the outcome in a variable called “result”.
  • We create a new task to display the contents of the variable “result” and we print the stdout

Executing the Playbook

All playbooks are executed by ansible-playbook command followed by the location of the playbook.

  • The text highlighted in yellow shows the outcome of the command that we pushed to the remote device. It’s basically a dictionary that contains list items within it. If you are familiar with Python then you can manipulate this data further to extract only the “up” interfaces or interfaces with certain IP subnets etc.
  • The text highlighted in orange shows the status of this transaction. So, you have ok, changed, unreachable, failed that corresponds to successful read operation, successful update operation, no connectivity, code failure status respectively.
[ssingh7@localhost ~]$ ansible-playbook /etc/ansible/example.yml

PLAY [Connecting to CSR Router] *************************************************************************************************************************************************************************************************************

TASK [Capture Interface Details] ************************************************************************************************************************************************************************************************************
ok: [10.0.0.115]

TASK [display results] **********************************************************************************************************************************************************************************************************************
ok: [10.0.0.115] => {
    "result.stdout_lines": [
        [
            "Interface              IP-Address      OK? Method Status                Protocol",
            "GigabitEthernet1       10.0.0.115      YES NVRAM  up                    up      ",
            "GigabitEthernet2       unassigned      YES NVRAM  up                    up      ",
            "GigabitEthernet3       unassigned      YES NVRAM  administratively down down"
        ]
    ]
}

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
10.0.0.115                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

I hope this was helpful to anyone who is interested in exploring different ways to automate Network jobs. The objective of the post was to make readers familiar with Ansible and show its application in the real world. I will be demonstrating some more real world examples and use cases in the coming posts. So, stay tuned.

Please feel free to drop your feedback/suggestions, if any. Until then, Happy Learnings!!

Let’s connect on LinkedIn

Leave a Reply