AgentXcpp  Version:0.3
Internals Documentation
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Pages
How to write a Subagent
Note
It is assumed that agentXcpp is installed properly; see Building and Installing AgentXcpp for how to do that.

This HOWTO explains how to implement, compile and run a trivial subagent. Our subagent has only one variable called 'simpleCounter' which is described within the following MIB:

SIMPLE-MIB DEFINITIONS ::= BEGIN

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

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

END

Now let's see how easy this can be implemented with agentXcpp. We implement the simpleCounter variable within the file SimpleCounter.hpp (header-only) and the main() function within simpleagent.cpp.

Implementing the simpleCounter variable

The simpleCounter thing mentioned in the MIB is an OBJECT-TYPE. OBJECT-TYPE is a description of the object, not the object itself. The OID of simpleCounter is "enterprises.42.1", while the real object will be accessible with OID "enterprises.42.1.0".

In C++, objects are described by their classes (note the difference between classes and objects). The class-object-relation in C++ is similar to the "OBJECT-TYPE"-"object"-relation in SNMP. Therefore, OBJECT-TYPEs are represented by classes in agentXcpp, while SNMP objects are represented by C++ objects.

Our simpleCounter variable will be an object of the class SimpleCounter, which we implement in SimpleCounter.hpp. First of all, we need to include some header files for our SimpleCounter class (and we add a header guard for completeness):

#ifndef _SIMPLECOUNTER_H_
#define _SIMPLECOUNTER_H_
// Make the class agentxcpp::IntegerVariable available:
#include <agentxcpp/IntegerVariable.hpp>

All agentXcpp classes are in the namespace 'agentxcpp'. To make the code more readable, we make the namespace globally available:

using namespace agentxcpp;

Next, we start our SimpleCounter class:

class SimpleCounter : public IntegerVariable
{

The simpleCounter has a type which is given in the SYNTAX clause in the MIB. AgentXcpp provides various classes to match standard SYNTAX types. These classes can be inherited to implement variables of this type, which is what SimpleCounter does. However, IntegerVariable and it companions can also be used without subclassing them; they have default perform_get() implementations which do nothing. Their internal value can be obtained with value() and set with setValue(), and an SNMP GET request will return this internal state.

SimpleCounter inherits from IntegerVariable and overrides IntegerVariable::perform_get(). This method is invoked on SNMP GET requests and should update the internal value of the IntegerVariable object. In our case, it simply increments the member variable "v" (this is easier than using value() and setValue()):

public:
virtual void perform_get()
{
++v;
}
};
#endif // _SIMPLECOUNTER_H_

And that's it. Next, let's implement the main() function.

Implementing the main() function

The main() function is implemented in simpleagent.cpp. First the headers:

// Make the class agentxcpp::MasterProxy available:
#include <agentxcpp/MasterProxy.hpp>
// We need the QCoreApplication class:
#include <QCoreApplication>
// Our simple counter is implemented here:
#include "SimpleCounter.hpp"

Again, we employ the using directive to ease typing:

using namespace agentxcpp;

The first thing to do in main() is to create a QCoreApplication object:

int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);

The agentXcpp library is a QT library, and SNMP request handling is done within the main event loop. Therefore, a QCoreApplication object is needed to provide the event loop. For more information about QCoreApplication refer to the QT documentation.

Next, we create a MasterProxy object:

MasterProxy master;

The class MasterProxy is used to communicate with the master agent. A MasterProxy can be thought of as a local C++ object representing the remote master agent. The MasterProxy can be in disconnected state at any time, or become disconnected, which means that most operations could possibly fail with an disconnected exception.

The default constructor of MasterProxy tries to connect to the master agent via the unix domain socket /var/agentx/master. Of course, another unix domain socket may be chosen, but it cannot be changed once the MasterProxy object is created.

Now, as we have the MasterProxy object, we register a subtree. This means that we provide an OID to the master to indicate that our subagent wishes to serve all requests to OIDs starting with that OID. For our subagent we use "enterprises.42", where 'enterprises' is the standard OID "1.3.6.1.4.1". The master will then send all SNMP requests within that subtree to our subagent (e.g. "enterprises.42.1.0" or "enterprises.42.1.1.2.0"), regardless of whether the subagent actually can serve them. If a request cannot be served, the MasterProxy will take care of it and send an appropriate error message to the master agent. Thus, our SimpleCounter implementation does not have to deal with that.

Here is the code for subtree registration:

Oid simpleagentOid = Oid(enterprises_oid, "42");
master.register_subtree(simpleagentOid);

As you can see, an OidVariable object can be created by giving it the "enterprises_oid" object (which is provided by agentXcpp for convenience) and an additional string with further subids.

Next, we create a SimpleCounter variable and register it with the master:

QSharedPointer<SimpleCounter> counter(new SimpleCounter);
Oid simpleCounterOid = simpleagentOid + 1 + 0;
master.add_variable(simpleCounterOid, counter);

This is the place where we create the real SNMP variable 'counter'. This variable is then registered with the master agent by providing its OID (the one with .0) and the object itself. Note that the object is given in form of a shared pointer, which means that is must be created using the 'new' operator.

Finally we start the main event loop in order to receive requests from the master agent:

app.exec();
}

This function blocks forever.

Compiling the Subagent

When compiling the subagent, we need to link against the needed Qt libraries and, against the agentXcpp library. On a GNU/Linux system, you can type the following command:

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

This assumes that the agentxcpp library is installed properly, so that the compiler can find the headers and the shared object file. The pkg-config tool is used here to add the compiler flags for QT. Note that we do not compile the SimpleCounter class separately, because it is implemented completely within its header file.

On other systems you may need to use another command.

Running the Subagent

This step is difficult to describe, because it depends on your system how to get the subagent running. This howto describes the procedure using the NET-SNMP package on linux. That package contains a daemon which can be used as master agent, and some command line tools, which can be used to query the SNMP agent.

We will configure the NET-SNMP agent to act as AgentX master agent. To keep this guide simple, we will use the SNMPv2c protocol using "rw" as read-write community name, but our subagent can of course be queried using SNMPv1 or SNMPv3, as long as the master is configured properly. The subagent implementation is independent of the used SNMP version.

Starting the Net-SNMP master agent

First, we add the following lines to its config file /etc/snmp/snmpd.conf:

rwcommunity rw        # use "rw" as read-write-community name
master      agentx    # snmpd shall act as agentX master agent
agentXPerms 0666 777  # make socket world-readable

The community name is needed later, when issuing a SNMP GET request. We set it to "rw" in the example, but you can use anything you wish.

The agentXPerms are not strictly required, but making the socket world-readable saves us some trouble regarding access restrictions. In the real world you should probably use a more restrictive setting. Next, we (re)start the master agent using its init scripts:

/etc/init.d/snmpd stop
/etc/init.d/snmpd start

Note that the path depends on your system. For example in ArchLinux, it is /etc/rc.d/snmpd.

Starting the Subagent

Now the subagent can be started:

./simpleagent

The subagent keeps sitting in the console, and now we can try if it works.

Querying the Subagent

Our little subagent exposes an INTEGER object with the OID "1.3.6.1.4.1.42.1.0". We can query this object with an SNMP GET request. We use the NET-SNMP utility snmpget (you can use any tool that you wish, of course):

snmpget -v2c -c rw localhost 1.3.6.1.4.1.42.1.0

Or, if the MIB above is put into a file in the right directory, we can also do:

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

Each time the command is executed, the simpleCounter object reports the incremented number. When everything works, your setup has proven to be usable, and you can now go on and add write support to it!