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:

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.

 
comments powered by Disqus