The problem with making changes to any decent-sized network, which is running a routing protocol such as OSPF, is that in order to fully verify the change you will need to log into every device in the network and verify that your change has worked. This post shows how Ansible can be used to perform basic tests on Cisco IOS devices.
In this short example, I’ll start off with a basic lab network consisting of 4 routers running IOS and a Linux host with Ansible installed. I haven’t shown how Ansible is configured to log into each router – there’s plenty of examples on the net showing how to do that.
Before starting, manually check that the Ansible host can ssh into each IOS device by running a simple test command using the raw module from Ansible to verify that it can log into each device:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
[kerry@zaphod ansible]$ ansible all -m raw -a "show users" r4 | SUCCESS | rc=0 >> Line User Host(s) Idle Location * 2 vty 0 ansible idle 00:00:00 zaphod.crypt.gen.nz Interface User Mode Idle Peer Address r1 | SUCCESS | rc=0 >> Line User Host(s) Idle Location * 2 vty 0 ansible idle 00:00:00 zaphod.crypt.gen.nz Interface User Mode Idle Peer Address r5 | SUCCESS | rc=0 >> Line User Host(s) Idle Location * 2 vty 0 ansible idle 00:00:00 zaphod.crypt.gen.nz Interface User Mode Idle Peer Address r2 | SUCCESS | rc=0 >> Line User Host(s) Idle Location * 2 vty 0 ansible idle 00:00:00 zaphod.crypt.gen.nz Interface User Mode Idle Peer Address r3 | SUCCESS | rc=0 >> Line User Host(s) Idle Location * 2 vty 0 ansible idle 00:00:00 zaphod.crypt.gen.nz Interface User Mode Idle Peer Address [kerry@zaphod ansible]$ |
Now create the Ansible playbook to perform the tests required. In the playbook below, I will perform the following tests on the routers belonging to the group “labrouters”:
- Test that each target router can ping it’s local gateway, 172.16.1.1
- Verify that there is a route to 192.168.1.0/24 in the routing table
- Ping the host address 192.168.1.5
- Verify that the default 0.0.0.0/0 exists in the routing table
- Ping the Google DNS server at 8.8.8.8
Here is the example playbook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
--- # # Ansible playbook to run a set of ping tests from the lab routers # to a bunch of destinations. # - name: Ping rtr1 from lab routers hosts: labrouters gather_facts: false remote_user: ansible connection: local tasks: - name: "Define provider" set_fact: provider: host: "{{ inventory_hostname }}" username: "{{ ansible_ssh_user }}" password: "{{ ansible_ssh_pass }}" # Ping gateway using ios_command - name: Run ping to R1 using ios_command register: ping_result ios_command: provider: "{{ provider }}" commands: - "ping 172.16.1.1 repeat 3 timeout 1" - name: Check ping result fail: msg="Ping failed {{ ping_result.stdout }}" when: "'100 percent' not in ping_result.stdout[0]" - name: Read routing table register: ip_route ios_command: provider: "{{ provider }}" commands: - "show ip route" - name: Test for 192.168.1.0 route fail: msg="192.168.1.0/24 is not in the routing table" when: "'192.168.1.0/24' not in ip_route.stdout[0]" - name: Run ping to rtr1 register: ping_result ios_command: provider: "{{ provider }}" commands: - "ping 192.168.1.1 repeat 3 timeout 1" - name: Check ping result fail: msg="Ping failed {{ ping_result.stdout }}" when: "'100 percent' not in ping_result.stdout[0]" - name: Test for default route fail: msg="0.0.0.0/0 is not in the routing table" when: "'0.0.0.0/0' not in ip_route.stdout[0]" # # Ping and check result all in one go # - name: Run ping to Google register: ping_result failed_when: "'100 percent' not in ping_result.stdout[0]" ios_command: provider: "{{ provider }}" commands: - "ping 8.8.8.8 repeat 3 timeout 1" |
Running the playbook is just a simple command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
[kerry@zaphod ansible]$ ansible-playbook ping_tests.yml PLAY [Ping rtr1 from lab routers] ********************************************************************** TASK [Define provider] ********************************************************************************* ok: [r2] ok: [r3] ok: [r4] ok: [r5] TASK [Run ping to R1 using ios_command] **************************************************************** ok: [r2] ok: [r4] ok: [r5] ok: [r3] TASK [Check ping result] ******************************************************************************* skipping: [r2] skipping: [r3] skipping: [r4] skipping: [r5] TASK [Read routing table] ****************************************************************************** ok: [r5] ok: [r3] ok: [r2] ok: [r4] TASK [Test for 192.168.1.0 route] ********************************************************************** skipping: [r2] skipping: [r3] skipping: [r4] skipping: [r5] TASK [Run ping to rtr1] ******************************************************************************** ok: [r2] ok: [r3] ok: [r5] ok: [r4] TASK [Check ping result] ******************************************************************************* skipping: [r2] skipping: [r3] skipping: [r4] skipping: [r5] TASK [Test for default route] ************************************************************************** skipping: [r2] skipping: [r3] skipping: [r4] skipping: [r5] TASK [Run ping to Google] ****************************************************************************** ok: [r5] ok: [r2] ok: [r4] ok: [r3] PLAY RECAP ********************************************************************************************* r2 : ok=5 changed=0 unreachable=0 failed=0 r3 : ok=5 changed=0 unreachable=0 failed=0 r4 : ok=5 changed=0 unreachable=0 failed=0 r5 : ok=5 changed=0 unreachable=0 failed=0 [kerry@zaphod ansible]$ |
Ansible runs all of the checks, against all of the routers, in parallel and reports the results in a section for each test. If a test fails, then Ansible will report the failure, and stop running tests. As a test, I’ve removed the default route from r4 and re-run the playbook against r4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
[kerry@zaphod ansible]$ ansible-playbook -l r4 ping_tests.yml PLAY [Ping rtr1 from lab routers] ********************************************************************** TASK [Define provider] ********************************************************************************* ok: [r4] TASK [Run ping to R1 using ios_command] **************************************************************** ok: [r4] TASK [Check ping result] ******************************************************************************* skipping: [r4] TASK [Read routing table] ****************************************************************************** ok: [r4] TASK [Test for 192.168.1.0 route] ********************************************************************** skipping: [r4] TASK [Run ping to rtr1] ******************************************************************************** ok: [r4] TASK [Check ping result] ******************************************************************************* skipping: [r4] TASK [Test for default route] ************************************************************************** fatal: [r4]: FAILED! => {"changed": false, "failed": true, "msg": "0.0.0.0/0 is not in the routing table"} to retry, use: --limit @/home/kerry/router_labs/ansible/ping_tests.retry PLAY RECAP ********************************************************************************************* r4 : ok=4 changed=0 unreachable=0 failed=1 [kerry@zaphod ansible]$ |
The message from the failed test is a bit cryptic, but you get the idea.
From this quick example you should be able to see how Ansible can be used to run a multitude of tests against a number of network devices in a very scalable manner. These kind of tests are invaluable in a large network when complex changes are being made to a distributed control plane where a small change to a single device can affect the whole network. The tests are typically run both before and after changes are made to verify that the network is operating properly in both cases.
Additional playbooks can be created to test almost any network function that you can think of. It is certainly much easier to get Ansible to do the tasks in parallel than to manually log into each device, run the commands, and check the results by hand.