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.98CPU 사용률: 12.67