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();