Getting Started With Ansible for Cisco IOS

Ansible is well-known for it’s low entry threshold. All what’s required to get started is just one inventory file. However Cisco IOS devices require special considerations.
Passwordless SSH RSA-based authentication is still a novelty and in most cases users are authenticated based on their passwords. Another problem is the lack of Python execution environment on IOS devices, which seriously limits the choice of Ansible modules that can be used. In this post I will show how to setup Ansible
environment to control Cisco IOS devices

Ansible overview

There’s been a lot written about what Ansible is and what it was built to accomplish. I will just provide a brief summary of its features focusing on what we’re gonna be using it for, leaving an in-depth explanation to the official Ansible documentation.

What is it?

Ansible is an IT automation and orchestration framework

What was it built to accomplish?

Ansible was designed to automate routine tasks like server/application deployment and configuration

How does it work?

It connects to several hosts at the same time and executes small programs called “modules” in the order specified in a file called “playbook”

To build what we’ve set out to accomplish I’m gonna be using the latter feature. I am not gonna be using Ansible for system provisioning or service orchestration. Instead, I will be exploiting Ansible’s ability to run multiple parallel connections to remote hosts, execute commands on them and return their result. Due to that, I will diverge from some of the Ansible’s best practices of splitting functions into roles and I will use one flat playbook file segregating different functions with tags.

Ansible configuration file

Ansible configuration file ansible.cfg contains application-wide settings like default timeouts, port numbers and other flags. The default Ansible configuration file is located in /etc/ansible/ directory. However, instead of overwriting the defaults it is possible to create a configuration file in a local directory (e.g. ~/tdd_ansible/ansible.cfg) with only the settings that need to be overridden. To better work with Cisco devices the following settings will need to be modified:

Default SSH library (transport) needs to be set to paramiko which is more stable than its alternative, OpenSSH, when working with Cisco IOS.

For a small project it is easier to maintain a local copy of inventory file which is configured with hostfile setting.

Strict SSH key checking is a MUST in every production environment, however, for development environment an exception can be made.

Default SSH timeout is decreased to 5 seconds reflecting a small size of the testing environment.

Inventory file

Inventory ~/tdd_ansible/myhosts contains the list of hosts to be managed by Ansible. Hosts are normally combined into groups (cisco-devices in our case) and Ansible performs actions on all hosts in the group in parallel.

[cisco-devices]
R1
R2
R3
R4

It is considered a best practice to keep all variables in separate folders and files. We need to define additional host variables to let Ansible know which IP address to use to connect to a remote device. I will also add SSH password to a host variable file which is a VERY bad practice, however this will prevent me from typing password every time I run a playbook. If I ever did this in production, I’d add host variables directory to .gitignore file so that it doesn’t get uploaded to Github. Host variables files must follow YAML formatting, must be stored in a ./host_vars directory and must match the name of the host they are being assigned to. Below example is ~/tdd_ansible/host_vars/R1 for host R1:

+++
ansible_ssh_host: 10.0.0.1
ansible_ssh_pass: cisco

Similar files need to be created for R2, R3 and R4.

Run a test traceroute commands

Now it is time to finally see Ansible in action. Let’s first see if we can run a standalone traceroute command. I will manually define SSH username with -u flag and use a module called raw passing traceroute command as an argument with -a option.

Ansible ad-hoc commands are a good way to quickly test something out and learn how things work. Next step would be to create a playbook file which will contain several of those commands in a more structured way. Playbooks use YAML syntax and follow strict formatting rules. At the top of the file there’s a name of the play along with the target hosts group. Following that are a list of tasks, each of which calls its own module and passes arguments to it. In this example playbook does the following:

Defines a loopbacks variable which stores in a hash a list of devices along with their loopback IP addresses.

Uses raw module to run traceroute commands. This is the only module that doesn’t require Python to be installed on a target machine.

For each host in cisco-devices group runs traceroute to every other hosts’ loopback IP

The above shows that all 12 tasks were completed successfully, meaning the command was executed and result was stored in a registered variable. To view the actual output of traceroute commands uncomment the two debug lines at the end of the playbook and rerun it.

Now that the goal of running commands on multiple devices in parallel is achieved, the next step would be to decide how to make use of the received output. In the next posts I will attempt to tackle the following problems: