AgentXcpp  Revision:0.2
API Documentation
 All Classes Functions Variables Enumerations Enumerator Friends Pages
How to implement a writeable variable
Note
This is a follow-up tutorial to How to write a Subagent.

In the previous tutorial we wrote a simple read-only counter variable. Here we will continue this work and add write support to the simpleCounter variable. Writing to the variable will set it to a new value, and it will continue to count from there.

The updated MIB looks like that:

SIMPLE-MIB DEFINITIONS ::= BEGIN

IMPORTS
    OBJECT-TYPE FROM SNMPv2-SMI
    enterprises FROM SNMPv2-SMI;

simpleCounter OBJECT-TYPE
    SYNTAX INTEGER
    ACCESS read-write
    STATUS current
    DESCRIPTION "A simple counter which is incremented each time
                 it is queried. Starts with 0."
    ::={ enterprises 42 1 }

END

This is the same as before, but with "ACCESS read-write".

The anatomy of a Set request

A single SNMP Set request may set multiple variables at once (possibly distributed over multiple subagents), and SNMP guarantees an atomic operation in that case. This means that, should any variable fail to perform the Set operation, no other variable is allowed to perform the set operation either. Therefore, the Set operation is carried out in multiple distinct steps:

  1. TestSet checks whether the variable could be set at all. This step is performed for all variables (in all subagents) affected by the single SNMP request. If any variable is not able to accept its new value, the Set operation is aborted (and no variable gets updated). During this step subagents shall allocate the resources they need to carry out the Set operation later.
  2. CommitSet actually sets the new value. This step may fail for a variable, even if the TestSet step announced that the CommitSet would work. If that happens, all other variables which already performed the CommitSet must undo their action. It is strongly recommended to implement variables in such a way that CommitSet does not fail, when possible.
  3. The third step is one of the following:
    • CleanupSet is usually the last step, performed after CommitSet was successful for all variables. In the CleanupSet step, the subagent shall release any resources which where allocated in the TestSet step.
    • UndoSet is performed if CommitSet failed for any variable. UndoSet is then called for all variables which already performed CommitSet and therefore must undo their action. For variables which didn't perform CommitSet yet, CleanupSet is called instead. Like CleanupSet, UndoSet shall also releases all resources which were allocated in the TestSet step.

To implement these actions, the Variable class provides the methods testset(), commitset(), cleanupset() and undoset() which can be overridden by a concrete implementation.

Extending the SimpleCounter class

To implement write support for the SimpleCounter class, we implement all four methods. The testset() implementation allocates an IntegerValue value, which is deallocated by cleanupset() respectively undoset(). This is not very realistic, but it demonstrates the usage of the individual steps.

TestSet

The testset() function must be overridden by any variable which should be writeable. If it is not overridden, agentXcpp assumes that the variable is read-only (which we exploited in the first, read-only version). Hence, we implement it for our SimpleCounter class by adding the following code to it:

private:
IntegerValue* old_value;
public:
virtual testset_result_t testset(shared_ptr<IntegerValue> new_value)
{
try
{
// Allocate space to store the old value
old_value = new IntegerValue;
}
catch(std::bad_alloc)
{
// Resource allocation failed.
return resourceUnavailable;
}
// setting the variable will succeed
return noError;
}

The testset() method receives the value and can check whether setting the variable to this value would work. Here, we don't check the value, but we allocate an IntegerValue to store the old value, which is needed for undoset(). If allocation fails, we return the error resourceUnavailable.

CommitSet

The commitset() method must also be implemented for writeable variables. In the SimpleCounter class, this method sets the internal value and stores the old one, in case undoset() is called:

virtual bool commitset(shared_ptr<IntegerValue> new_value)
{
// Remember old value for rollback
*old_value = counter;
// Set new value
counter = *new_value;
// Operation succeeded
return true;
}

CleanupSet

The cleanupset() method releases old_value again:

virtual void cleanupset(shared_ptr<IntegerValue> new_value)
{
// Release old value
delete old_value;
}

If old_value were an IntegerValue (instead of a pointer to IntegerValue) no allocation and deallocation would have been necessary. The cleanupset() method would not be implemented then. In fact, this method is the only one which is optional for writeable variables, while the other methods are mandatory.

UndoSet

The undoset() method must be implemented by each variable and perform a rollback. It is technically possible to omit this method; the compiler has no way to detect a missing implementation. However, it is an error to omit it, and an error will be reported to the master agent if it is missing. For simpleCounter implementation is easy:

virtual bool undoset(shared_ptr<IntegerValue> new_value)
{
// Restore old value
counter = *old_value;
// Release old value
delete old_value;
// Rollback succeeded
return true;
}

It's important to note that the method releases old_value.

Testing the extended subagent

Again, we compile and run the subagent. This is exactly the same as in the first tutorial (it is assumed that your system is already setup accordingly):

g++ simpleagent.cpp -o simpleagent -lagentxcpp \
    `pkg-config --cflags --libs QtNetwork QtCore`

Next, we start the subagent again:

./simpleagent

Now, we can query the variable:

snmpget -v1 -c rw localhost SIMPLE-MIB::simpleCounter.0

Each time the variable is queried, the counter increments. Now, let's set the counter to, say, 42:

snmpset -v1 -c rw localhost SIMPLE-MIB::simpleCounter.0 i 42

Now, we continue to query it again several times and the counter should continue at 43, further incrementing.

Et voilá: a read-write variable. Now go on and learn how to send notifications!