最近工作内容涉及到snmp协议相关内容,由于第一次接触,又没有实际的环境测试,写出来的东西自己都不确定能不能用,直到最近有环境后,花了一段时间才搞定。
写下这篇文章,只是给和我之前一样的小白指点一波,让其他人不必再和我一样被折磨一遍,如果大佬看到我哪里有写错,也请指正。
这里首先强调一点,snmp协议,我java代码这边是作为接收者,而提供信息的是机器设备,但我想通过snmp协议获取数据,是由我发起,然后接收返回的参数。
我这边对接的对象是华为交换机,我这边由参考一篇文章,可以根据下面这篇的文章找到对接的机器的型号,再找到oid
http://t.zoukankan.com/zhenwei66-p-10473121.html
然后网上还可以取找一个叫MIB Browser的工具,这个玩意也可以测试snmp
下面是我的java代码,我会逐一解释其中的内容
首先是第一个方法,我是把snmp协议需要的参数全部丢到json文件中了,这个方法是用来处理json文件读取的,然后把读到的信息转成对象,转换的是最下面两个类,再写入snmp配置,两个入参,第一个是json文件中的品牌,第二个是你snmp协议中的请求例如:get,getnext这些。
private static final Logger logger = LoggerFactory.getLogger(SnmpUtil.class);
private static final String jsonFile = "Snmp.json";
private Snmp snmp;
/**
* 根据入参,选择json文件内容进行snmp协议发送
* @author 汪彪
* @date 2022/7/22 15:23
* @param name
* @param pduType
* @return java.util.List<jee.pluses.com.vo.huawei.Mib>
*/
public List<Mib> sendSnmp(String name,Integer pduType) throws IOException {
//读取Snmp.json文件中数据
String js = null;
js = readJson(jsonFile);
//将读取到的字符串实例化成对象
JSONObject jasonDate = JSONObject.parseObject(js);
JSONArray pageData = (JSONArray) jasonDate.get("snmp");
List<SnmpVo> snmpVoList = JSONObject.parseArray(pageData.toString(), SnmpVo.class);
for (SnmpVo snmpVo : snmpVoList) {
if (name.equals(snmpVo.getName())){
return sendSnmp(snmpVo, pduType);
}
}
return null;
}
这个方法是配置snmp协议属性用的,在这里调用其他方法生成snmp,pdu,target,然后调用send方法就可以收获你想要的值
/**
* 获取snmp数据,根据json文件内容
* @author 汪彪
* @date 2022/7/22 15:22
* @param snmpVo
* @param pduType
* @return java.util.List<jee.pluses.com.vo.huawei.Mib>
*/
private List<Mib> sendSnmp(SnmpVo snmpVo,Integer pduType) throws IOException {
initSnmp(snmpVo);
PDU pdu = setPDU(snmpVo, pduType);
Target target = setTarget(snmpVo);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
if (response == null){
return null;
}
List<Mib> mib = snmpVo.getMib();
for (Mib m : mib) {
Variable variable = response.getVariable(new OID(m.getOid()));
m.setValue(String.valueOf(variable));
}
return mib;
}
这里是target的设置,由于我对接的设备是版本为2的,所以版本3行不行,我这里还不确定。但版本1和2 都是一样的,只需要设置地址,和团体名称,加设置版本,基本就可以了
/**
* target设置,版本3未测试
* @author 汪彪
* @date 2022/7/21 17:12
* @param snmpVo
* @return org.snmp4j.Target
*/
private Target setTarget(SnmpVo snmpVo){
Address targetAddress = GenericAddress.parse("udp:" + snmpVo.getTargetIp() + "/" + snmpVo.getTargetPort());
Target target = null;
//SNMP的不同版本
if (snmpVo.getVersion() == SnmpConstants.version3) {
// 添加用户
OctetString userName = new OctetString(snmpVo.getUserName());
OctetString userAuthPassword = new OctetString(snmpVo.getUserAuthPassword());
OctetString userPrivatePassword = new OctetString(snmpVo.getUserPrivPassword());
UsmUser usmUser = new UsmUser(userName, ,userAuthPassword , ,userPrivatePassword);
snmp.getUSM().addUser(userName, usmUser);
target = new UserTarget();
// 设置安全级别
((UserTarget) target).setSecurityLevel(SecurityLevel.AUTH_PRIV);
((UserTarget) target).setSecurityName(userName);
target.setVersion(SnmpConstants.version3);
} else {
//设定CommunityTarget
target = new CommunityTarget();
if (snmpVo.getVersion() == SnmpConstants.version1) {
//设置使用的snmp版本
target.setVersion(SnmpConstants.version1);
} else {
//设置使用的snmp版本
target.setVersion(SnmpConstants.version2c);
}
//设置团体名
((CommunityTarget) target).setCommunity(new OctetString(snmpVo.getCommunity()));
}
//设定地址
target.setAddress(targetAddress);
//设置超时重试次数
target.setRetries(2);
//设置超时时间
target.setTimeout(TimeUnit.SECONDS.toMillis(snmpVo.getTimeout()));
return target;
}
这里是pdu的设置,这里面的oid非常关键,根据上面的参考文章,找到oid,随便挑一个测试就可以
/**
* pdu设置
* @author 汪彪
* @date 2022/7/21 16:53
* @param snmpVo
* @param pduType
* @return org.snmp4j.PDU
*/
private PDU setPDU(SnmpVo snmpVo,Integer pduType){
PDU pdu = new PDU();
for (Mib mib : snmpVo.getMib()) {
pdu.add(new VariableBinding(new OID(mib.getOid())));
}
pdu.setType(pduType);
return pdu;
}
这边snmp的初始化其实也很简单,没什么需要特别注意的点
/**
* snmp初始化
* @author 汪彪
* @date 2022/7/21 16:47
* @param snmpVo
* @return void
*/
private void initSnmp(SnmpVo snmpVo) throws IOException {
snmp = new Snmp(new DefaultUdpTransportMapping());
if (snmpVo.getVersion() == SnmpConstants.version3) {
// 设置安全模式
USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
SecurityModels.getInstance().addSecurityModel(usm);
}
snmp.listen();
}
这边是我用来读取json文件的方法,json文件我是直接放在了resources文件夹下,大家也可以按目录去放
/**
* 读取json文件
* @author 汪彪
* @date 2022/7/21 16:21
* @param filePath
* @return java.lang.String
*/
public static String readJson(String filePath) throws IOException {
InputStream inputStream = SnmpUtil.class.getClassLoader().getResourceAsStream(filePath);
File file = new File(filePath);
copyInputStreamToFile(inputStream, file);
if (!file.exists()) {
logger.error("未找到文件");
}
//创建FileInputStream对象
FileInputStream fileInputStream = new FileInputStream(file);
//创建InputStreamReader对象
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "utf-8");
//创建字符输入流
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//从文件读取字符
String js = "";
String line = null;
while ((line = bufferedReader.readLine()) != null) {
js += line;
}
return js;
}
/**
* 复制流
* @author 汪彪
* @date 2022/7/22 16:05
* @param inputStream
* @param file
* @return void
*/
private static void copyInputStreamToFile(InputStream inputStream, File file) throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int read;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
}
}
这里是协议的配置信息,由json文件转对象时需要用到这个类
package jee.pluses.com.vo.huawei;
import lombok.Data;
import java.util.List;
/**
* Snmp协议
*
* @author 汪彪
* @date 2022/7/21
*/
@Data
public class SnmpVo {
/**
* 品牌名称
*/
private String name;
/**
* 目标ip地址
*/
private String targetIp;
/**
* 目标端口号
*/
private Short targetPort;
/**
* 团体名
*/
private String community;
/**
* 协议版本
*/
private Integer version;
/**
* 超时设置
*/
private Integer timeout;
/**
* 用户名称
*/
private String userName;
/**
*
*/
private String userAuthPassword;
/**
*
*/
private String userPrivPassword;
/**
* 点位信息
*/
private List<Mib> mib;
}
这里是每个节点的信息类
package jee.pluses.com.vo.huawei;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 点位信息
*
* @author 汪彪
* @date 2022/7/21
*/
@Data
public class Mib {
/**
* 节点名称
*/
@ApiModelProperty(value="节点名称", name="node", dataType = "String")
private String node;
/**
* OID
*/
@ApiModelProperty(value="OID", name="oid", dataType = "String")
private String oid;
/**
* 说明
*/
@ApiModelProperty(value="说明", name="mean", dataType = "String")
private String mean;
/**
* 点位值
*/
@ApiModelProperty(value="点位值", name="value", dataType = "String")
private String value;
}
除了最下面两个类,其他方法代码都是在一个类中,以下是导包信息
import jee.pluses.com.common.PageResult;
import jee.pluses.com.service.huawei.HuaWeiService;
import jee.pluses.com.utils.PageResultUtil;
import jee.pluses.com.utils.SnmpUtil;
import jee.pluses.com.utils.huawei.HuaWeiConstants;
import jee.pluses.com.vo.huawei.Mib;
import jee.pluses.com.vo.huawei.PageModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.PDU;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
这是MIB Browser的参数配置,其实和上面我的配置差不多。
Address和Port在target中配置
Community也是在target中配置
Version也是在target中配置
OID则是在PDU中配置
还有右上角的请求方式,这边是由调用的时候配置,下面就是我调用的时候内容
下面是我json文件,里面的内容按照自己的需要配置改改就好
{
"snmp": [
{
"name": "华为",
"targetIp": "127.0.0.1",
"targetPort": 161,
"community": "community",
"version":1,
"关键注释内容(无其他作用)": "version版本1为0,version版本2为1,version版本3为3",
"timeout": 5,
"userName": "",
"userAuthPassword": "",
"userPrivPassword": "",
"mib": [
{
"node": "hwEntityCpuUsage",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.1.1.5",
"mean": "实体CPU使用率,取值范围:2~100。"
},
{
"node": "hwEntityMemUsage",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.1.1.7",
"mean": "实体内存使用率,取值范围:0~100。"
},
{
"node": "hwEntityMemSize",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.1.1.9",
"mean": "实体内存容量,单位:Byte。"
},
{
"node": "hwEntityTemperature",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.1.1.11",
"mean": "实体温度,读取的是温度传感器测量温度的最高值,单位:°C。"
},
{
"node": "hwEntityOpticalTemperature",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.3.1.5",
"mean": "光模块温度,单位:°C。"
},
{
"node": "hwEntityOpticalVoltage",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.3.1.6",
"mean": "光模块电压,单位:mV。"
},
{
"node": "hwEntityOpticalBiasCurrent",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.3.1.7",
"mean": "光模块偏置电流,单位:uA。"
},
{
"node": "hwEntityOpticalRxPower",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.3.1.8",
"mean": "光模块接收功率,单位:uW,其中uW=(10^(dBM/10))*1000。\nMIB文件中定义此单位是dBm。"
},
{
"node": "hwEntityOpticalTxPower",
"oid":"1.3.6.1.4.1.2011.5.25.31.1.1.3.1.9",
"mean": "光模块发送功率,单位:uW。\nMIB文件中定义此单位是dBm。"
},
{
"node": "ifTable",
"oid":"1.3.6.1.2.1.2.2.1",
"mean": "接口当前接收和发送报文流量统计信息。"
},
{
"node": "ifOperStatus",
"oid":"1.3.6.1.2.1.2.2.1.8",
"mean": "接口当前的状态。"
},
{
"node": "hwIfMonitorInputRate",
"oid":"1.3.6.1.4.1.2011.5.25.41.1.7.1.1.8",
"mean": "入方向带宽占用率"
},
{
"node": "hwIfMonitorOutputRate",
"oid":"1.3.6.1.4.1.2011.5.25.41.1.7.1.1.10",
"mean": "出方向带宽占用率"
},
{
"node": "dot1dTpFdbAddress",
"oid":"1.3.6.1.2.1.17.4.3.1.1",
"mean": "获取设备上所有的MAC Address表项。"
},
{
"node": "hwDynFdbMac",
"oid":"1.3.6.1.4.1.2011.5.25.42.2.1.3.1.1",
"mean": "用于管理设备动态MAC地址表。"
},
{
"node": "hwArpDynTable",
"oid":"1.3.6.1.4.1.2011.5.25.123.1.17.1",
"mean": "获取设备上的动态ARP表项。"
},
{
"node": "hwArpCfgTable",
"oid":"1.3.6.1.4.1.2011.5.25.123.1.18.1",
"mean": "用于设置和查询静态ARP表项。"
},
{
"node": "lldpRemTable",
"oid":"1.0.8802.1.1.2.1.4.1.1",
"mean": "记录LLDP远端邻居信息。"
},
{
"node": "dot1dStpPortState",
"oid":"1.3.6.1.2.1.17.2.15.1.3",
"mean": "标识端口当前的STP状态:\n•1:disabled\n•2:blocking\n•3:listening\n•4:learning\n•5:forwarding\n•6:broken\n"
},
{
"node": "hwRrppRingState",
"oid":"1.3.6.1.4.1.2011.5.25.113.2.2.1.4",
"mean": "环状态。\n当前支持的取值为:\n•1: unknown\n•4: complete\n•5: failed\n•6: linkup\n•7: linkdown\n•8: preforwarding\n•9: linkupnotify\n•10: linkdownnotify\n•11: preforwardnotify\n"
},
{
"node": "vrrpOperState",
"oid":"1.3.6.1.2.1.68.1.3.1.3",
"mean": "虚拟路由器的当前状态。此对象定义了四个值:\n•initialize(1):初始化状态。在此状态下,设备不会对VRRP报文做任何处理。\n•backup(2):备用状态。在此状态下,设备接收Master设备发送的VRRP报文,判断Master设备工作是否正常。\n•master(3):主用状态。在此状态下,设备定期发送VRRP通告报文,并转发目的MAC地址是虚拟MAC地址的IP报文。"
}
]
}
]
}
突然发现忘记加导包的内容了
这个是gradle的导入
//snmp
implementation 'org.snmp4j:snmp4j:2.8.3'
这个是pom的导入
//snmp
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.8.3</version>
</dependency>