Chapter 17 Developing an SNMP Manager
The Java Management Extensions (JMX) specify the SNMP manager
API for implementing an SNMP manager application in the Java programming language.
This API is covered in the Javadoc API provided with the Java Dynamic Management Kit (Java DMK)
(see “Related Books” in the Preface
for more information). In this chapter, we explain the example applications
that use this API.
The SNMP manager API can be used to access any SNMP agent, not just
those developed with the Java DMK. It is compatible with SNMPv1,
SNMPv2c and SNMPv3, and it includes mechanisms for handling traps. It lets
you program both synchronous managers that block while waiting for responses
and multi-threaded asynchronous managers that do not. Managers can also communicate
with other managers using inform requests and responses.
The complete source code for these applications is available in the current/Snmp/Manager and current/Snmp/Inform
example directories located in the main examplesDir (see “Directories
and Classpath” in the Preface).
This chapter covers the following topics:
17.1 Synchronous Managers
The synchronous SNMP manager is the simplest to program:
the manager sends a request to an agent (peer) and waits for the answer. During
the wait, the manager is blocked until either a response is received or the
timeout period expires.
The SNMP manager API allows two ways of referring to variables when
issuing requests:
Referring directly to OIDs requires no configuration but makes code
less flexible. The advantages of using variable names are simplified coding
and the independence of manager code when custom MIBs are modified. The SNMP
manager API supports variable names by storing a description of the MIB it
accesses in the SnmpOid object.
To refer to variable names, the manager needs to initialize this description
with an OID table object. The OID table is instantiated from a subclass of
the SnmpOidTableSupport class generated by the mibgen tool when compiling the MIB. Because this support class is
regenerated whenever the MIB is recompiled, the new MIB definition is automatically
loaded into the manager when it is started (see the Example 17–1).
The SNMP manager API specifies the SnmpPeer object
for describing an agent, and the SnmpParameters object
for describing its read-write communities and its protocol version (SNMPv1
or SNMPv2). The objects SnmpUsmPeer and SnmpUsmParameters perform these roles under SNMPv3, and handle
user-based security parameters. The SnmpSession is
an object for sending requests. The session instance has an SnmpOptions field that we can use to set multiplexing and error fixing behavior.
Note –
The objects specified by the SNMP manager API are not MBeans and
cannot be registered in an MBean server to create a manager that can be controlled
remotely. However, you can write an MBean that uses these classes to retrieve
and expose information from SNMP agents.
A manager can contain any number of peers, one for each agent it accesses,
and any number of sessions, one for each type of behavior it implements. Once
the peers and the sessions are initialized, the manager can build lists of
variables and send session requests to operate on them. The session returns
a request object, and the manager calls its waitForCompletion
method with the desired timeout delay.
Finally, the manager analyzes the result of the request, first to see
if there were any errors, then to extract the data returned by the request.
17.1.1 Synchronous SNMPv1 and SNMPv2 Managers
Example 17–1 shows the code of the main method of the SyncManager application
for SNMPv1 and SNMPv2. It applies all the steps described in the previous
section to execute a very simple management operation.
Example 17–1 SNMPv1 and SNMPv2 SyncManager Example
The SyncManager example is found in the examplesDir/current/Snmp/Manager directory.
// read the command line parameters
final String host = argv[0];
final String port = argv[1];
try {
// Specify the OidTable containing all the MIB II knowledge
// Use the OidTable generated by mibgen when compiling MIB II
//
final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
SnmpOid.setSnmpOidTable(oidTable);
final SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
// When creating the parameter object, you can specify the
// read and write community to be used when querying the agent.
final SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);
final SnmpSession session = new SnmpSession("SyncManager session");
// When invoking a service provided by the SnmpSession, it
// will use the default peer if none is specified explicitly
session.setDefaultPeer(agent);
// Create a listener and dispatcher for SNMP traps:
// SnmpEventReportDispatcher will run as a thread and
// listens for traps in UDP port = agent port + 1
final SnmpEventReportDispatcher trapAgent =
new SnmpEventReportDispatcher(Integer.parseInt(port)+1,
null,taskServer,null);
// TrapListenerImpl will receive a callback
// when a valid trap PDU is received.
final Thread trapThread = new Thread(trapAgent);
trapThread.setPriority(Thread.MAX_PRIORITY);
trapThread.start();
// Build the list of variables you want to query.
// For debugging, you can associate a name to your list.
final SnmpVarbindList list= new SnmpVarbindList(
"SyncManager varbind list");
// We want to read the "sysDescr" variable.
list.addVarBind("sysDescr.0");
// Make the SNMP get request and wait for the result.
SnmpRequest request = session.snmpGet(null, list);
final boolean completed = request.waitForCompletion(10000);
// Check for a timeout of the request.
if (completed == false) {
java.lang.System.out.println(
"Request timed out. Check if agent can be reached");
java.lang.System.exit(0);
}
// Check if the response contains an error.
int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
java.lang.System.out.println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
java.lang.System.out.println("Error index = " +
request.getErrorIndex());
java.lang.System.exit(0);
}
// Now we can extract the content of the result.
final SnmpVarbindList result = request.getResponseVarBindList();
java.lang.System.out.println("Result: \n" + result);
[...] // Wait for user to type enter. Traps will be handled.
// End the session properly and we're done
session.destroySession();
java.lang.System.exit(0);
|
In this SNMP manager application, we demonstrate how to implement and
enable a trap listener for the traps sent by the agent. First we need to instantiate
an SnmpEventReportDispatcher object. Then we add our
listener implementation through its addTrapListener
method, and finally we start its thread. Trap listeners can be implemented
in any manager using the SNMP manager API, not only synchronous managers.
17.1.2 Configuring SNMPv3 Security for Managers
Before you run the SNMPv3
manager examples, you require some information about how SNMPv3 user-based
model (USM) security is configured. Below is a brief description of the SNMPv3
security mechanism that provides you with the information you need to run
the SNMPv3 examples in this chapter. Full descriptions of the SNMPv3 security
mechanisms are given in 19.3 SNMPv3 User-Based Security Model.
An SNMPv3 manager requires a security file, in the same way as an SNMPv3
agent does. The jdmk.security file for an SNMPv3 manager
differs slightly from that of an SNMPv3 agent, as shown in the following example.
Example 17–2 A jdmk.security File for an SNMPv3 Manager
# User and security configuration
userEntry=0x8000002a05819dcb6e00001f95,defaultUser,,
usmHMACMD5AuthProtocol,mypasswd
userEntry=0x8000002a05819dcb6e00001f96,defaultUser,,
usmHMACMD5AuthProtocol,mypasswd
# Number of boots
localEngineBoots=5
# Local engine ID
localEngineID=0x8000002a05000000ec4c49ded9
|
In a manager's security file, there is more emphasis on the engine ID
than in an agent's security file. The userEntry provides
all the security information the manager needs to communicate with a particular
authoritative agent, as follows:
-
0x8000002a05819dcb6e00001f95
-
This is the engine ID of the agent with which the manager
will communicate
-
defaultUser
-
The authorized user for that agent
-
usmHMACMD5AuthProtocol
-
The authentication algorithm; in this case, HMAC MD5
-
mypasswd
-
The
privacy password
In this example, the information in the userEntry corresponds to the security information provided in the AgentV3 example's jdmk.security file, in Example 16–3. Therefore, this manager can communicate with
that agent.
The remaining information pertains to the manager itself:
-
localEngineBoots
-
Sets how many times the local engine will boot
-
localEngineID
-
Represents the ID of the engine associated to the SNMP session in which the
manager is running
17.1.3 Synchronous SNMPv3 Managers
The example synchronous manager application created for
SNMPv3 is similar to the SNMPv1/v2 manager, except that it implements SNMPv3
user-based USM mechanisms before making requests.
Example 17–3 SNMPv3 SyncManagerV3 Example
The SyncManagerV3 example is in the examplesDir/current/Snmp/Manager directory.
//Read the command line parameters
final String host = argv[0];
final String port = argv[1];
try {
// Initialize the SNMP Manager API.
final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
SnmpOid.setSnmpOidTable(oidTable);
// Build the session.
//
try {
session= new SnmpSession("SyncManagerV3 session");
}catch(SnmpStatusException e) {
println(e.getMessage());
java.lang.System.exit(0);
}
catch(IllegalArgumentException e) {
// If the engine configuration is faulty
println(e.getMessage());
java.lang.System.exit(0);
}
// Access the SNMPv3 engine using getEngine
//
final SnmpEngine engine = session.getEngine();
// Create an SnmpUsmPeer object
//
final SnmpUsmPeer agent =
new SnmpUsmPeer(engine, host, Integer.parseInt(port));
// Create USM parameters
//
final SnmpUsmParameters p =
new SnmpUsmParameters(engine, "defaultUser");
// Set the security level
//
p.setSecurityLevel(SnmpDefinitions.authNoPriv);
// Contextualize the send request
//
p.setContextName("TEST-CONTEXT".getBytes());
// Set the contextEngineId discovered by the peer upon
// creation
p.setContextEngineId(agent.getEngineId().getBytes());
// Associate the parameter with the agent.
//
agent.setParams(p);
// Discover time of creation and boot
//
agent.processUsmTimelinessDiscovery();
// Associate a default peer (agent) to an SnmpSession.
//
session.setDefaultPeer(agent);
// Create a taskServer for processing traps (optional)
final DaemonTaskServer taskServer = new DaemonTaskServer();
taskServer.start(Thread.NORM_PRIORITY);
// Create a listener and dispatcher for SNMP traps
//
final SnmpEventReportDispatcher trapAgent =
new SnmpEventReportDispatcher(engine,
Integer.parseInt(port) + 1,
taskServer, null);
trapAgent.addTrapListener(new TrapListenerImpl());
final Thread trapThread = new Thread(trapAgent);
trapThread.setPriority(Thread.MAX_PRIORITY);
trapThread.start();
// Build the list of variables you want to query
//
final SnmpVarBindList list =
new SnmpVarBindList("SyncManagerV3 varbind list");
// Read the "sysDescr" variable
//
list.addVarBind("sysDescr.0");
// Make the SNMP get request and wait for the result
//
final SnmpRequest request =
session.snmpGetRequest(null, list);
println("SyncManagerV3::main:" +
"Send get request to SNMP agent on " + host +
" at port " + port);
final boolean completed = request.waitForCompletion(10000);
// Check for a timeout
//
if (completed == false) {
println("SyncManagerV3::main:" +
" Request timed out. Check if agent
can be reached");
// Print request.
//
println("Request: " + request.toString());
java.lang.System.exit(0);
}
// Check the response for errors
//
final int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
println("Error index = " +
request.getErrorIndex());
java.lang.System.exit(0);
}
// Display the result.
//
final SnmpVarBindList result = request.getResponseVarBindList();
println("Result: \n" + result);
[...]
// End the session
//
session.destroySession();
trapAgent.close();
taskServer.terminate();
java.lang.System.exit(0);
[...]
}
|
The first instantiated session creates an engine. This engine can be
accessed using the getEngine method. To avoid excessive
engine creation for each instantiated session, the first engine can be shared
between SNMP session objects. While sharing is possible, it should be avoided.
It represents an unnecessary increase in overhead and limits the security
possibilities because only one security file can be associated with an engine.
The engine is used by all the other classes in the application programming
interface (API) to access the USM configuration, contained in the jdmk.security file associated with that session. In Example 17–3,
when the peer p is created, it discovers its engineId, and then uses it as the SNMPv3 ContextEngineId. When the request is sent, this engine ID is included as a parameter
by setContextEngineId.
In this example, the level of security is set as authentication without
privacy. Consequently, this level of security is applied to all the requests
between this manager and the peers associated with it, via the security parameters.
This level of security must match the level specified in the engine's jdmk.security file.
It is also possible to access MIBs that have been registered in the
scope of a context (see 16.2.3 Binding the MIB MBeans for details of contextualized
MIBs). In this example, the context TEST-CONTEXT is used,
and is set as a parameter in the request by setContextName.
Finally, before sending any requests, if authentication is activated,
the timeliness parameters of the request are discovered, using processUsmTimelinessDiscovery.
17.1.4 SNMP Trap Handler
A trap handler for
the SNMP manager is an object that implements the SnmpTrapListener interface in the com.sun.management.snmp.manager
package. When this object is bound as a listener of an SnmpEventReportDispatcher object, its methods are called to handle trap PDUs.
A trap listener is not a notification listener because the dispatcher
is not a notification broadcaster. The listener has callback methods that
work in the same manner, but they are given objects that represent traps,
not instances of the Notification class.
The interface defines three methods, one for processing SNMPv1 traps,
another for SNMPv2 traps and a third for SNMPv3 traps. Trap PDU packets have
already been decoded by the dispatcher, and these methods handle an object
representation of the trap: SnmpPduTrap objects for
SNMPv1, SnmpPduRequest objects for SNMPv2 and SnmpScopedPduRequest for SNMPv3.
Example 17–4 SnmpTrapListener Implementation
public class TrapListenerImpl implements SnmpTrapListener {
public void processSnmpTrapV1(SnmpPduTrap trap) {
println("NOTE: TrapListenerImpl received trap :");
println("\tGeneric " + trap.genericTrap);
println("\tSpecific " + trap.specificTrap);
println("\tTimeStamp " + trap.timeStamp);
println("\tAgent adress " + trap.agentAddr.stringValue());
}
public void processSnmpTrapV2(SnmpPduRequest trap) {
println("NOTE: Trap V2 not of interest !!!");
}
public void processSnmpTrapV3(SnmpScopedPduRequest trap) {
println("NOTE: TrapListenerImpl received trap V3:");
println("\tContextEngineId : " +
SnmpEngineId.createEngineId(trap.contextEngineId));
println("\tContextName : " + new String(trap.contextName));
println("\tVarBind list :");
for(int i = 0; i < trap.varBindList.length; i++)
println("oid : " + trap.varBindList[i].oid + " val : " +
trap.varBindList[i].value);
}
}
|
To Run the SyncManager Example
-
In the examplesDir/current/Snmp/Manager directory, generate the OID table description of MIB-II that our
manager will access.
-
Compile the example classes.
To set up your environment, see “Directories
and Classpath” in the Preface.
$ javac -classpath classpath -d . *.java
|
-
Make sure that no other agent is running on port 8085, and start the
simple SNMP agent in examplesDir/current/Snmp/Agent.
See To Run the SNMPv1/v2 Agent Example if you have not already built
and run this example.
Type the following command to start the Agent
example:
$ cd examplesDir/current/Snmp/Agent
$ java -classpath classpath Agent nbTraps
|
If you are also running the asynchronous manager example with this agent,
omit the nbTraps parameter. The agent then sends
traps continuously and they can be seen in both managers. Otherwise, specify
the number of traps to be sent to the manager. Wait until the manager is started
to send the traps.
-
Start the manager application in another window to connect to this agent.
If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.
$ cd examplesDir/current/Snmp/Manager
$ java -classpath classpath SyncManager localhost 8085
SyncManager::main: Send get request to SNMP agent on localhost at port 8085
Result:
[Object ID : 1.3.6.1.2.1.1.1.0 (Syntax : String)
Value : SunOS sparc 5.7]
|
Here we see the output of the SNMP request, it is the value of the sysDescr variable on the agent.
-
Press Enter in the agent's window.
You should see the manager receiving the traps it sends. Leave the agent
running if you are going on to the next example, otherwise remember to stop
it by pressing Control-C.
To Run the SyncManagerV3 Example
-
In the examplesDir/current/Snmp/Manager directory, generate the OID table description of MIB-II that our
manager will access.
-
Compile the example classes.
To set up your environment, see “Directories
and Classpath” in the Preface.
$ javac -classpath classpath -d . *.java
|
-
Make sure that no other agent is running on port 8085, and start the
simple SNMPv3 agent, AgentV3, in examplesDir/current/Snmp/Agent.
See To Run the SMNPv3 AgentV3 Example if you have not already built
and run this example.
Type the following commands to start the AgentV3
example:
$ cd examplesDir/current/Snmp/Agent
$ java -classpath classpath -Djdmk.security.file=jdmk.security AgentV3
|
-
Start the manager application in another window to connect to this agent.
If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.
$ cd examplesDir/current/Snmp/Manager
$ java -classpath classpath -Djdmk.security.file=jdmk.security
SyncManagerV3 localhost 8085
|
Be sure to run the manager in its directory, otherwise it cannot find
its jdmk.security file.
-
Press Enter in the agent's window.
You should see the manager receiving the traps it sends. Stop the manager
by typing Control-C.
17.1.5 Synchronous Managers Accessing Several Agents
The SNMP API enables you to configure a synchronous SNMPv3
manager that can access several agents. The SyncManagerMultiV3 example demonstrates a simple SNMPv3 manager API that makes requests
on two SNMPv3 agents. For the sake of simplicity, this manager does not listen
for traps.
Example 17–5 SNMPv3 SyncManagerMultiV3 Example
//Check host and port arguments
//
final String host1 = argv[0];
final String port1 = argv[1];
final String host2 = argv[2];
final String port2 = argv[3];
// Initialize the SNMP Manager API.
//
final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
SnmpOid.setSnmpOidTable(oidTable);
[...]
// Build the session.
//
try {
// When instantiating a session, a new SNMP V3 engine is
// instantiated.
//
session= new SnmpSession("SyncManagerMultiV3 session");
}catch(SnmpStatusException e) {
println(e.getMessage());
java.lang.System.exit(0);
}
catch(IllegalArgumentException e) {
// If the engine configuration is faulty
//
println(e.getMessage());
java.lang.System.exit(0);
}
// Get the SnmpEngine.
//
final SnmpEngine engine = session.getEngine();
// Create a SnmpPeer object for representing the first
// entity to communicate with.
//
final SnmpUsmPeer agent1 =
new SnmpUsmPeer(engine, host1, Integer.parseInt(port1));
//Create the second peer.
//
final SnmpUsmPeer agent2 =
new SnmpUsmPeer(engine, host2, Integer.parseInt(port2));
// Create parameters to associate to the entity to
// communicate with.
//
final SnmpUsmParameters p =
new SnmpUsmParameters(engine, "defaultUser");
// Set security level to authNoPriv.
//
p.setSecurityLevel(SnmpDefinitions.authNoPriv);
// Contextualize the send request
//
p.setContextName("TEST-CONTEXT".getBytes());
// Specify a contextEngineId
//
p.setContextEngineId(agent1.getEngineId().getBytes());
// Associate the newly created parameter to the agent
//
agent1.setParams(p);
// Discover timeliness and boot
agent1.processUsmTimelinessDiscovery();
// Build the list of variables you want to query
//
final SnmpVarBindList list =
new SnmpVarBindList("SyncManagerMultiV3 varbind list");
// Read the "sysDescr" variable
//
list.addVarBind("sysDescr.0");
// Make the SNMP get request on the first agent agent1
// and wait for the result
//
SnmpRequest request =
session.snmpGetRequest(agent1, null, list);
println("SyncManagerMultiV3::main:" +
" Send get request to SNMP agent on " +
host1 + " at port " + port1);
boolean completed = request.waitForCompletion(10000);
// Check for a timeout
//
if (completed == false) {
println("SyncManagerMultiV3::main:" +
" Request timed out. Check if agent can
be reached");
// Print request
//
println("Request: " + request.toString());
java.lang.System.exit(0);
}
// Check if the response contains an error
//
int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
println("Error index = " +
request.getErrorIndex());
java.lang.System.exit(0);
}
// Display the content of the result
//
SnmpVarBindList result = request.getResponseVarBindList();
println("Result: \n" + result);
println("\n>> Press Enter if you want to send the request" +
" on the second agent.\n");
java.lang.System.in.read();
// Repeat the process for the second agent agent2.
//
SnmpUsmParameters p2 =
new SnmpUsmParameters(engine, "defaultUser");
p2.setSecurityLevel(SnmpDefinitions.authNoPriv);
p2.setContextName("TEST-CONTEXT".getBytes());
p2.setContextEngineId(agent2.getEngineId().getBytes());
// Associate the updated parameters with agent2.
//
agent2.setParams(p2);
// Discover timeliness and boot
//
agent2.processUsmTimelinessDiscovery();
// Make the request with agent2
//
request = session.snmpGetRequest(agent2, null, list);
println("SyncManagerMultiV3::main:" +
" Send get request to SNMP agent on " +
host2 + " at port " + port2);
completed = request.waitForCompletion(10000);
// Check for a timeout
//
if (completed == false) {
println("SyncManagerMultiV3::main:" +
" Request timed out. Check if agent can be
reached");
// Print request.
//
println("Request: " + request.toString());
java.lang.System.exit(0);
}
// Check if the response contains an error
//
errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
println("Error index = " +
request.getErrorIndex());
java.lang.System.exit(0);
}
// Display the content of the result
//
result = request.getResponseVarBindList();
println("Result: \n" + result);
println("\n>> Press Enter if you want to stop " +
"this manager.\n");
java.lang.System.in.read();
// End the session
//
session.destroySession();
}
|
The SyncManagerMultiV3 example essentially performs
the same actions as the SyncManagerV3 example, except
it creates two SNMP USM peers rather than just one. The two peer agents are
each created in exactly the same way as in the single peer example.
Both the peer agents are accessed using a common set of parameters,
which are kept in the jdmk.security file for the session.
This jdmk.security file contains two rows, one for each
SNMP USM peer agent, as shown in the following example.
Example 17–6 jdmk.security File for the SyncManagerMultiV3 Example
#Authentication only.
userEntry=0x8000002a05819dcb6e00001f95,defaultUser,,usmHMACMD5AuthProtocol,
mypasswd
userEntry=0x8000002a05819dcb6e00001f96,defaultUser,,usmHMACMD5AuthProtocol,
mypasswd
# #####APPENDED PROPERTY####
localEngineBoots=5
# #####APPENDED PROPERTY####
localEngineID=0x8000002a05000000ec4c49ded9
|
If you configure your manager to create a greater number of peers, then
its associated jdmk.security file must contain a corresponding
number of entries, one for each authoritative engine with which the manager
will communicate.
In this example, the only difference between the two userEntry rows is between the engine ID numbers. These engine IDs correspond
to the engines of the two SNMP adaptor servers created by the MultipleAgentV3 example in 16.5 Multiple Agents.
To Run the SyncManagerMultiV3 Example
-
Before running the example, you must have run mibgen
and compiled the Java classes in the examplesDir/current/Snmp/Manager directory.
See To Run the SyncManager Example for instructions if you have
not already done this.
-
Make sure that no other agent is running, and start the multiple SNMPv3
agent, MultipleAgentV3, in examplesDir/current/Snmp/Agent.
See 16.5.1 Running the SNMPv3 MultipleAgentV3 Example if you have not already built and
run this example.
Type the following commands to start the MultipleAgentV3 example:
$ cd examplesDir/current/Snmp/Agent
$ java -classpath classpath -Djdmk.security.file=jdmk.security
MultipleAgentV3
|
The MultipleAgentV3 example simulates two SNMPv3
agents in one process. We shall make these agents peers of SyncManagerMultiV3.
Be sure to run the multiple agent in its directory, otherwise it cannot
find its jdmk.security and jdmk2.security
files.
-
Start the manager application in another window to connect to these
two agents.
If you want to run the manager on a different host from the one where
the agents are running, replace localhost with the name
of the host where you started the agents. Both the agents in the MultipleAgentV3 example run on the same host.
$ cd examplesDir/current/Snmp/Manager
$ java -classpath classpath -Djdmk.security.file=jdmk.security
SyncManagerMultiV3 localhost 8085 localhost 8087
|
Be sure to run the manager in its directory, otherwise it cannot find
its jdmk.security file.
-
You should see sending a request to the first agent. Press Enter to send a request to the second agent.
You will now see the manager sending a second request to the agent on
port 8087.
-
Press Enter to stop the manager
17.2 Asynchronous Managers
The asynchronous SNMP manager
lets you handle more requests in the same amount of time because the manager
is not blocked waiting for responses. Instead, it creates a request handler
object that runs as a separate thread and processes several responses concurrently.
Otherwise, the initialization of peers, parameters, sessions, options, and
dispatcher is identical to that of a synchronous manager. This applies to
all three versions of SNMP.
Example 17–7 The AsyncManager Example
// read the command line parameters
String host = argv[0];
String port = argv[1];
// Use the OidTable generated by mibgen when compiling MIB-II.
final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
// Sample use of the OidTable.
SnmpOidRecord record = oidTable.resolveVarName("udpLocalPort");
java.lang.System.out.println(
"AsyncManager::main: variable = " + record.getName() +
" oid = " + record.getOid() + " type = " + record.getType());
// Initialize the SNMP Manager API.
SnmpOid.setSnmpOidTable(oidTable);
// Create an SnmpPeer object for representing the agent
final SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
// Create parameters for communicating with the agent
final SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);
// Build the session and assign its default peer
final SnmpSession session = new SnmpSession("AsyncManager session");
session.setDefaultPeer(agent);
final DaemonTaskServer taskServer = new DaemonTaskServer();
taskServer.start(Thread.NORM_PRIORITY);
// Same dispatcher and trap listener as in SyncManager example
SnmpEventReportDispatcher trapAgent =
new SnmpEventReportDispatcher(Integer.parseInt(port)+1,
null,taskServer,null);
trapAgent.addTrapListener(new TrapListenerImpl());
final Thread trapThread = new Thread(trapAgent);
trapThread.setPriority(Thread.MAX_PRIORITY);
trapThread.start();
// Build the list of variables to query
SnmpVarbindList list = new SnmpVarbindList("AsyncManager varbind list");
list.addVariable("sysDescr.0");
// Create a simple implementation of an SnmpHandler.
AsyncRspHandler handler = new AsyncRspHandler();
// Make the SNMP walk request with our handler
final SnmpRequest request = session.snmpWalkUntil(
handler, list, new SnmpOid("sysServices"));
// Here you could do whatever processing you need.
// In the context of the example, we are just going to wait
// 4 seconds while the response handler displays the result.
Thread.sleep(4000);
[...] // Wait for user to type enter. Traps will be handled.
// End the session properly and we're done.
//
session.destroySession();
java.lang.System.exit(0);
|
The trap mechanism in this application is identical to the one presented
in the SyncManager example. The event report dispatcher
receives traps and calls the corresponding method of our SnmpTrapListener class.
In this example, the manager performs an snmpWalkUntil
request that gives a response for each variable that it gets. The response
handler is called to process each of these responses.
17.2.1 Response Handler
A response handler for an asynchronous manager is an implementation
of the SnmpHandler interface. When a handler object
is associated with a request, its methods are called when the agent returns
an answer or fails to return an answer. In these methods, you implement whatever
actions you want for processing the responses to a request. Typically, these
methods extract the result of each request or the reason for its failure.
The timeout used by the request handler is the one specified by the SnmpPeer object representing the agent. The handler is also called
to process any errors caused by the request in the session. This ensures that
the manager application is never interrupted after issuing a request.
Example 17–8 The SnmpHandler Implementation
public class AsyncRspHandler implements SnmpHandler {
// Empty constructor
public AsyncRspHandler() {
}
// Called when the agent responds to a request
public void processSnmpPollData( SnmpRequest request,
int errStatus, int errIndex, SnmpVarbindList vblist) {
// Check if a result is available.
if (request.getRequestStatus() ==
SnmpVarBindList result = request.getResponseVarBindList();
println("Result = " + result.varBindListToString());
// Extract the result for display
SnmpVarbindList result = request.getResponseVbList();
java.lang.System.out.println(
"Result = " + result.vbListToString());
}
}
// Called when the agent fails to respond to a request
public void processSnmpPollTimeout(SnmpRequest request) {
java.lang.System.out.println(
"Request timed out: " + request.toString());
if (request.getRequestStatus() ==
SnmpRequest.stResultsAvailable) {
// The result is empty and will display an error message
SnmpVarbindList result = request.getResponseVbList();
java.lang.System.out.println(
"Result = " + result.vbListToString());
}
}
// Called when there is an error in the session
public void processSnmpInternalError(SnmpRequest request,
String errmsg) {
java.lang.System.out.println(
"Session error: " + request.toString());
java.lang.System.out.println("Error is: " + errmsg);
}
}
|
To Run the AsyncManager Example
-
If you have not done so already, start the simple SNMP agent in examplesDir/current/Snmp/Agent, after making sure
that no other agent is running on port 8085.
This manager also uses the OID table description (the SnmpOidTableSupport class) that we generated from the MIB for the synchronous manager.
If you have not already done so, see To Run the SyncManager Example for
instructions on how to do this.
-
If you do not have an SNMP agent still running, make sure that no other
agent is running on port 8085 and start one with the following command:
$ cd examplesDir/current/Snmp/Agent
$ java -classpath classpath Agent nbTraps
|
-
Specify the number of traps to be sent to the manager in the nbTraps parameter.
Wait until the manager is started to send the traps.
-
In another terminal window, start the manager application to connect
to this agent.
If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.
$ cd examplesDir/current/Snmp/Manager
$ java -classpath classpath AsyncManager localhost 8085
|
You should then see the output of the SnmpWalkUntil
request: the response handler method is called for each variable that is returned.
-
Press Enter in the agent's window to send traps and
see the trap reports as they are received in the manager.
When you have finished with the agent, do not forget to stop it by typing Control-C in its terminal window.
17.3 Inform Requests
The inform request is specified in SNMPv2 and SNMPv3 as a mechanism
for sending a report and receiving a response.
Because SNMP managers both send and receive inform requests, the SNMP
manager API includes the mechanisms for doing both. Roughly, inform requests
are sent in the same way as other requests, and they are received in the same
way as traps. Both of these mechanisms are explained in the following sections.
There are simple examples, one for SMNPv2 and one for SNMPv3, in examplesDir/current/Snmp/Inform. Each example has
two manager applications, one of which sends an inform request, and the other
which listens for and replies to this request. No SNMP agents are involved
in these exchanges.
17.3.1 Sending an Inform Request (SNMPv2)
Like the
other types of requests, the manager sends an inform request through a session.
The only difference is that the peer object associated with the request should
be an SNMP manager able to receive and reply to InformRequest
PDUs.
You can associate a peer with a session by making it the default peer
object. This is how we do it in this example. This means that if we do not
specify a peer when sending requests, they are automatically addressed to
our manager peer. Because sessions often have agent peers as a default, you
can specify the manager peer as a parameter to the snmpInform
method of the session object.
Example 17–9 Sending an SNMPv2 Inform Request in SimpleManager1
// When calling the program, you must specify the hostname
// of the SNMP manager you want to send the inform to.
//
final String host = argv[0];
// Initialize the port number to send inform PDUs on port 8085.
//
final int port = 8085;
try {
// Create an SnmpPeer object that represents the entity to
// communicate with.
//
final SnmpPeer peer = new SnmpPeer(host, port);
// Create parameters to associate to the peer.
// When creating the parameter object, you can specify the
// read and write community to be used when sending an
// inform request.
//
final SnmpParameters params = new SnmpParameters(
"public", "private", "public");
// The newly created parameter must be associated to the peer.
//
peer.setSnmpParam(params);
// Build the session. A session creates, controls and manages
// one or more requests.
//
final SnmpSession session = new SnmpSession("SimpleManager1
session");
session.setDefaultPeer(peer);
// Make the SNMP inform request and wait for the result.
//
final SnmpRequest request = session.snmpInform(
null, new SnmpOid("1.2.3.4"), null);
java.lang.System.out.println(
"NOTE: Inform request sent to SNMP manager on " +
host + " at port " + port);
boolean completed = request.waitForCompletion(10000);
// Check for a timeout of the request.
//
if (completed == false) {
java.lang.System.out.println(
"\nSimpleManager1::main: Request timed out. " +
"Check if agent can be reached");
// Print request.
//
java.lang.System.out.println("Request: " + request.toString());
java.lang.System.exit(0);
}
// Now we have a response. Check if the response contains an error.
//
final int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
java.lang.System.out.println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
java.lang.System.out.println("Error index = " +
request.getErrorIndex());
java.lang.System.exit(0);
}
// Now we shall display the content of the result.
//
final SnmpVarbindList result = request.getResponseVbList();
java.lang.System.out.println("\nNOTE: Response received:\n" + result);
// Stop the session properly before exiting
session.destroySession();
java.lang.System.exit(0);
} catch(Exception e) {
java.lang.System.err.println(
"SimpleManager1::main: Exception occurred:" + e );
e.printStackTrace();
}
|
Before sending the request, the snmpInform method
automatically adds two variables to the head of the varbind
list that is passed in as the last parameter. These are sysUpTime.0 and snmpTrapOid.0, in the order they appear
in the list. These variables are mandated by RFC 1905 and added systematically so that the calling
process does not need to add them.
Like all other requests in a session, inform requests can be handled
either synchronously or asynchronously in the sender. In our example, we process
the inform request synchronously: the manager blocks the session while waiting
for the completion of the request. In an asynchronous manager, you would need
to implement a response handler as explained in 17.2.1 Response Handler,
and then use it to process responses, as shown in Example 17–7.
17.3.2 Sending an Inform Request (SNMPv3)
This example shows how to build an SNMPv3 manager that sends and/or receives
SNMPv3 requests. It initializes an SNMPv3 USM peer and an SNMP session, and
then sends an inform request to a second SNMP manager.
Example 17–10 Sending an SNMPv3 Inform Request in SimpleManager1V3
// When calling the program, specify the hostname of the
// SNMP manager to send the inform to
//
final String host = argv[0];
// Initialize the port number to send inform PDUs on port 8085.
//
final int port = 8085;
[...]
// Create an SnmpUSMPeer object for representing the entity
// to communicate with.
//
final SnmpUsmPeer peer = new SnmpUsmPeer(session.getEngine(),
host,
port);
// Create parameters to associate to the entity to
// communicate with.
// When creating the parameter object, you specify the necessary
// security related parameters.
//
final SnmpUsmParameters params =
new SnmpUsmParameters(session.getEngine());
// Set the parameters
//
// First a principal
//
params.setPrincipal("defaultUser");
// A security level. Authentication is applied.
//
params.setSecurityLevel(SnmpDefinitions.authNoPriv);
// Add a contextEngineId. The context engine Id is the
// manager's engine ID and not the adaptor's.
//
params.setContextEngineId(peer.getEngineId().getBytes());
// Add a context name.
//
params.setContextName("TEST-CONTEXT".getBytes());
// The newly created parameter must be associated to the peer.
//
peer.setParams(params);
// The inform is authenticated, so the timeliness
// discovery must be processed.
//
peer.processUsmTimelinessDiscovery();
// A default peer (listener manager) can be associated to an
// SnmpSession. When invoking a service provided by the
// SnmpSession, if the peer is not specified, the session
// will perform the service using the default one as the
// target of the service
//
session.setDefaultPeer(peer);
println("\nNOTE: SNMP V3 simple manager 1 initialized");
// Make the SNMP inform request and wait for the result.
//
println("\n>> Press Enter to send the inform request on " +
host + " at port " + port + "...");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
final SnmpRequest request =
session.snmpInformRequest(null, new SnmpOid("1.2.3.4"),
null);
println("NOTE: Inform request sent to SNMP manager on " +
host + " at port " + port);
final boolean completed = request.waitForCompletion(10000);
// Check for a timeout of the request.
//
if (completed == false) {
println("\nSimpleManager1::main: Request timed out." +
" Check reachability of agent");
// Print request.
//
println("Request: " + request.toString());
java.lang.System.exit(0);
}
// Check if the response contains an error
//
final int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
println("Error status = " +
SnmpRequest.snmpErrorToString(errorStatus));
println("Error index = " + request.getErrorIndex());
java.lang.System.exit(0);
}
// Display the content of the result.
//
final SnmpVarBindList result = request.getResponseVarBindList();
println("\nNOTE: Response received:\n" + result);
// End the session
//
println("\nNOTE: SNMP V3 simple manager 1 stopped...");
session.destroySession();
}
|
As you can see, once the SNMPv3 session has been instantiated and the
SNMPv3 USM peer has been created and configured, the inform request is sent
in exactly the same way as under SNMPv2. The only difference is the additional
SNMPv3 security.
17.3.3 Receiving Inform Requests
Managers receive inform requests
as they do traps: they are unsolicited events that must be received by a dispatcher
object. Unlike traps, an inform request requires a response PDU that, according
to the SNMP specification, must contain the same variable bindings. Therefore,
immediately after an inform request is successfully received and decoded,
the SnmpEventReportDispatcher class automatically constructs
and sends the inform response back to the originating host.
The manager application then retrieves the data in the inform request
through a listener on the dispatcher. Inform request listeners are registered
with the dispatcher object in the same way as trap listeners. The receiving
manager in our example is very simple, because its only function is to create
the dispatcher and the listener for inform requests. The receiving manager SimpleManager2 is the same for both the SimpleManager1 and SimpleManager1V3 examples.
Example 17–11 Receiving Inform Requests in SimpleManager2
// Initialize the port number to listen for incoming inform PDUs on
// port 8085.
int port = 8085;
try {
// Create a dispatcher for SNMP event reports
// (SnmpEventReportDispatcher).
// SnmpEventReportDispatcher is run as a thread and listens for informs
// on the specified port.
// Add our InformListenerImpl class as an SnmpInformListener.
// InformListenerImpl will receive a callback when a valid trap
// PDU is received.
//
final DaemonTaskServer taskServer = new DaemonTaskServer();
taskServer.start(Thread.NORM_PRIORITY);
final SnmpEventReportDispatcher informDispatcher =
new SnmpEventReportDispatcher(port,taskServer,null,null);
informDispatcher.addInformListener(new InformListenerImpl());
final Thread informThread = new Thread(informDispatcher);
informThread.setPriority(Thread.MAX_PRIORITY);
informThread.start();
println("\nNOTE: SNMP simple manager 2 initialized");
// Note that you can use the same SnmpEventReportDispatcher object
// for both incoming traps and informs.
// Just add your trap listener to the same dispatcher, for example:
// informDispatcher.addTrapListener(new TrapListenerImpl());
// Here we are just going to wait for inform PDUs.
//
java.lang.System.out.println("\nNOTE: Event report listener
initialized");
java.lang.System.out.println(
" and listening for incoming inform PDUs on port " + port
+ "...");
} catch(Exception e) {
java.lang.System.err.println(
"SimpleManager2::main: Exception occurred:" + e );
e.printStackTrace();
}
|
The remaining step is to program the behavior we want upon receiving
an inform request. To do this, we must write the InformListenerImpl class that we registered as an inform request listener in the
previous code sample. This class implements the SnmpInformListener interface and its processSnmpInform and processSnmpInformV3 methods handle the incoming inform requests.
Because the dispatcher automatically sends the inform response back
to the originating host, the SnmpInformListener implementation
does not need to do this. Usually this method extracts the variable bindings
and takes whatever action is necessary upon receiving an inform request. In
our example, we simply print out the source and the contents of the inform
request.
Example 17–12 The InformListenerImpl Class
public class InformListenerImpl implements SnmpInformListener {
public void processSnmpInform(SnmpPduRequest inform) {
// Display the received PDU.
//
java.lang.System.out.println("\nNOTE: Inform request received:\n");
java.lang.System.out.println("\tType
= " + inform.pduTypeToString(inform.type));
java.lang.System.out.println("\tVersion = " + inform.version);
java.lang.System.out.println("\tRequestId = " + inform.requestId);
java.lang.System.out.println("\tAddress = " + inform.address);
java.lang.System.out.println("\tPort = " + inform.port);
java.lang.System.out.println("\tCommunity = " +
new String(inform.community));
java.lang.System.out.println("\tVB list = ");
for (int i = 0; i < inform.varBindList.length; i++) {
java.lang.System.out.println("\t\t" + inform.varBindList[i]);
}
}
public void processSnmpInformV3(SnmpScopedPduRequest inform) {
println("\nNOTE: Inform request V3 received:\n");
println("\tType =
" + inform.pduTypeToString(inform.type));
println("\tVersion = " + inform.version);
println("\tRequestId = " + inform.requestId);
println("\tAddress = " + inform.address);
println("\tPort = " + inform.port);
println("\tContext = " + new String(inform.contextName));
println("\tVB list = ");
for (int i = 0; i < inform.varBindList.length; i++) {
println("\t\t" + inform.varBindList[i]);
}
}
private final static void println(String msg) {
java.lang.System.out.println(msg);
}
private final static void print(String msg) {
java.lang.System.out.print(msg);
}
}
|
To Run the SNMPv2 Inform Request Example
The examplesDir/current/Snmp/Inform directory contains all of the files for the two manager applications,
along with the InformListenerImpl class.
-
Compile all files in this directory with the javac
command.
For example, on the Solaris platform with the Korn shell, you would
type:
$ cd examplesDir/current/Snmp/Inform/
$ javac -classpath classpath *.java
|
-
To run the example, start the inform request receiver with the following
command.
$ java -classpath classpath SimpleManager2
|
You can start the application in another terminal window or on another
host.
-
Wait for this manager to be initialized, then start the other manager
with the following command.
The hostname is the name of the host where
you started the receiving manager, or localhost.
$ java -classpath classpath SimpleManager1 hostname
|
-
When the sender is ready, press Enter to send the
inform request.
You should see the contents of the request displayed by the receiving
manager. Immediately afterwards, the sender receives the inform response containing
the same variable bindings and displays them. Both manager applications then
exit automatically.
To Run the SNMPv3 Inform Request Example
-
Before running the example, you must compile the Java classes in the examplesDir/current/Snmp/Inform directory.
Type the following commands in your working directory:
$ javac -d . SimpleManager1V3.java SimpleManager2.java
InformListenerImpl.java
|
-
Start the receiving manager, SimpleManager2:
$ java -classpath classpath -Djdmk.security.file=jdmk.security
SimpleManager2
|
This manager binds to port 8085.
-
Wait for this manager to be initialized, then start the other manager
with the following command, specifying the hostname and port number of the
receiving manager.
In this case, the host name is the localhost
$ java -classpath classpath -Djdmk.security.file=sender.security
SimpleManager1V3 localhost
|
-
When the sender is ready, press Enter to send the
inform request.
You should see the contents of the request displayed by the receiving
manager. Immediately afterwards, the sender receives the inform response containing
the same variable bindings and displays them. Both manager applications then
exit automatically.