Java를 이용한 SNMP 프로토콜로 서버 정보 수집

Java를 이용한 SNMP 프로토콜로 서버 정보 수집
1. SNMP 소개
SNMP는 IP 네트워크에서 노드(서버, 워크스테이션, 라우터, 스위치 및 허브 등)를 관리하기 위해 특별히 설계된 표준 프로토콜로, 응용 계층 프로토콜입니다. SNMP를 통해 네트워크 관리자는 네트워크 성능을 관리하고, 문제를 발견하며 해결하며, 네트워크 성장을 계획할 수 있습니다. SNMP를 통해 무작위 메시지(및 이벤트 보고서)를 수신함으로써 네트워크 관리 시스템은 네트워크 문제를 인지합니다.
SNMP의 전신은 통신 선로를 관리하기 위해 만들어진 간단 게이트웨이 모니터링 프로토콜(SGMP)입니다. 그 후, SGMP는 크게 수정되었으며, 특히 Internet 정의에 부합하는 SMI와 MIB가 추가되었습니다. 개선된 프로토콜이 바로 유명한 SNMP입니다. TCP/IP 기반의 SNMP 네트워크 관리 프레임워크는 산업 표준으로, 관리 정보 구조(SMI, Structure of Management Information), 관리 정보库(MIB) 및 관리 프로토콜(SNMP)의 세 가지 주요 부분으로 구성됩니다.
- SMI는 SNMP 프레임워크에서 사용되는 정보의 조직 및 식별을 정의하며, MIB에 관리 객체를 정의하고 관리 객체를 사용하기 위한 템플릿을 제공합니다.
- MIB는 SNMP를 통해 접근할 수 있는 관리 객체의 집합을 정의합니다.
- SNMP 프로토콜은 응용 계층 프로토콜로, 네트워크 관리자가 에이전트 프로세스의 MIB 객체를 읽고 쓰는 방법을 정의합니다.
SNMP의 MIB는 트리 구조 데이터베이스이며, MIB가 관리하는 객체는 트리의 끝 노드입니다. 각 노드는 고유한 위치와 고유한 이름을 가집니다. IETF는 관리 정보库 객체 식별자(OID, Object Identifier)가 객체를 고유하게 지정하도록 규정하며, 그 명명 규칙은 부모 노드의 이름이 자식 노드 이름의 접두사가 되는 것입니다.
2. 개통 통합


org.snmp4j
snmp4j
2.7.0
  https://mvnrepository.com/artifact/org.snmp4j/snmp4j
  현재 최신 버전은 3.7.7이며, 여기서는 2.7.0 버전을 예로 들어 설명합니다.
3. 기본 OID
import java.math.BigDecimal;

