At times when I look at the tools available for server admins today I long for the times when I didn’t work in networking. Sure we can use tools like Puppet and Ansible for networking too. However the tools are made for servers. Of course there are tie-ins into network automation, but the functionality is rudimentary at best. The current problem as I see it is the lack of decent APIs, granted some vendors are better than others. And I haven’t had the pleasure of working with those who understand XML. Sometime in a not too distant future when we have flying skateboards, SDN and nano bots these problems will disappear, but we’re not there yet. Before I take a deep dive to see what’s actually possible to do with onePK, OpenDaylight and all that good stuff I wanted to see how much is possible to do today. So this post is about Ansible which is really simple to learn and SNMP, where one of the words in the acronym is “simple”. It should be a perfect match, right?
The last few months I’ve tested Puppet and Ansible, both of the systems are great. A big difference is that Puppet is client / server based, while Ansible works in a standalone way. Basically what this means is that the node being managed by Ansible isn’t aware of that fact, but a Puppet client will have a Puppet agent installed. In my case I wanted to see what could be done with SNMP. For most Ansible modules a connection to the remote device will be done over ssh, then Ansible will upload Python scripts to the node which is to be managed. These scripts are then run locally on the remote machine and the result is then reported back to the initiator. Since most network devices doesn’t have Python installed this presents a bit of a problem. The workaround is to run the connection in local mode, that way Ansible won’t try to upload a script to the remove server. Instead the module you want to run will be executed on the local machine. I don’t know if you would be able to do this with Puppet, but I don’t see how it would fit into how Puppet works. So that’s why I created a module for Ansible instead.
This post doesn’t really describe Ansible so if you haven’t done so it could be a good idea to learn some Ansible basics before you read on.
While my ASA or switches doesn’t have Python installed they have support for SNMP. Even my 10 year old printer has support for SNMP and with this module I’ve been able to collect Ansible facts from that printer. While the market to use Ansible with printers probably isn’t just about to take off, the point is that all network devices has support for SNMP. With this module you can gather facts from all of them.
Below are two examples of how you could setup the tasks in a playbook.
# Gather facts with SNMP version 2<br />
This module requires pysnmp to be installed on your system. In order to use the module you can add its directory to your $ANSIBLE_LIBRARY variable, or create a subdirectory for the module in the current library directory. Typically this is /usr/share/ansible.
You might be thinking; “But this doesn’t actually do anything!?” And that is correct, this module is a collection module and will only gather information from your network devices. Other modules will be needed to make use of the information collected with snmp_facts. For example you could use the facts in playbooks together with the work Jason Edelman has been doing (I’m looking forward to when I will have time to look closer at the code behind that). Another option would be to use the information together with the Ansible templating system in order to build or modify config files.
If you use the verbose option (-v) with ansible / ansible-playbook you will see what is collected from each host.
There is an example Playbook included in the snmp_facts download which you can take a look at in order to see how you can extract this information.
In this first version of the module I collect:
There are a lot of things which can be collected to Ansible facts with SNMP. To name a few things we have neighbor information with CDP or LLDP, routing tables, NAT setup, OSPF neighbors, IPv6 addresses, mac address tables, VRF information and a lot more. Since the sysobjectid is unique for each device type a future option could be to first run a collection just to get basic information, then in a playbook pass the sysobjectid to a Jinja template through a lookup function in order to get back the device class. That way it would be possible to run other collections but only against certain devices. For example if you want to use snmp_facts against all your devices to collect the routing table you can do that with SNMP. The problem here would be that some devices keep this information in IP-FORWARD-MIB while others have it in RFC1213-MIB. So a module option called route_table could be created with took the arguments route_table=IP-FORWARD-MIB or route_table=RFC1213-MIB. Here you could manually group your devices in two groups and have two tasks. Or you can first run a collection task to get the sysobjectid, do a lookup to get the device class. That device class would then be registered to a variable which you could dynamically use in the following task.
At the moment I just wanted to see what could be done and I won’t do much more until I have some sort of feedback. Either in the comments below or through email.
Specifically I would like some feedback on how to present ip addresses and their correlation to interfaces. Looking at a typical Linux box an address will be configured on a eth0 interface. However on a router an interface could be named as GigabitEthernet0/6. I don’t like the idea of using this name as a key as it has a slash character in the name. The other issue is that the interface can have multiple ip addresses if someone has configured secondary addresses. In SNMP all of the interfaces have a unique index (ifIndex), also the ip addresses have unique indexes. We could use these indexes as the key names, and this is what I do now with the physical interfaces. A downside to this is that these numbers aren’t really relevant outside the world of SNMP. Is it a bad idea to present addresses and interfaces in this way? Do you have any better suggestions? Other general feature requests are also welcome.
Because I’m not sure about the best way to present this the current structure might change. Also some of the return values such as the system description will be in hex format for some devices. I will write a function to convert these to a readable string.
Though there are a lot of things which could be done with SNMP. I don’t currently see it as a practical way to actually modify the device configurations. Sure SNMP is fully capable to change configurations on routers and switches. However only some parts can be changed and it seems cumbersome to create such a module which would be idempotent. The module can of course poll the device first to see what is already there, but I don’t like the idea of making changes this way. Another reason not to do it is I think it could be a lot of work just to setup a playbook in order to make changes with SNMP. I might reconsider this in the future but I think I will try to use other APIs when I want to actually change the configurations.
Update: Well I changed my mind and started develop Ansible modules which can change configuration on Cisco devices.
The snmp_facts module is freely available over at Github.