MFD:ifTable:Data_Access - fenner/net-snmp-wiki-experiment GitHub Wiki
Now that we have the basic code generated, the next step is to implement the data access functions. The data access code is responsible for reading the available data and determining the valid indexes. For each data item, or row, a new row request structure is created. The mib context, contained in the row request, is initialized with the appropriate index data for the row, and then the row is inserted into the table container. The module can also initialize the data context at this time, or delay that processing for later. (see When should I initialize the data context?)
In the Linux kernel, interface statistics are available by reading the file /proc/net/dev. The format looks like this:
|
(If you want to try this tutorial on a non-Linux system, just copy the above data to any file, and update the code to use a static copy of the file. Note that some ioctl calls are made for some data that isn't found in /proc/net/dev. If your OS doesn't support the ioctl calls that are used, comment them out or replace them with code to get the data for your OS.)
As is often the case with external data, the interfaces data we get from the kernel is not sorted by the index specified by the MIB. The kernel reports data by interface names ('eth0', 'lo', etc), and the ifTable is ordered by the arbitrary ifIndex.
To keep this tutorial simple, we aren't going to implment every column in the ifTable. In particular, the ifAdminStatus, ifOperStatus and ifLastChange columns will not be implemented. This also lets us ignore set support for the time being. We'll cover dealing with these columns later on.
The default data access method used by MFD is the container-cached method. Basically this means that a netsnmp_container will be used to cache the data for our table. (If you don't want caching, don't worry. You can configure the cache to be rebuilt for every request.)
There are two initialization routines called for container-cached. One is the data init routine that is common to all MFD code. The other is specificly to init the container and cache.
The data init routine is called at startup, and any necessary data related setup can be done here. In this example, we are going to set up a netsnmp_column_info structure to let the agent know which columns we will be implementing. This will allow the agent to skip these columns.
The format of the data file we are using changed between the 2.2 and 2.4 versions of the kernel. We need to know which format the current system is using, so we call a routine that will take a peek at the data file to determine the format of the file.
Modified code |
|
|
The container init routine is called right after the data init routine. We are going to leave the default code as-is, to have a container allocated for us. (The timeout could be increased, depending on your tolerance for stale data. Set it to -1 if you want to rebuild the cache for every request.) We are going to to set one extra flag, to have the cache pre-load right away. This will allow the agent to discover all the index values at startup, which is necessar because other parts of the agent need to know what ifIndex values are valid.
Modified code |
|
|
The cache helper is used to load the indexes (and by default, the data) for each row in a table. In this tutorial, we will be using all of the default behavior, with the addition of the pre-loading (remember the flag we set during continer init?).
The default behaviour is to load the cache when a request is received for data from the table. If requests continue to come in, the cached data will be used until the time expires, at which point the cache will be unloaded and the re-loaded.
} |
When a cache has expired, the unload routine is called. The MFD interface code calls the cache free routine to allow your handler to do any necessary cleanup. Note that you should not actually remove items from the container or delete anything - that will be handled automaticaly by the container class.
|
In this simple example, we get most of our data from an external file, so simply flushing the cache meets our needs. In later tutorials, we'll see how to supress the cache unload/free and re-use existing data.
The agent does runs a periodic check (the default is every 60 seconds, I believe) for expired caches, and will call the unload routine for expired caches. See the documentation page on the cache helper for ways to modify this behavior, if you need to.
The row preparation function ifTable_row_prep is common to all data access methods. After the correct row request context is found for a request, this routine will be called for each row that is referenced in the incoming request. This gives you one last chance to update the context before the request is processed.
The primary purpose of the cache load routine (for MFD tables) is to set up the indexes. In this case, we have access to everything we need during cache loading, so we set up all our data there. If some data needed to be retrieved from another source, we could delay loading it until the row is needed.
Not, however, that while the cache load routine won't be called again until the cache expires, the row prep routine is called for each request. This is why we perform the ioctl to retrieve the physical address (ifPhysAddr) during the cache load. If the ioctl was for data that changed frequently, we could have chosen to update that data here. That is an option if you don't mind having a mix of cached data and fresh data.
Since we have already done the processing we need to do, so we'll just leave this function as is.
|