/**
* snmp 프로토콜 유틸리티 클래스 구성 파일
*/
public class SnmpConfiguration
{
private SnmpConfiguration(){};

/**
* get cpu 설명 정보
*/
public static final String SNMP_GET_CPU_DESCRIPTION = ".1.3.6.1.2.1.1.1.0.255.1";

/**
* walk 네트워크 인터페이스 설명
*/
public static final String SNMP_WALK_IF_DESCRIPTION = ".1.3.6.1.2.1.2.2.1.2";

/**
* walk 인터페이스 물리 주소
*/
public static final String SNMP_WALK_IF_PHYS_ADDRESS = ".1.3.6.1.2.1.2.2.1.6";

/**
* get IO 로드
*/
public static final String SNMP_GET_IO_LOAD = ".1.3.6.1.2.1.1.1.0.255.0";

/**
* 디스크 크기
*/
public static final String SNMP_GET_DISK_TOTAL = ".1.3.6.1.4.1.2021.9.1.6";

/**
* walk cpu 로드
*/
public static final String SNMP_WALK_PROCESS_LOAD = ".1.3.6.1.2.1.25.3.3.1.2";

/**
* walk cpu 유휴율
*/
public static final String SNMP_WALK_CPU_IDLE = ".1.3.6.1.4.1.2021.11.11.0";

/**
* walk 저장 장치 설명
*/
public static final String SNMP_WALK_STORAGE_DESC = ".1.3.6.1.2.1.25.2.3.1.3";
/**
* walk 저장 장치 설명
*/
public static final String SNMP_WALK_ALLOC_UNITS = ".1.3.6.1.2.1.25.2.3.1.4";
/**
* walk 저장 장치 설명
*/
public static final String SNMP_WALK_UNITS_RESERVED = ".1.3.6.1.2.1.25.2.3.1.5";

/**
* walk 저장 장치 사용 크기
* 메모리 사용량은 총 용량으로 나누면 사용률이 됩니다
*/
public static final String SNMP_WALK_STORAGE_USED = ".1.3.6.1.2.1.25.2.3.1.6";
/**
* walk 메모리 사용량
* 각 프로세스가 사용하는 메모리
*/
public static final String SNMP_WALK_MEMORY_WIN = ".1.3.6.1.2.1.25.5.1.1.2";

/**
* walk 저장 장치 사용률
*
*/
public static final String SNMP_WALK_DISK_PERCENT = ".1.3.6.1.4.1.2021.9.1.9";

/**
* get 메모리 크기 가져오기
*/
public static final String SNMP_GET_MEMORY_SIZE = ".1.3.6.1.2.1.25.2.2.0";

/**
* 시스템 이름 가져오기
*/
public static final String SNMP_WALK_SYSTEM_NAME = ".1.3.6.1.2.1.1.5";

/**
* 성능 매개변수 ID - cpu 사용률
*/
public static final Integer PERFORMANCE_PARAM_CPU_USAGE = 1;

/**
* 성능 매개변수 ID - 메모리 사용률
*/
public static final Integer PERFORMANCE_PARAM_MEMORY_USAGE = 2;

/**
* 성능 매개변수 ID - io 대역폭 사용률
*/
public static final Integer PERFORMANCE_PARAM_IO_USAGE = 3;

/**
* decimal 타입 1024, 나눗셈 계산에 사용
*/
public static final BigDecimal DIVIDE_NUMBER = new BigDecimal("1024");

/**
* get 방식으로 메모리 크기 가져오기
*/
public static final String GET_MEMORY = "1.3.6.1.2.1.25.2.2.0";

/**
* 시스템 설명 가져오기
*/
public static final String SYS_DESC = "1.3.6.1.2.1.1.1.0";

/**
* 네트워크 인터페이스 수 가져오기
*/
public static final String IF_NUMBER = "1.3.6.1.2.1.2.1.0";

/**
* cpu 코어 수 가져오기
*/
public static final String CPU_NUMBER = "1.3.6.1.2.1.25.3.3.1.2";

}
4. snmp 정보 수집 유틸리티 클래스
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.List;

/**
* snmp 프로토콜 유틸리티 클래스
* 서버 cpu, 메모리 및 디스크 사용량 가져오기
*/
public class SnmpCollector
{
private static Logger logger = LogManager.getLogger(SnmpCollector.class);
private static Snmp snmp = null;
private CommunityTarget target;

@SuppressWarnings("squid:S3010")
public SnmpCollector(String deviceIp, Integer snmpPort) throws IOException
{
if (snmp == null)
{
snmp = new Snmp(new DefaultUdpTransportMapping());
snmp.listen();
}
// CommunityTarget 초기화
target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
target.setVersion(SnmpConstants.version2c);
target.setAddress(new UdpAddress(deviceIp + "/" + snmpPort));
target.setTimeout(1000);
target.setRetries(1);
}

private ResponseEvent executeGet(String oid)
{
PDU pdu = new PDU();
pdu.addOID(new VariableBinding(new OID(oid)));
ResponseEvent response = null;
try
{
response = snmp.get(pdu, target);
}catch (Exception e){
logger.info("SNMP GET 실행 중 오류 발생: " + e.getMessage());
}
return response;
}

private List executeWalk(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);
}

