Architecture - Persistency

Persistency subsystem in the FriendlySNMP completely overrides underlying SNMP4J library persistency.

SNMP4J persistency limitations
The SNMP4J agent preserves its state in a binary file using serialization mechanism. In terms of the SNMP4J this file is a configuration file. This file is persistent storage of all registered managed object states. At startup the SNMP agent loads the configuration file and restores all managed object states. SNMP4J provides update and replace options to load configuration. Refer to SNMP4J documentation for details.

Limitations of the SNMP4J persistency design are the following:

  • Option to load configuration in update or replace mode is defined explicitly in the agent initialization code. This introduces a problem for developer: what mode to use in the delivered product? Using one of modes as a default does not work for all situations.

  • Registering MIBs with the agent creates and registers managed objects defined in MIBs. Some managed objects may be in conflict with managed objects restored from agent state. Thus the order of registering MIBs and restoring agent's state becomes essential and may lead to difficult to debug problems. For example, the order (1)register MIBs and (2)restore agent's state could override managed object from the MIB with the wrong value from persistent configuration file. The reverse order does not allow preserving managed objects state modified from MIB browser with set operation.

  • At development time managed objects are often renamed and shuffled around the MIB with different OID assignments. Restoring state from the persistent configuration file may resurrect managed objects which do not exist anymore or create managed object with wrong OID or wrong name. In the worst scenario the object with reused OID might have a different syntax type. This may lead to exceptions in the agent's code. Unfortunately MIBs are often written with many errors and code should handle them.

  • Static managed object could have a wrong initialization value. Static managed object in this discussion is referred for example to a read-only scalar which is initialized at application startup and remains unchanged the whole application lifespan. Some value "first" assigned to this scalar is stored in the agent's state configuration file on agent's shutdown. This value is restored next time the application creates the agent. Later the decision made that the initial value of this scalar must be modified to "second". Application initializes the scalar to a new value but agent restores the state with the original one. A developer has a hard time figuring out what is happening. The bug like this could be first time discovered after the application is deployed.

    Another example of this problem is a snmpEngineID scalar declared in SNMP-FRAMEWORK-MIB. The wrong value could be loaded from configuration file and override correct one defined at agent startup. The problem manifests as a broken connection between agent and MIB browser.

  • Agent's configuration file is a black box which cannot be viewed, examined or modified. The only option to fix many of the above discussed problems is to delete the SNMP4J configuration file and recreate it. All managed objects values are lost in this case.

Many of the agent SNMP4J configuration file problems are not discovered on early stage of development because usually the initialized managed object states have the same values in the previously saved agent state. Any modification or discrepancy in managed object OID, name or value manifests as a problem which cannot be debugged.

Persistency design
SNMP4J approach to preserve and restore agent's state is not used in the FriendlySNMP. Probably in some cases restoring agent's state makes sense and could be applied programmatically. Usually agent is not an independent piece of software. It is always instantiated, initialized and started by some application (code). This code is taking care about agent's initialization. Registering MIBs and managed objects declared in them is part of initialization process. Usually initial values are assigned to managed objects at this time. The application is aware of all default initial values for all managed objects. The only exception are read-write managed objects which values were altered from a MIB browser. Thus it makes sense to preserve in an external persistency storage only values associated with read-write managed objects modified via SET request. It is clear how to implement this technique for scalars. Tracking and preserving partial table modifications is much more difficult. A table may have some cell modified or row inserted or row deleted. FriendlySNMP considers any modification in the table as a mark of dirty table and preserves the whole table in a persistence storage.

As it was mentioned above the application which creates an agent is responsible to initialize all managed objects. The application checks presence of persistent values associated with each managed object which could be modified from a MIB browser. Then each managed object is initialized with default value or a value from a persistent storage if such value exists.

Example code to initialize a scalar is the following:

DemoScalarRwMibFriend mib = new DemoScalarRwMibFriend();
. . .
FScalar scalarImageFilename = mib.getImageFilename(); 
scalarImageFilename.setVolatile(false); // loads persistent value (if exist)
if (!scalarImageFilename.isPersistLoaded()) {
    scalarImageFilename.setValue(IMAGE_FILENAME);
}

Example code to initialize a table is the following:

DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
. . .
FTable table = mib.getFlightEntry();
table.setVolatile(false); // loads persistent value (if exist)
if (table.isPersistLoaded()) {
    
}

FriendlySNMP applies persistent value from a storage to a managed object only if persistent value is a perfect fit to a managed object. Scalar values are checked for syntax, table values are checked for columns count and syntax. Orphan values which were not applied to any of managed objects at agent startup are removed from persistent storage.

Persistency policies
FriendlySNMP supports the following two persistency policies: on_change and on_exit. The default policy is on_change. The policy could be modified via setting in a property file.

The policy on_change triggers persistency file recreation on each event which modifies agent persistency state. This policy could be used for agents with not very frequent persistency state updates and for relatively small persistency file data.

The policy on_exit could be recommended for agents with very frequent persistency state updates and/or huge persistency file data. The agent creates the persistency file only once on its shutdown. WARNING: The persistency file is not generated and managed object states are lost if agent crashes.

Persistency management
The trivial technique to manage persistency file is to delete the file. This could be done when agent is not running and the access to the file is not locked. The file will be recreated next time the agent starts with all managed objects including boots counter reset to default values.

FriendlySNMP provides fine grained management of the persistency file content. Management details for modified from MID browser scalars and tables are discussed below.

SNMP scalars persistency
The table persistScalarTable contains details of each read-write scalar which value was modified from a MIB browser. Example of this table:

The default action for rows in this table is keep(1) which does nothing. The committed action delete(2) removes a persistent value for a selected scalar from a persistent storage. The application is notified with an event RESTORE_DEFAULT about this action. It is application's responsibility to restore default value after receiving this event. Application has an option to ignore this event. The removed value from persistent storage does not have any affect to the managed object value which remains intact. Next time the agent restarts it picks up the default value instead of persistent value.

SNMP tables persistency
The table persistTableTable contains details of each modified from a MIB browser table. Modified table is a table which has at least one cell with read-write access or / and the whole table has read-create status with RowStatus column. Example of this table:

The default action for rows in this table is keep(1) which does nothing. The committed action delete(2) removes a persistent value for a selected table from a persistent storage. Note, the whole table is deleted. The application is notified with an event RESTORE_DEFAULT about this action. It is application's responsibility to restore default value after receiving this event. Application has an option to ignore this event. The removed value from persistent storage does not have any affect to the managed object value which remains intact. Next time the agent restarts it picks up the default value instead of persistent value.

The table persistCellTable contains details of all cells of all modified from a MIB browser tables. This table does not have any actions and used only as a viewer. Example of this table:

Custom persistency implementation
Default persistency storage is implemented in a class org.friendlysnmp.persist.PersistStorageImpl which extends an abstract class org.friendlysnmp.persist.PersistStorage. The default behavior of the implementation is to serialize persistent data into a file. Different implementations like using database or networking could be designed. Custom implementation class must extend the base class org.friendlysnmp.persist.PersistStorage, override abstract methods and modify the agent with new custom persistency implementation.