Ansible and IOS quick start
Ansible has been around for I while but I didn't had a chance to play with it so far.
Now the time has come: I manage enough IOS devices with homogeneous configurations in multiple sites without Cisco Prime. Any change is a pain, it's time to automate all the things!
My environment
I run Ansible inside Bash on Windows, I don't see any issue or difference than running in an actual Linux box or docker/vagrant/whatever and it permits a better integration with the tools I already use.
I like try new editors, I switch from Notepad++ to Atom to nano, now I'm testing Visual Studio Code from Microsoft, it has some nice features:
- free
- multi-platform (Windows, Linux, Mac)
- extensions - use this for Ansible
Install Ansible
Check if Ansible is already installed with:
ansible --version
Today latest stable release is
ansible 2.3.0.0
If an older version is installed upgrade with
pip install ansible --upgrade
Or install it
apt-add-repository ppa:ansible/ansible
apt-get update
apt-get install ansible
Older version may cause some errors with the example I'll show below.
First example: update configuration on IOS devices
The first example is a very simple change: check if a command is in running configuration, if it's not apply the command and save the configuration.
First thing: create a file named /etc/ansible/secrets.yaml to store the credentials to access the devices. There're better ways do this but for a quick start that's good enough:
username: myUserName
password: mySup3rS3cr3tPa55w0rd
Then create/edit file /etc/ansible/hoststest with a list of host to test the scripts:
[ios]
192.0.2.1
192.0.2.2
Note: I used RFC5737 addresses, ain't that cool? ;-)
Now it's time for the actual script, well put it in file /etc/ansible/playbook/makechange.yaml.
Comments are inline (starting with #), you can copy&paste and make the changes you need.
In my example I'll check if an SNMP community is present in running config.
---
- connection: local
gather_facts: no
hosts: ios
tasks:
- name: SET FACTS
set_fact:
snmp_community: "snmp-server community MYCOMM RW"
# command output that should be present in running config
- name: GET CREDENTIALS
# read credentials from file
include_vars: secrets.yaml
- name: DEFINE PROVIDER
# set some variables
set_fact:
provider:
host: "{{ inventory_hostname }}"
username: "{{ creds['username'] }}"
password: "{{ creds['password'] }}"
- name: GET COMMUNITY
# variable snmp_config will store the output ot the command below
register: snmp_config
ios_command:
provider: "{{ provider }}"
commands:
# this is the command that will be run on remote hosts
- "sh run | i snmp-server community MYCOMM RW"
- name: SET COMMUNITY
# if the community is not in running configuration...
when: ("snmp_community != snmp_config.stdout_lines[0]")
# ...set a variable with name "changed"...
register: changed
# ...and run the following command
ios_config:
provider: "{{ provider }}"
lines:
- snmp-server community MYCOMM RW
- name: SAVE CONFIG
# if variable "changed" exists and it's value is "true", meaning a configuration change was made...
when: "(changed is defined) and (changed == true)" # ONLY SAVE IF CONFIG HAS CHANGED
# ...run the following command to save the configuration
ios_command:
provider: "{{ provider }}"
commands:
- "write memory"
Now we can run the playbook with
ansible-playbook -i /etc/ansible/hoststest /etc/ansible/playbook/makechange.yaml
and see the magic happen!
Second example: get a command output
Scenario: I manage a FlexVPN network with dual-hub and over 100 spokes. While I wait for a proper monitoring to be in place I need a quick way to check if all the spokes have two tunnels up.
This playbook runs a command on each hub and saves the output in a local file. Comments inline.
Host inventory: /etc/ansible/inventory/flexhubs
Results saved in: /etc/ansible/results
Playbook name: /etc/ansible/playbook/flexspokelist.yaml
---
- connection: local
gather_facts: no
hosts: ios
tasks:
- name: GET CREDENTIALS
include_vars: secrets.yaml
- name: DEFINE PROVIDER
set_fact:
provider:
host: "{{ inventory_hostname }}"
username: "{{ creds['username'] }}"
password: "{{ creds['password'] }}"
- name: SHOW SPOKES
register: config
# run command on HUB
ios_command:
provider: "{{ provider }}"
commands:
- "sh ip bgp | i 172.23.11"
- name: append to output
# append the command output to a local file
copy:
content: "{{ config.stdout[0] }}"
dest: "/etc/ansible/results/{{ inventory_hostname }}.txt"
Run the playbook
ansible-playbook -i ./inventory/flexhubs ./playbook/flexspokelist.yaml
The output actually needs some manipulation to get the information I need:
awk '{ print $2}' ./results/flexhub1.txt | sed 's/\/32//g' | more | sort -V > ./results/flexhub1list.txt
awk '{ print $2}' ./results/flexhub2.txt | sed 's/\/32//g' | more | sort -V > ./results/flexhub2list.txt
diff -y ./results/flexhub1list.txt ./results/flexhub2list.txt | grep '<\|>'
Done! Now we have the list of spokes that have just one tunnel with bgp peering up.
Wrap up
Automation is not easy, it is something that requires some effort to start.
Today many network engineers have a big WORKLOAD that is managed by hand, requires lots of skills, is error-prone and does not scale.
The next step should be to translate the workload to a RUNBOOK that is versionable, repeatable and that could be delegated to less skilled or junior engineers.
A further step would be to AUTOMATE the runbook, this is where Ansible really helps.
The last step would be ORCHESTRATION a.k.a. running automated tasks when some conditions arise - that is out of scope for this post.
I'll share this and future playbooks in my GITHUB repository.
You can find more complex examples in the blog of my friend Andrea.
I hope you enjoyed this post and will use is as a starting point for more automation.