/**
* cpu 사용률 가져오기
* @return
*/
public BigDecimal getCpuUsage()
{
List list = executeWalk(SnmpConfiguration.SNMP_WALK_PROCESS_LOAD);
BigDecimal usage = new BigDecimal("0");
for (TableEvent tableEvent : list)
{
try
{
String value = tableEvent.toString().split("=")[3].split("]")[0].trim();
if (!"-4,exception".equals(value)){
usage = usage.add(new BigDecimal(value));
} else {
return new BigDecimal("-1");
}
}
catch (Exception e)
{
logger.info("cpu 사용률 가져오기 실패: " + e.getMessage());
return new BigDecimal("-1");
}
}
usage = usage.divide(new BigDecimal(list.size()), 2, BigDecimal.ROUND_HALF_UP);
return usage;
}

/**
* cpu 사용률 가져오기
* @return
*/
public BigDecimal getCpuUtilization()
{
BigDecimal totalSize;
ResponseEvent responseEvent = executeGet(SnmpConfiguration.SNMP_WALK_CPU_IDLE);
if(responseEvent != null && responseEvent.getResponse() != null){
totalSize = new BigDecimal(responseEvent.getResponse().toString().split("=")[4].split("]")[0].trim());
} else {
return new BigDecimal("-1");
}
return new BigDecimal("100").subtract(totalSize);
}

/**
* 디스크 사용률 가져오기 (Windows용)
* @return
*/
public BigDecimal getDiskUsageForWindows()
{
BigDecimal result = new BigDecimal("-1");
try
{
BigDecimal diskSize = getDiskSize();
BigDecimal diskUsed = getDiskUsed();
if (BigDecimal.ZERO.compareTo(diskSize) < 0 && BigDecimal.ZERO.compareTo(diskUsed) < 0){
result = diskUsed.divide(diskSize, 3, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
}
}catch (Exception e){
logger.info("Windows 디스크 사용률 가져오기 오류: " + e.getMessage());
}

return result;
}

/**
* 디스크 사용률 가져오기
* @return
*/
public BigDecimal getDiskUsage()
{
BigDecimal totalSize = null;
// org.snmp4j.util.TableEvent[index=null,vbs=null,status=-1,exception=null,report=null]
List tableEvents = executeWalk(SnmpConfiguration.SNMP_WALK_DISK_PERCENT);
if(tableEvents.isEmpty()){
totalSize = getDiskUsageForWindows();
return totalSize;
}
for(int i = 0; i < tableEvents.size(); i++){
if ("-1,exception".equals(tableEvents.get(i).toString().split("=")[3].split("]")[0].trim())){
return new BigDecimal("-1");
}
String value = tableEvents.get(i).toString().split("=")[3].split("]")[0].trim();
if (!"-4,exception".equals(value)){
totalSize = new BigDecimal(value);
} else {
return new BigDecimal("-1");
}
}

return totalSize;
}

/**
* 메모리 사용률 가져오기
* @return
*/
public BigDecimal getMemoryUsage(){
BigDecimal usage;
BigDecimal totalSize;
try
{
ResponseEvent event = executeGet(SnmpConfiguration.SNMP_GET_MEMORY_SIZE);
if (event != null && event.getResponse() != null)
{
totalSize = new BigDecimal(event.getResponse().toString().split("=")[4].split("]")[0].trim());
usage = getMemoryUsed();
return usage.multiply(new BigDecimal("100")).divide(totalSize, 0, BigDecimal.ROUND_HALF_UP);
}
else
{
return new BigDecimal("-1");
}
}catch (Exception e){
logger.info("메모리 사용률 가져오기 실패: " + e.getMessage());
return new BigDecimal("-1");
}
}

/**
* 메모리 사용량 가져오기
* @return
*/
public BigDecimal getMemoryUsed(){
List list = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_DESC);
int index = 0;
BigDecimal usedMemory = new BigDecimal("0");
for(int i = 0; i < list.size(); i++){
String description = list.get(i).toString().split("=")[3].split("]")[0].trim();
if(description.contains("Physical memory")){
// Linux 시스템
index = i;
List usedList = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_USED);
usedMemory = new BigDecimal(usedList.get(index).toString().split("=")[3].split("]")[0].trim());
} else if (description.contains("Physical Memory")){
// Windows 시스템
List winMemoryList = executeWalk(SnmpConfiguration.SNMP_WALK_MEMORY_WIN);
BigDecimal total = new BigDecimal("0");
for (int j = 0; j < winMemoryList.size(); j++){
String memValue = winMemoryList.get(j).toString().split("=")[3].split("]")[0].trim();
BigDecimal b = new BigDecimal(memValue);
total = total.add(b);
}
return total;
} else if(description.contains("Memory buffers")){
// Linux 시스템
index = i;
List usedList = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_USED);
usedMemory = usedMemory.subtract(new BigDecimal(usedList.get(index).toString().split("=")[3].split("]")[0].trim()));
} else if(description.contains("Cached memory")){
// Linux 시스템
index = i;
List usedList = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_USED);
usedMemory = usedMemory.subtract(new BigDecimal(usedList.get(index).toString().split("=")[3].split("]")[0].trim()));
}
}
return usedMemory;

}

