snmp中文含义是简单网络管理协议,可用完成对计算机、路由器和其他网络设备的远程管理和监视。利用 SNMP 协议可以更好地管理和监控网络。管理工作站可以远程管理所有支持该协议的网络设备,如监视网络状态、修改网络设备配置、接收网络事件警告等。
通俗的讲: 我们可用通过snmp协议获取物理机、存储服务器、交换机等设备的信息,本文我们是通过java程序来获取。
首先,我们需要在对应的设备上开启snmp协议,不同的设备可自行百度开启的方法。一般设备有对应的管理软件,可从管理软件开启snmp。
开启snmp需要注意两个地方:
1、开启时会让你设置团体名,一般填pulic就行。
2、端口默认162。
举例说明,获取任何一类设备信息,都需要该类设备对应的MIB文件,从MIB文件中解析出OID,每个OID对应一个指标。使用cpu核数对应的OID才能获取核数的个数。
注意:mib文件获取方式
1、打设备厂商客服电话获取。
2、去githup下载: mib链接,下载后不确定哪个MIB文件是我们需要的,只能用mib浏览器自己试呗。
mib浏览器就相当于一个客户端,用来解析mib文件,可以使用该浏览器模拟snmp请求获取对应的指标信息。
我是用浏览器先测试请求可不可用,然后在从浏览器上拿到oid用java程序获取。
点击进入mib浏览器下载地址
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.8.3</version>
</dependency>
注:代码种有snmpGet、snmpWalk两个方法可用获取snmp信息。
snmpGet:是传入的哪个OID的值,就获取该OID的值。
snmpWalk:获取OID的子OID的值。举例说明,传入的OID是1.2.3.4。 获取的是1.2.3.4.1,1.2.3.4.2,1.2.3.4.3的OID值。
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TableEvent;
import org.snmp4j.util.TableUtils;
import java.io.IOException;
import java.util.*;
@Slf4j
public class SnmpUtil2 {
private static Snmp snmp = null;
private static CommunityTarget target = null;
private static Integer port = 162;
private static String ip = "10.11.83.10";
private static String communityName = "public";
public static void main(String[] args) throws Exception {
new SnmpUtil2(ip, port);
List<TableEvent> tableEvents = snmpWalk("1.3.6.1.4.1.2011.2.235.1.1.15.50.1.12");
for (TableEvent tableEvent : tableEvents) {
VariableBinding[] vb = tableEvent.getColumns();
if (null == vb) {
continue;
}
System.out.println(vb[0].getOid()+"======"+vb[0].getVariable());
}
log.info("=================美丽的分割线===================");
ResponseEvent responseEvent = snmpGet("1.3.6.1.4.1.2011.2.235.1.1.15.50.1.12");
PDU response = responseEvent.getResponse();
for (int i = 0; i < response.size(); i++) {
VariableBinding vb1 = response.get(i);
System.out.println(vb1.getOid().toString() + "===" + vb1.getVariable());
}
}
public SnmpUtil2(String intranetDeviceIp, Integer snmpPort) throws IOException {
if (snmp == null) {
snmp = new Snmp(new DefaultUdpTransportMapping());
snmp.listen();
}
//初始化CommunityTarget
target = new CommunityTarget();
target.setCommunity(new OctetString(communityName));
target.setVersion(SnmpConstants.version2c);
target.setAddress(new UdpAddress(intranetDeviceIp + "/" + snmpPort));
target.setTimeout(1000);
target.setRetries(3);
}
private static ResponseEvent snmpGet(String oid) {
PDU pdu = new PDU();
pdu.addOID(new VariableBinding(new OID(oid)));
ResponseEvent re = null;
try {
re = snmp.get(pdu, target);
} catch (Exception e) {
log.error("snmpGet 异常" + e.getMessage());
}
return re;
}
private static List<TableEvent> snmpWalk(String oid) {
TableUtils utils = new TableUtils(snmp, new DefaultPDUFactory(PDU.GETBULK));
OID[] columnOid = new OID[]{new OID(oid)};
return utils.getTable(target, columnOid, null, null);
}
}
用枚举的方式列了一些常用的OID,包括获取戴尔华为物理机CPU、内存容量及使用率,戴尔存储集群,FS8600,SCV3000,PS6210的容量及使用率
具体看下代码注释
public enum OidEnum {
//物理机CPU总核数、使用率
HostCpuUtilization("1.3.6.1.2.1.25.3.3.1.2","物理机CPU总核数和使用率","walk"),
//物理机内存总量
HostMemoryTotal("1.3.6.1.2.1.25.2.2.0","物理机内存总量","get"),
//物理机内存总量(用来计算内存的使用率或者拿到硬盘总量)
HostMemoryTotalCalUtil("1.3.6.1.2.1.25.2.3.1.5","物理机内存总量(用来计算内存的使用率或者拿到硬盘总量)","walk"),
//物理机内存使用率(用来计算内存的使用率或者拿到硬盘使用量)
HostMemoryUsedCalUtil("1.3.6.1.2.1.25.2.3.1.6","物理机内存使用率(用来计算内存的使用率或者拿到硬盘使用量)","walk"),
/**
* 华为物理机CPU总核数
*/
HuaWeiHostCpuTotal("1.3.6.1.4.1.2011.2.235.1.1.15.50.1.12","华为物理机CPU总核数","walk"),
/**
* 华为物理机CPU使用率, 单位%
*/
HuaWeiHostCpuUtilization("1.3.6.1.4.1.2011.2.235.1.1.1.23.0","华为物理机CPU使用率","get"),
/**
* 华为物理机内存总量
*/
HuaWeiHostMemoryTotal("1.3.6.1.4.1.2011.2.235.1.1.16.50.1.4","华为物理机内存总量","walk"),
/**
* 华为物理机内存使用率, 单位%
*/
HuaWeiHostMemoryUtilization("1.3.6.1.4.1.2011.2.235.1.1.1.25.0","华为物理机内存总量","get"),
//scv3000获得存储总量
StorageScv3000Total("1.3.6.1.4.1.674.11000.2000.500.1.2.32.1.3", "scv3000获得存储总量(单位GB)","walk"),
StorageScv3000Alloc("1.3.6.1.4.1.674.11000.2000.500.1.2.32.1.7", "scv3000获得存储分配量(单位GB)","walk"),
StorageScv3000Used("1.3.6.1.4.1.674.11000.2000.500.1.2.32.1.6", "scv3000获得存储使用量(单位GB)","walk"),
//PS6210存储总量
StoragePs6210Total("1.3.6.1.4.1.12740.1.1.2.1.1.1","PS6210存储总量(单位MB)","get"),
//PS6210存储空闲量
StoragePs6210Free("1.3.6.1.4.1.12740.1.1.2.1.15.1","PS6210存储空闲量(单位MB)","get"),
//PS6210存储使用量
StoragePs6210Used("1.3.6.1.4.1.12740.1.1.2.1.2.1","PS6210存储已使用(单位MB)","get"),
//FS8600获得存储总量
StorageFs8600Total("1.3.6.1.4.1.674.11000.2000.200.1.38.1","FS8600获得存储总量(单位MB)","get"),
//FS8600获得存储使用量
StorageFs8600Used("1.3.6.1.4.1.674.11000.2000.200.1.38.3","FS8600获得存储使用量(单位MB)","get"),
//存储总量
StorageClusterTotal("1.3.6.1.4.1.12124.1.3.1.0", "存储总量(从磁盘集群获取)","get"),
//存储已使用量
StorageClusterUsed("1.3.6.1.4.1.12124.1.3.2.0", "存储使用量(从磁盘集群获取)","get");
private String oid;
private String describe;
private String type;
OidEnum(String oid, String describe, String type) {
this.oid = oid;
this.describe = describe;
this.type = type;
}
public String getOid() {
return oid;
}
public String getdDscribe() {
return describe;
}
}
SNMP Trap是SNMP的一部分,当被监控段出现特定事件,可能是性能问题,甚至是网络设备接口宕掉等,代理端会给管理站发告警事件。通过告警事件,管理站可以通过定义好的方法来处理告警。
trap是主动把告警信息推送到一个监听了162端口的接收程序,接收程序接收到的告警是一个OID,我们需要对oid进行翻译成可读的告警信息,通过该OID去mib文件找到具体告警的详情描述。
比较难的是解析MIB文件,我写了个程序解析mib文件,发现有些文件解析不成功,也不知道为啥,去外网查了下,说是需要引入其他依赖的mib文件,不过我试了很多次还是不行。
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.CommunityTarget;
import org.snmp4j.MessageDispatcher;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.MessageException;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.log.LogFactory;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.StateReference;
import org.snmp4j.mp.StatusInformation;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.*;
import org.snmp4j.tools.console.SnmpRequest;
import org.snmp4j.transport.AbstractTransportMapping;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;
@Slf4j
public class TrapReceiver implements CommandResponder {
public TrapReceiver() {
}
public static void main(String[] args) {
TrapReceiver snmp4jTrapReceiver = new TrapReceiver();
try {
snmp4jTrapReceiver.listen(new UdpAddress("10.11.26.219/162"));
} catch (IOException e) {
System.err.println("Error in Listening for Trap");
System.err.println("Exception Message = " + e.getMessage());
}
}
/**
* This method will listen for traps and response pdu's from SNMP agent.
*/
public synchronized void listen(TransportIpAddress address) throws IOException {
AbstractTransportMapping transport;
if (address instanceof TcpAddress) {
transport = new DefaultTcpTransportMapping((TcpAddress) address);
} else {
transport = new DefaultUdpTransportMapping((UdpAddress) address);
}
ThreadPool threadPool = ThreadPool.create("DispatcherPool", 10);
MessageDispatcher mtDispatcher = new MultiThreadedMessageDispatcher(threadPool, new MessageDispatcherImpl());
// add message processing models
mtDispatcher.addMessageProcessingModel(new MPv1());
mtDispatcher.addMessageProcessingModel(new MPv2c());
// add all security protocols
SecurityProtocols.getInstance().addDefaultProtocols();
SecurityProtocols.getInstance().addPrivacyProtocol(new Priv3DES());
// Create Target
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("1q2w#E$R5t"));
Snmp snmp = new Snmp(mtDispatcher, transport);
snmp.addCommandResponder(this);
transport.listen();
System.out.println("Listening on " + address);
try {
this.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
/**
* This method will be called whenever a pdu is received on the given port
* specified in the listen() method
*/
public synchronized void processPdu(CommandResponderEvent cmdRespEvent) {
log.debug("Received PDU...");
PDU pdu = cmdRespEvent.getPDU();
System.out.println(cmdRespEvent.toString());
System.out.println("接收到的trap信息:[发送来源="+cmdRespEvent.getPeerAddress()+",snmp版本="+"2"+",团体名="+"public"+", 携带的变量="+cmdRespEvent.getPDU().getVariableBindings()+"]");
if (pdu != null) {
for (int i = 0; i < pdu.size(); i++) {
VariableBinding vb1 = pdu.get(i);
// System.err.println(vb1.toString());
}
int pduType = pdu.getType();
if ((pduType != PDU.TRAP) && (pduType != PDU.V1TRAP) && (pduType != PDU.REPORT)
&& (pduType != PDU.RESPONSE)) {
pdu.setErrorIndex(0);
pdu.setErrorStatus(0);
pdu.setType(PDU.RESPONSE);
StatusInformation statusInformation = new StatusInformation();
StateReference ref = cmdRespEvent.getStateReference();
try {
cmdRespEvent.getMessageDispatcher().returnResponsePdu(cmdRespEvent.getMessageProcessingModel(),
cmdRespEvent.getSecurityModel(), cmdRespEvent.getSecurityName(),
cmdRespEvent.getSecurityLevel(),
pdu, cmdRespEvent.getMaxSizeResponsePDU(), ref, statusInformation);
} catch (MessageException ex) {
System.err.println("Error while sending response: " + ex.getMessage());
LogFactory.getLogger(SnmpRequest.class).error(ex);
}
}
}
}
}
引入java的两个jar包,mibble-2.9.3.jar和mibble-mibs-2.9.3.jar,我解析时,遇到有的文件解析成功,有的解析报错。没有解析成功的,可用用笨方法,就是用mib浏览器查看oid,把所有的告警OID做成一个字典。
因没有实现成功,具体解析代码我就不贴了。
如果我有哪个地方写的不合适的地方,或者需要交流的可用加我qq:739749855