OpenNMS扩展–基于JoeSNMP/MIB数据访问实现
1. MIB访问流程及原理
JoeSNMP是OpenNMS采用的SNMP API,已独立成为一个Open Source Framework,基于JoeSNMP可以对MIB网络数据进行相应的操作。JoeSNMP封装了多个对SNMP Get,Set等操作的处理。
l Get:用于读取设备MIB信息库中实例对象的单个值,Get取OID表示对象实例值,
l Set:用于简单的设置MIB中可读写的对象实例值。
MIB访问的主流程图如下:
图1. MIB访问的主流程图
首先根据自变量接收一个要访问的被管理对象主机名,并接收要查询的对象标识符的简略定义形式. 过程首先生成SNMP 报文,一旦报文生成,再把报文简单反转过来,发送出去.生成SNMP 报文后,建立代理地址,创建套接字连接,以便代理能将响应发回. 然后,将SNMP 请求报文发给代理 ,等待一个响应的到来.因为SNMP 是工作在UDP 之上的,所以在SNMP 应用实体间通信时,无需先建立连接,这样虽降低了系统开销,但UDP 传输是不可靠的,为此,网络管理站采取了相应的超时和重发策略. 系统在发出请求报文之后,启动超时计数器,等待响应的到来,并设置重发次数为3. 若3 次之后仍没有收到响应,则关闭套接字,并返回一个“接收失败”错误代码. 反之,若确定收到一个响应,则调用一过程来把响应转换成为内部表示形式, 即对SNMP 报文进行译码. 再用已保存的Request-id与响应Request-id段进行比较,来验证这个报文是否是刚才发出的请求报文的响应. 若是,就调用另一个过程,把每段对象标识符的ASN.1表示形式转换成自己的内部形式,并显示其值。
2. Get-Request/Set-Request步骤
实现SnmpHandler接口
写一个SnmpSet, SnmpGet类, implement SnmpHandler,实现处理SNMP的方法:snmpReceivedPdu, snmpTimeoutErrort和snmpInternalError. 如:SnmpGet.java
public class SnmpGet implements SnmpHandler {
/**
* The log4j category used to log debug messsages and statements.
*/
private static final String LOG4J_CATEGORY = "SureTech.NMS";
public void snmpInternalError(SnmpSession session, int err, SnmpSyntax pdu) {
System.out.println("InternalError");
value ="Internal Error";
synchronized (session) {
session.notify();
}
}
public void snmpReceivedPdu(SnmpSession session, int command, SnmpPduPacket pdu) {
ThreadCategory.setPrefix(LOG4J_CATEGORY);
Category log = ThreadCategory.getInstance();
SnmpVarBind varBind = pdu.getVarBindAt(0);
value = varBind.getValue().toString();
if(log.isDebugEnabled()){
log.debug("Received value: " + varBind.getName() + "="
+ varBind.getValue());
}
synchronized (session) {
session.notify();
}
}
public void snmpTimeoutError(SnmpSession session, SnmpSyntax pdu) {
System.out.println("SnmpTimeout");
value ="Snmp Timeout";
synchronized (session) {
session.notify();
}
}
}
2.2创建SnmpPeer对象
创建一个SnmpPeer对象,需定义:
l Agent的IP地址
l port (defaults to 161)
l retries (defaults to 3)
l timeout (defaults to .8 seconds)
如下在方法snmpGet方法中的红色代码片段:
/**
* The value of snmpget command.
*/
public String snmpGet(String host, String oid) throws SocketException,
UnknownHostException {
InetAddress remote = InetAddress.getByName(host);
SnmpPeer peer = new SnmpPeer(remote);
peer.setPort(161);
peer.setTimeout(5000);
peer.setRetries(1); // it is actually number of tries
SnmpParameters parms = peer.getParameters();
parms.setVersion(SnmpSMI.SNMPV2);
parms.setReadCommunity("public");
final SnmpSession session = new SnmpSession(peer);
session.setDefaultHandler(new SnmpGet());
SnmpVarBind[] vblist = { new SnmpVarBind(oid) };
SnmpPduRequest pdu = new SnmpPduRequest(
oid.endsWith(".0") ? SnmpPduPacket.GET : SnmpPduPacket.GETNEXT,
vblist);
pdu.setRequestId(1);
try {
synchronized (session) {
session.send(pdu);
session.wait();
}
} catch (InterruptedException e) {
// do nothing
} finally {
session.close();
}
return value;
}
2.3创建SnmpParameters对象
创建一个SnmpPeer对象,需定义:
l read community string (defaults to public)
l write community string (defaults to null)
l SNMP version to use (defaults to SNMPV1)
l AsnEncoder to use (defaults to BER)
SnmpParameters parms = peer.getParameters();
parms.setVersion(SnmpSMI.SNMPV2);
parms.setReadCommunity("public");
2.4创建SnmpSession对象,发送PDU
为SnmpPeer创建一个SnmpSession对象,这样就创建一个“SnmpPortal”线程,此线程用来接受来自远程Agent的响应,也可以创建一个Quene来处理所有的SNMP请求。
final SnmpSession session = new SnmpSession(peer);
session.setDefaultHandler(new SnmpGet());
SnmpVarBind[] vblist = { new SnmpVarBind(oid) };
SnmpPduRequest pdu = new SnmpPduRequest(
oid.endsWith(".0") ? SnmpPduPacket.GET : SnmpPduPacket.GETNEXT,
vblist);
pdu.setRequestId(1);
try {
synchronized (session) {
session.send(pdu);
session.wait();
}
} catch (InterruptedException e) {
// do nothing
} finally {
session.close();
}
l 通过调用session.send(pdu)发送PDU请求到远程的Agent,开启一个新的线程发送请求和处理重试或超时。
l 调用session.wait()进行线程等待。
l 当SNMP session线程收到来自Agent的响应后会调用snmpTimeoutError方法,如果Agent没有在超时时间内响应,SNMP session线程将调用snmpTimeoutError方法,如果是内部发生了问题,则调用snmpInternalError方法。
l 线程唤醒,并继续处理。
2.5关闭Session
session.close();