/**
* 디스크 크기 가져오기
* @return
*/
public BigDecimal getDiskSize(){
List list = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_DESC);
BigDecimal diskSize = new BigDecimal(-1);
if (!list.isEmpty()){
diskSize = new BigDecimal("0");
List allocUnitsList = executeWalk(SnmpConfiguration.SNMP_WALK_ALLOC_UNITS);
List unitsReservedList = executeWalk(SnmpConfiguration.SNMP_WALK_UNITS_RESERVED);
for(int i = 0; i < list.size(); i++){
boolean isStorageDevice = list.get(i).toString().split("=")[3].split("]")[0].trim().contains(":") ||
list.get(i).toString().split("=")[3].split("]")[0].trim().contains("/");
if(isStorageDevice){
BigDecimal allocUnits = new BigDecimal(allocUnitsList.get(i).toString().split("=")[3].split("]")[0].trim());
BigDecimal unitsReserved = new BigDecimal(unitsReservedList.get(i).toString().split("=")[3].split("]")[0].trim());
diskSize = diskSize.add(allocUnits.multiply(unitsReserved).divide(new BigDecimal("1073741824"), 2, BigDecimal.ROUND_HALF_UP));
}
}
}
return diskSize;
}

/**
* 디스크 사용량 가져오기
* @return
*/
public BigDecimal getDiskUsed(){
List list = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_DESC);
BigDecimal diskUsed = new BigDecimal(-1);
if (!list.isEmpty()){
diskUsed = new BigDecimal("0");
List allocUnitsList = executeWalk(SnmpConfiguration.SNMP_WALK_ALLOC_UNITS);
List storageUsedList = executeWalk(SnmpConfiguration.SNMP_WALK_STORAGE_USED);
for(int i = 0; i < list.size(); i++){
boolean isStorageDevice = list.get(i).toString().split("=")[3].split("]")[0].trim().contains(":") ||
list.get(i).toString().split("=")[3].split("]")[0].trim().contains("/");
if(isStorageDevice){
BigDecimal allocUnits = new BigDecimal(allocUnitsList.get(i).toString().split("=")[3].split("]")[0].trim());
BigDecimal used = new BigDecimal(storageUsedList.get(i).toString().split("=")[3].split("]")[0].trim());
diskUsed = diskUsed.add(allocUnits.multiply(used).divide(new BigDecimal("1073741824"), 2, BigDecimal.ROUND_HALF_UP));
}
}
}
return diskUsed;
}

