Basic Network Automation with Ansible Galaxy IOS Collection

Introduction

One great resource you can use when learning and using Ansible for network automation, or any automation with Ansible for that matter, is Ansible Galaxy. Ansible Galaxy is a place for the Ansible community to share roles and collections for others to use. For this post I will highlight some of the roles found within the Cisco IOS collection.

Installation

For my setup, I am using Ansible 2.10.4 and will be connecting to a device running IOS-XE. The IOS collection page within Galaxy says it should work with Ansible versions >=2.9.10. The first thing you’ll have to do is install the IOS collection. The Galaxy commands come bundled with Ansible, so there is nothing you need to do in order to ‘enable’ the following commands. This will install the Cisco IOS collection and the netcommon collection, which provides support for common network tasks (see https://docs.ansible.com/ansible/latest/collections/ansible/netcommon/index.html for details):

ansible-galaxy collection install cisco.ios
ansible-galaxy collection install ansible.netcommon

The default install location for collections is ~/.ansible/collections. You can check out the contents within that directory and its sub-directories if you are curious. Once you have installed the collections, you can now use all the roles found on the Cisco IOS collection page linked previously. There are roles for configuring certain parameters, gathering facts, or even testing connectivity with ping. Lets start with something that won’t be disruptive: gathering facts.

The Inventory

Let’s make a quick playbook to gather some facts. First, we need an inventory file for the devices we want to run this playbook against. For this example, my inventory is simple, with a group called [ios] and a single IP for that group:

[ios]
192.168.122.2

Group Variables

I like to use a group_vars folder to manager my variables, rather than putting them directly into an inventory or even the playbook. In the same directory as my inventory (and later playbook) I created a directory called ‘group_vars’ and created a file named ‘ios’ for all variables pertaining to my ios group. While same may see it as unnecessary for such a small playbook, I like to get in the habit of breaking it out like this so it becomes second nature when I start working on bigger, ‘real’ playbooks. Here are the contents of that ios file:

ansible_become: yes
ansible_become_method: enable
ansible_network_os: cisco.ios.ios
ansible_user: otaku

If you have used ansible before most of these variables are nothing new, but I will go over them for those you don’t have as much experience. Ansible_become tells Ansible that elevated privileges are required. Ansible_become_method tells it what kind of privileges to use. Enable is the privilege escalation used for network devices. Ansible_network_os is how you can differentiate between different network operating systems. This is basically telling Ansible how to interact with the network device. If you have worked in multi-vendor environments, then you know there are a lot of similarities, but also tiny differences in some network operating systems. Ansible_user is the username Ansible will use to log in to the device.

The First Playbook – Gathering Facts

With that taken care of, we can create a playbook. You can browse through the Cisco IOS Collection mentioned earlier to get a feel for all the different commands we can use. For our first playbook, lets gather the hostname and IOS-XE version of the device. Here is a playbook for that:

---

- name: Basic Automation With Ansible Galaxy
  connection: network_cli
  gather_facts: true
  hosts: all
  tasks:

    - name: View hostname
      debug:
        var: ansible_net_hostname

    - name: View OS
      debug:
        var: ansible_net_version

Here are the important takeaways from the playbook. Connection is set to network_cli is what tells Ansible how it will connect to the device, with network_cli catering to networking devices. We set gather_facts to true because now we are specifically trying to gather facts about the device. This is what allows us later on to use debug that will print this information as we run the playbook. We use debug here to print out the specific information we want to see. When Ansible first connects and we set gather_facts to true, it will gather all of the variables listed in the Cisco IOS collections page. Normally you would only use debug to troubleshoot, but I use it here to show how different variables are pulled from the configuration. This can be helpful if you want to use a variable as a condition. Lets say you want to create a playbook for upgrading IOS versions. You can use this variable to only upgrade from a specific version to another. Now lets run this playbook.

Running Our Gathering Facts Playbook

With our inventory file and playbook in a directory, which also contains our group_vars directory within it, we can run the playbook with the following command:

ansible-playbook -k -i inventory main.yml

In the above command, inventory is the name of my inventory file and main.yml is the name of my playbook. They can be whatever you want them to be. I use the -k switch because I did not supply a password in my group variables. You can securely put passwords in with the Ansible Vault. Vault provides a way to encrypt variables, but I did not want to add too much to this post. I plan to make a future post showing how to use vault to add passwords to group_vars files, but you can search for Ansible Vault if you want. Here is the output I saw from running this command:

otaku@netlab blog-basic-galaxy % ansible-playbook -k -i inventory main.yml
SSH password: 

PLAY [Basic Automation With Ansible Galaxy] *****************

TASK [Gathering Facts] **************************************
[WARNING]: Ignoring timeout(10) for cisco.ios.ios_facts
[WARNING]: Platform linux on host 192.168.122.2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [192.168.122.2]

TASK [View hostname] ***************************************
ok: [192.168.122.2] => {
    "ansible_net_hostname": "R1"
}

TASK [View OS] *********************************************
ok: [192.168.122.2] => {
    "ansible_net_version": "16.12.03"
}

PLAY RECAP *************************************************
192.168.122.2              : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

An Actual Configuration Change

Now gathering facts is nice, and is something great to start automating (like generating reports), but you probably want to also dabble in configuration changes. Lets remove the View OS task and replace it with a task for changing the hostname. Then we can add another task to view the hostname so we can see that it got changed without having to actually log into the device and check. Here is our new playbook:

---

- name: Basic Automation With Ansible Galaxy
  connection: network_cli
  gather_facts: true
  hosts: all
  tasks:

    - name: View hostname
      debug:
        var: ansible_net_hostname

    - name: Change hostname
      ios_system:
        hostname: otaku_R1

    - name: View changed hostname
      debug:
        var: ansible_net_hostname

Changing the hostname falls under the cisco.ios.ios_system module, and you simply have to type hostname: followed by what you want to changed the hostname to. Lets run the playbook again and see how it goes:

otaku@netlab blog-basic-galaxy % ansible-playbook -k -i inventory main.yml
SSH password: 

PLAY [Basic Automation With Ansible Galaxy] ****************

TASK [Gathering Facts] *************************************
[WARNING]: Ignoring timeout(10) for cisco.ios.ios_facts
[WARNING]: Platform linux on host 192.168.122.2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [192.168.122.2]

TASK [View hostname] ***************************************
ok: [192.168.122.2] => {
    "ansible_net_hostname": "R1"
}

TASK [Change hostname] *************************************
changed: [192.168.122.2]

TASK [View changed hostname] *******************************
ok: [192.168.122.2] => {
    "ansible_net_hostname": "R1"
}

PLAY RECAP *************************************************
192.168.122.2              : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Oops! So the change hostname task appears to have worked, but the hostname still shows R1. Any ideas why? I overlooked this when doing this in the lab, but left it in to show that we all make mistakes, and also to show an important concept. Remember earlier how I said that the gather_facts variable being set to yes would tell Ansible to gather ALL the facts when it first connects to the device. Well that means if we debug any variable, no matter how many times it has been changed since Ansible first connected, Ansible will print the original ‘fact’ from when it first connected. To correct this, we can tell Ansible to refresh our network facts. We could do something like run a specific show command, register that as a variable, and then display it, but that isn’t going to scale very well. I would reserve that if you found a variable you wanted that isn’t part of a ios_fact already defined. So lets add the following task between our Change hostname and View changed hostname tasks:

    - name: Refresh ios_facts
      ios_facts:
        gather_subset: all

Note that if you are following along, to see the change like I do below, you will have to SSH/connect to the device and change the hostname back to what you originally had it because the change did work when we ran the last playbook and just had the ios_facts problem.

otaku@netlab blog-basic-galaxy % ansible-playbook -k -i inventory main.yml
SSH password: 

PLAY [Basic Automation With Ansible Galaxy] *****************

TASK [Gathering Facts] **************************************
[WARNING]: Ignoring timeout(10) for cisco.ios.ios_facts
[WARNING]: Platform linux on host 192.168.122.2 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible/2.10/reference_appendices/interpreter_discovery.html for more information.
ok: [192.168.122.2]

TASK [View hostname] ****************************************
ok: [192.168.122.2] => {
    "ansible_net_hostname": "R1"
}

TASK [Change hostname] **************************************
changed: [192.168.122.2]

TASK [Refresh ios_facts] ************************************
ok: [192.168.122.2]

TASK [View changed hostname] ********************************
ok: [192.168.122.2] => {
    "ansible_net_hostname": "otaku_R1"
}

PLAY RECAP **************************************************
192.168.122.2              : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

Success! For the Updated ios_facts task, we used the gather_subset: all which will refresh all of the facts. If you know what specific section that fact falls under, you can just specify that subset. If you browse through the cisco.ios.ios_facts portion of the Cisco IOS collection page, you will see examples of what different subsets you can pull. This may not save that much time if you have a great connection or if you are only doing a few devices, but that time could add up with many devices and some latency to some devices.

Summary

Although barely scratching the surface of the Cisco IOS collection, I hope this has shown how easy automation can be. You don’t have to swing for the fences and roll out some kind of zero touch provisioning for your entire enterprise on day one. You can start with simple tasks like gathering facts and making small changes. I plan to make another post to go a little deeper into reports, as that is your best way into automation. Get familiar with Ansible, pull facts (there are a ton to pull), and find ways to get them into reports. See what you can come up with and we’ll see how it compares. In the meantime, read through the Cisco IOS collection and start trying out your own playbooks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s