An introduction to network automation with Ansible (original) (raw)

Thanks to tools like Ansible, network administrators can configure a vendor's network device without knowing its command-line syntax. Users can also implement configurations across several devices simultaneously.

This article introduces how to use Ansible for network automation, from getting started to running Ansible playbooks and troubleshooting errors. No knowledge of Ansible is necessary, as the article includes firsthand examples to demonstrate each process.

Get started with Ansible for network automation

When setting up Ansible, install it on a control node that connects to network devices and manages them. You need to define the network configuration in a YAML file called a playbook. For example, this playbook gathers the interfaces on a router.

- hosts: R1
gather_facts: false
tasks:
- name: Gather router interfaces
raw: 'show ip int brief'

Ansible uses three communication protocols to connect the control node to network devices:

  1. CLI over Secure Socket Shell (SSH). The network device CLI configuration is abstracted into a module and defined in a playbook. For example, to view a router's interface, you only need to specify an argument like l3_interfaces in the playbook. Ansible understands that you mean show ip interface brief and implements it over SSH.
  2. XML over SSH. Ansible uses Network Configuration Protocol, an XML-based protocol, to interact with the network device over SSH. It sends XML commands to retrieve or modify the device configurations. This is also defined in a playbook.
  3. API over HTTP or HTTPS. Here, Ansible interacts with a network device using REST APIs over HTTP or HTTPS. The configuration is sent in JSON to the device. These API calls are also defined in a playbook.

Let's examine how to implement network configurations automatically across two network devices with Ansible. You can apply the configuration with the CLI over the SSH communication protocol.

Configure Ansible for network devices

First, let's review the requirements needed for this lab, how to prepare the Ansible and network device environment before automation, and how to read a vendor's Ansible documentation.

You need the following to configure Ansible for your network devices:

Ansible directory and file structure

By default, Ansible stores files at /etc/ansible, including systemwide configuration files, roles and inventory.

ansible.cfg