/**
* IO 로드 가져오기
* @return
*/
public Float getIOUsage(){
ResponseEvent event = executeGet(SnmpConfiguration.SNMP_GET_IO_LOAD);
if(event != null && event.getResponse() != null){
float usage = Float.parseFloat(event.getResponse().toString().split("=")[4].split("]")[0].trim());
if(usage > 1){
return usage;
} else {
return usage * 100;
}
}
return null;
}

/**
* 특정 성능 매개변수 데이터 가져오기
* @param parameterId
* @return
*/
public Object getPerformanceData(Integer parameterId) {
Object data = null;
try{
if(parameterId.equals(SnmpConfiguration.PERFORMANCE_PARAM_CPU_USAGE)){
data = getCpuUsage();
} else if(parameterId.equals(SnmpConfiguration.PERFORMANCE_PARAM_MEMORY_USAGE)){
data = getMemoryUsage();
} else if(parameterId.equals(SnmpConfiguration.PERFORMANCE_PARAM_IO_USAGE)){
data = getIOUsage();
}
}catch(Exception e){
logger.info("성능 매개변수 가져오기 오류: " + e.getMessage());
}
return data;
}

/**
* 장치 물리 주소 가져오기
* @return
*/
public String getMacAddress(){
try
{
List list = executeWalk(SnmpConfiguration.SNMP_WALK_IF_DESCRIPTION);
int index = 0;
for (int i = 0; i < list.size(); i++)
{
if (list.get(i).toString().split("=")[3].split("]")[0].trim().contains("eth"))
{
index = i;
}
}
List ifAddressList = executeWalk(SnmpConfiguration.SNMP_WALK_IF_PHYS_ADDRESS);
return ifAddressList.get(index).toString().split("=")[3].split("]")[0].trim();
}catch (Exception e){
logger.info("물리 주소 가져오기 실패: " + e.getMessage());
return "failure";
}
}

/**
* 장치 메모리 크기 가져오기 (단위: GB)
* @return
*/
public String getMemoryDescription() {
ResponseEvent event = executeGet(SnmpConfiguration.SNMP_GET_MEMORY_SIZE);
Long bytes = Long.parseLong(event == null ? "0" : event.getResponse().toString().split("=")[4].split("]")[0].trim());
float gb = bytes / 1024.0f / 1024.0f;
DecimalFormat decimalFormat = new DecimalFormat(".00");
return decimalFormat.format(gb) + "GB";
}

/**
* @throws IOException
* cpu 설명 정보 가져오기
* @return
*/
public String getCpuDescription() throws IOException {
ResponseEvent event = executeGet(SnmpConfiguration.SNMP_GET_CPU_DESCRIPTION);
return event == null ? "" : event.getResponse().toString().split("=")[4].split("]")[0].trim().split(":")[1].trim();
}

/**
* 저장 장치 크기 가져오기 (남은 공간이 부족한 경우)
* @param deviceCode
* @return
*/
public BigDecimal getDiskSize(String deviceCode){

List list = executeWalk(SnmpConfiguration.SNMP_GET_DISK_TOTAL);
BigDecimal diskSize = new BigDecimal(-1);
if (list != null){
try
{
for (TableEvent tableEvent : list){
diskSize = new BigDecimal(tableEvent.toString().split("=")[3].split("]")[0].trim()).divide(SnmpConfiguration.DIVIDE_NUMBER)
.divide(SnmpConfiguration.DIVIDE_NUMBER, 2, RoundingMode.HALF_UP);
}
}catch (Exception e){
logger.info("디스크 크기 가져오기 실패: " + e.getMessage());
}
}
if (BigDecimal.ZERO.compareTo(diskSize) > 0){
diskSize = getDiskSize();
}
return diskSize;
}


