In this article, we’ll look at various Ansible modules that can be used to fetch information from Cisco IOS devices: ios_facts, snmp_facts and ios_command. Regardless of the used module, we’ll store the output in a JSON file that can easily be used in other tools.
ios_facts
ios_facts is the obvious choice. It connects to the target using SSH and gathers information with CLI commands. Here’s a sample playbook:
$ cat fetch_iosfacts.yml - hosts: ios connection: local vars: tasks: - name: ssh facts register: iosfacts_out ios_facts: provider: "{{ credentials }}" - copy: content="{{ iosfacts_out | to_nice_json }}" dest="out/{{inventory_hostname}}_iosfacts.json"
The resulting file is very similar to ordinary Ansible facts you’d get from any other non-IOS host:
{ "ansible_facts": { "ansible_net_all_ipv4_addresses": [ "192.168.43.19" ], "ansible_net_all_ipv6_addresses": [], "ansible_net_filesystems": [ "fstage:", "flash:", "system:", "tmpsys:", ], "ansible_net_hostname": "nealab19", "ansible_net_image": "flash:/c3750e-universalk9-mz.150-2.SE6/c3750e-universalk9-mz.150-2.SE6.bin", "ansible_net_interfaces": { "FastEthernet0": { "bandwidth": 100000, "description": null, "duplex": null, "ipv4": null, "lineprotocol": "down ", ...
snmp_facts
snmp_facts are very similar to ios_facts, but as the name suggests, obtained by walking various SNMP MIBs. There is generally a bit less information, but with the upside that this module will work for non-Cisco stuff and devices without SSH access.
The sample playbook looks very similar, but of course we need to supply the appropriate SNMP v2 or v3 parameters:
$ cat fetch_snmpfacts.yml - hosts: ios connection: local vars: tasks: - snmp_facts: host: "{{ credentials.host }}" version: "{{ credentials.snmpversion }}" community: "{{ credentials.snmpcommunity }}" register: snmpfacts_out - copy: content="{{ snmpfacts_out | to_nice_json }}" dest="out/{{inventory_hostname}}_snmpfacts.json"
The output is comparable as well. You get some additional information from the MIB-2 System MIB, at the cost of having the interfaces indexed by OID instead of their name.
{ "ansible_facts": { "ansible_all_ipv4_addresses": [ "192.168.43.19" ], "ansible_interfaces": { "14502": { "adminstatus": "up", "description": "", "ifindex": "14502", "mac": "f0f742abee40", "mtu": "1500", "name": "FastEthernet0", "operstatus": "down", "speed": "1500" }, ... }, "ansible_sysdescr": "Cisco IOS Software, C3750E Software... "ansible_syslocation": "", "ansible_sysname": "nealab19.netnea.com", "ansible_sysobjectid": "1.3.6.1.4.1.9.1.516", "ansible_sysuptime": "208066918" }...
ios_command
A big shortcoming of the previous modules is that the list of discovered attributes is not extendable. To collect custom data, you can resort to the ios_command module. However to get nicely formatted values in your JSON, some extraction work on the command output is required. In this sample playbook, we extract all SNMP trap destinations and format them nicely as array:
$ cat fetch_command.yml - hosts: ios connection: local vars: trapdests: [] tasks: - name: show snmp hosts register: command_out ios_command: commands: "show snmp host | include Notification host" provider: "{{ credentials }}" - name: extract values set_fact: trapdests: "{{trapdests + [item.split()[2]]}}" with_items: "{{command_out.stdout_lines}}" - copy: content="{{ trapdests | to_nice_json }}" dest="out/{{inventory_hostname}}_trapdests.json"
The set_fact block loops over all output lines, then extracts only the destination IP and appends it to the trapdests array. So from the raw command output
nealab19#show snmp host | include Notification host Notification host: 192.168.25.8 udp-port: 162 type: trap Notification host: 192.168.25.9 udp-port: 162 type: trap
we arrive at this JSON array:
[ "192.168.25.8", "192.168.25.9" ]
Running the sample playbooks
To run these samples yourself, you’ll need a few additional files the weren’t mentioned yet. First, we need an inventory defining the ios group and all devices we’d like to collect data from:
$ cat inventory [ios] nealab19 nealab23
Also we have used the credentials structure all over the place, which is centrally defined in our group vars:
$ cat group_vars/ios ansible_python_interpreter: /opt/ansible/pyenv/bin/python credentials: host: "{{ inventory_hostname }}" username: "demo" password: "demo" snmpversion: v2c snmpcommunity: demo
Of course storing passwords and communities in plain text is generally a bad idea. For production usage, we recommend using the vault. In case you’re using another python than /usr/bin/python to execute Ansible modules, you’ll need to set the ansible_python_interpreter as well.
Once all of this is in place, you should be able to run the playbooks like e.g.
$ ansible-playbook -i inventory fetch_command.yml
Please note that you need at least Ansible 2.2 to have all of the modules.
IOS-XR, NX-OS and others
For these IOS variants, you can find specialized versions of the ios_-modules like iosxr_facts, nxos_command and many more. For a full list of Ansible networking-related functionality, check out this page. Happy hacking!