root@Charles:/etc/ansible# tree
.
|-- ansible.cfg
`-- hosts

The ansible.cfg file is the main configuration file where you set the default location of specific files, such as inventory, logs, remote port number, roles and other assets.

For now, be familiar with the inventory file. Other files will be helpful as your knowledge builds.

hosts

This is the inventory file. It contains the IP addresses or hostnames of the devices to which you are connecting. You can group the devices and give them a name. For example, group devices based on their location, such as Boston or California.

The content in the Ansible default inventory host file uses the .ini format. It's usually in plaintext, which is difficult to read. Many network administrators prefer YAML because it's easier to decipher. This saves the file in the .yaml file type.

Configure DNS resolution on the Ansible control node

You need to add the IP address of the router's interface to your host file so your control node understands when it sees routers such as R1, R2, R3 and R4.

Also, the router's IP address must be in the same range as the Ansible control node.

vi /etc/hosts

192.168.122.161 R1
192.168.122.162 R2
192.168.122.163 R3
192.168.122.164 R4

Be sure not to mistake this host file with the one in the Ansible directory.

Manually configure the router's management interface and hostname

The management interface must be in the same range as the Ansible control node to prevent the node from interacting with it.

In a production network, these interfaces are usually automatically created using zero-touch provisioning or Dynamic Host Configuration Protocol.

Here's how to configure our example Cisco routers, R1 and R2.

configure terminal

hostname R1

interface Eth0/0
ip address 192.168.122.161 255.255.255.0
no shutdown

end


configure terminal

hostname R2

interface Eth0/0
ip address 192.168.122.162 255.255.255.0
no shutdown

end

And here's the same process for our Arista routers, R3 and R4.

configure terminal

hostname R3

interface Eth0/0
ip address 192.168.122.163 255.255.255.0
no shutdown


configure terminal

hostname R4

interface Eth0/0
ip address 192.168.122.164 255.255.255.0
no shutdown

Manually configure SSH on your devices

The next step is to ensure SSH is configured on the routers.

Here's the process for Cisco R1 and R2.

configure terminal

username cisco password cisco
username cisco privilege 15

line vty 0 4
 transport input all
 login local

exit

ip domain-name homelab.com

crypto key generate rsa
 1024

end
write

Here's the SSH configuration process for Arista R3 and R4.

username arista privilege 15 secret arista

dns domain homelab.com

management ssh

Next, SSH into the routers from your Ansible control node to ensure the router's SSH configuration is correct. This process also adds the router's SSH keys locally to your control node's known hosts file.

ssh cisco@R1
ssh cisco@R2


ssh arista@R3
ssh arista@R4

Now, you can reference the SSH credential and router hostname in your inventory file.

Create a custom inventory file

Although Ansible has a default inventory file situated at /etc/ansible/hosts, you can create a custom one in another directory and reference it when you execute it.

Below is a custom inventory file containing instructions on how to connect to your devices. Remember to manually configure SSH on your devices first. You can find more inventory connection options here.

The inventory.yaml file defines and organizes the hosts and groups managed by Ansible, as seen here.

local:
 hosts:
 R1:
 ansible_connection: network_cli
 ansible_host: R1
 ansible_user: cisco
 ansible_password: cisco
 ansible_network_os: cisco.ios.ios
 ansible_become: true
 ansible_become_method: enable
 R2:
 ansible_connection: network_cli
 ansible_host: R2
 ansible_user: cisco
 ansible_password: cisco
 ansible_network_os: cisco.ios.ios
 ansible_become: true
 ansible_become_method: enable
 R3:
 ansible_connection: network_cli
 ansible_host: R3
 ansible_user: arista
 ansible_password: arista
 ansible_network_os: arista.eos.eos
 ansible_become: true
 ansible_become_method: enable
 R4:
 ansible_connection: network_cli
 ansible_host: R4
 ansible_user: arista
 ansible_password: arista
 ansible_network_os: arista.eos.eos
 ansible_become: true
 ansible_become_method: enable

How to read a vendor's Ansible documentation

As you implement network automation with Ansible, it's important to specify parameters when attempting to define a playbook to configure a vendor's device. However, it can be tricky to understand the data types of the parameters. Let's focus on the following three parameter data types:

  1. dictionary.
  2. list / elements=dictionary.
  3. list / elements=string.

1. dictionary

This data structure stores key-value pairs and is commonly used for complex configurations to ensure a structured, readable format.

key_1: value_1
key_2: value_2

2. list / elements=dictionary

This is a list of dictionaries. An index of the list can contain a dictionary with multiple key-value pairs.

list_of_dictionaries:
 - key_1: value_1
 key_2: value_2
 - key_3: value_3
 key_4: value_4

This example has a list with two elements, each marked by a hyphen. In other words, each hyphen represents an index of a list. This structure is frequently used in module parameters, variable definitions and loop constructs.

3. list / elements=string

This notation represents a list where each element contains string values. Each index in the list below has one string.

list_of_strings:
 - "value_1"
 - "value_2"

This structure is simpler than list / elements=dictionary, which contains nested structures.

Install a collection

Now, let's get practical and install the Cisco or Arista collection. A collection is a module that abstracts the command-line syntax of an action, such as viewing interfaces or configuring an interface.

ansible-galaxy collection install cisco.ios
ansible-galaxy collection install arista.eos

Configure OSPF on the network devices

Next, define the configurations in a playbook. One playbook can contain many tasks, such as these examples:

In your playbook, start by defining the host to which you will connect, the task you'd like to perform and the module configuration you plan to use.

Cisco and Arista use ios_ospfv2 and os_ospfv2, respectively, to configure OSPFv2.

Open the Modules page of the device vendor's documentation, and go to the Examples section to learn how the module is used and what it does.

Configure OSPF on R1 and R2 using parameters in the Modules documentation.

When you open Cisco or Arista's OSPFv2 documentation, the first parameter is config, which has a dictionary data type. You expect a single key-value pair, key1: value1, just like described previously.

The second parameter is processes. You are already familiar with configuring OSPF on Cisco or Arista devices via a CLI, so you know you need to include an OSPF process. The process parameter has a data type of list / elements=dictionary, which means you need to define one list that might have multiple indexes with several key-value pairs each.

Once you start reading the documentation this way, you can define your Ansible configuration in no time.

Configure OSPF R1 and R2 (Cisco)

Here, configure OSPF on the Cisco R1 and R2 routers, using the cisco-ospf.yaml playbook.

Configure OSPF R3 and R4 (Arista)

Here, configure OSPF on the Arista R3 and R4 routers, using the arista-ospf.yaml playbook.

In Ansible, you need to define an output task if you want to see the result of an applied configuration. You can achieve this using the register variable, as used in the previous playbook. The variable captures the output and displays it with the built-in Ansible debug module.

How to run Ansible playbooks

To run a playbook in Ansible, use the ansible-playbook command. The command also lets you use the -i flag to define the custom inventory you would like to use.

Here's how to run thecisco-ospf playbook.

ansible-playbook cisco-ospf.yaml -i inventory.yaml

Here's the output from the Cisco R1 task.

TASK [advertise R1 network to ospf] ********************************************
changed: [R1]
TASK [show the r1_ospf output] **************************************************************
ok: [R1] => {
 "r1_ospf": {
 "after": {
 "processes": [
 {
 "network": [
 {
 "address": "192.168.122.0",
 "area": "0",
 "wildcard_bits": "0.0.0.255"
 }
 ],
 "process_id": 1
 }
 ]
 },
 "before": {},
 "changed": true,
 "commands": [
 "router ospf 1",
 "network 192.168.122.0 0.0.0.255 area 0"
 ],
 "failed": false
 }
}

Here's how to run the arista-ospf playbook.

ansible-playbook arista-ospf.yaml -i inventory.yaml

And here's the output from Arista R3's task.

TASK [advertise R3 network to ospf] ****************************************************
changed: [R3]

TASK [show the r3_ospf output] **********************************************************************
ok: [R3] => {
 "r3_ospf": {
 "after": {
 "processes": [
 {
 "max_lsa": {
 "count": 12000
 },
 "networks": [
 {
 "area": "0.0.0.0",
 "prefix": "192.168.122.0/24"
 }
 ],
 "process_id": 1
 }
 ]
 },
 "before": [],
 "changed": true,
 "commands": [
 "router ospf 1",
 "network 192.168.122.0 0.0.0.255 area 0",
 "exit"
 ],
 "failed": false
 }
}

These outputs mean OSPF has been configured successfully. For brevity, this article only includes one output each from each vendor.

Notice the commands part in each output.

"commands": [
 "router ospf 1",
 "network 192.168.122.0 0.0.0.255 area 0",
 "exit"
],

Although the CLI command wasn't specified in the playbook, the Ansible module understood the request and translated it into the CLI command.

How to troubleshoot with Ansible

This section presents many troubleshooting errors that can happen with Ansible and ways to remedy them. These installation commands were executed on a Debian-based Linux distribution. For non-Debian users, a quick search on how to install applications might be helpful.

Ansible can't successfully SSH into the device

Install ansible-pylibssh to provide an alternative SSH implementation and resolve connection issues.

sudo apt install python3-pip -y
pip3 install ansible-pylibssh --break-system-packages

Additionally, check SSH key permissions, verify that manual SSH connections work and try the -vvv flag to increase the level of output during execution, such as the following:

Ansible is rejecting my SSH password

Install the sshpass tool using the following.

sudo apt install sshpass -y

This workaround is useful when working with systems that don't support key-based authentication, in fully automated deployments without interactive password prompts and in events where correct credentials are provided.

This method does come with security risks, however, including password exposure and insecure storage of passwords.

Ansible is rejecting the Cisco and Ansible collection I used when I execute a playbook

In this case, upgrade your Ansible version to the latest one. The Cisco and Ansible collection requires Ansible version ≥ 2.16.0.

Next steps with Ansible

Network administrators can tap into advanced features as well. Roles and asynchronous actions are examples:

Learning a new tool can be difficult, but network administrators can check out Ansible's community forum to communicate with fellow Ansible users. The forum, along with other public online communities, includes a wealth of knowledge from network engineers skilled in Ansible to help beginners get started.

Charles Uneze is a technical writer who specializes in cloud-native networking, Kubernetes and open source.