/**
* 디스크 설명 가져오기
* @param deviceCode
* @return
* @throws IOException
*/
public BigDecimal getDiskDescription(String deviceCode) {
return getDiskSize(deviceCode).divide(SnmpConfiguration.DIVIDE_NUMBER, 2, RoundingMode.HALF_UP)
.divide(SnmpConfiguration.DIVIDE_NUMBER, 2, RoundingMode.HALF_UP);
}

/**
* snmp 프로토콜 연결 확인 */
public boolean checkSnmpConnection() {
ResponseEvent response = executeGet(".1.3.6.1.4.1.2021.255.1");
return response == null || response.getResponse() != null;
}

/**
* 시스템 이름 가져오기
* @return
*/
public String getSystemName(){
String name = null;
try{
List list = executeWalk(SnmpConfiguration.SNMP_WALK_SYSTEM_NAME);
if (list != null){
for (TableEvent tableEvent : list){
name = (tableEvent.toString().split("=")[3].split("]")[0].trim());
}
}
}catch (Exception e){
logger.info("시스템 이름 가져오기 실패: " + e.getMessage());
}
return name;
}

/**
* 메모리 총 크기 가져오기 (실제 값)
* @return
*/
public BigDecimal getTotalMemorySize(){
try
{
ResponseEvent event = executeGet(SnmpConfiguration.GET_MEMORY);
if(event != null && event.getResponse() != null){
String value = event.getResponse().toString().split("=")[4].split("]")[0].trim();
return new BigDecimal(value).divide(SnmpConfiguration.DIVIDE_NUMBER, 2, RoundingMode.HALF_UP)
.divide(SnmpConfiguration.DIVIDE_NUMBER, 2, RoundingMode.HALF_UP);
} else {
throw new RuntimeException("메모리 정보 가져오기 실패");
}
}
catch (Exception e)
{
logger.info("메모리 총 크기 가져오기 실패: " + e.getMessage());
}
return new BigDecimal("-1");
}

/**
* 시스템 설명 가져오기
* @return
*/
public String getSystemDescription(){
try
{
ResponseEvent event = executeGet(SnmpConfiguration.SYS_DESC);
if(event != null && event.getResponse() != null){
return event.getResponse().toString().split("=")[4].split("]")[0].trim();
} else {
throw new RuntimeException("시스템 설명 정보 가져오기 실패");
}
}
catch (Exception e)
{
logger.info("시스템 설명 정보 가져오기 실패: " + e.getMessage());
}
return "failure";
}

/**
* 인터페이스 수 가져오기
* @return
*/
public Integer getInterfaceCount(){
try
{
ResponseEvent event = executeGet(SnmpConfiguration.IF_NUMBER);
if(event != null && event.getResponse() != null){
String value = event.getResponse().toString().split("=")[4].split("]")[0].trim();
return Integer.parseInt(value);
} else {
throw new RuntimeException("인터페이스 정보 가져오기 실패");
}
}
catch (Exception e)
{
logger.info("인터페이스 수 가져오기 실패: " + e.getMessage());
}
return -1;
}

/**
* cpu 코어 수 가져오기 (n개의 cpu는 n개의 데이터)
* @return
*/
public Integer getCpuCoreCount(){
List list = executeWalk(SnmpConfiguration.CPU_NUMBER);
Integer count = -1;
if (list != null){
count = list.size();
}
return count;
}


public static void main(String[] args) throws IOException
{
String deviceIp = "127.0.0.1";
Integer snmpPort = 161;
SnmpCollector collector = new SnmpCollector(deviceIp, snmpPort);

System.out.println("시스템 이름: " + collector.getSystemName());
System.out.println("디스크 크기: " + collector.getDiskSize("device"));
System.out.println("물리 주소: " + collector.getMacAddress());
System.out.println("CPU 사용률: " + collector.getCpuUsage());
}
}
5. 결과 표시
시스템 이름: localhost
디스크 크기: 49.98
CPU 사용률: 12.67

태그: snmp java 서버 모니터링 네트워크 관리 MIB

6월 11일 21:41에 게시됨