优化磁盘健康信息采集
This commit is contained in:
@@ -15,6 +15,16 @@ import java.util.*;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DiskServiceImpl implements DiskService {
|
public class DiskServiceImpl implements DiskService {
|
||||||
|
// 定义需要排除的虚拟设备前缀列表
|
||||||
|
private static final String[] VIRTUAL_DISK_PREFIXES = {
|
||||||
|
"/dev/dm", // device-mapper (LVM/加密/RAID)
|
||||||
|
"/dev/nbd", // 网络块设备
|
||||||
|
"/dev/loop", // 回环设备
|
||||||
|
"/dev/ram", // RAM磁盘
|
||||||
|
"/dev/drbd", // DRBD
|
||||||
|
"/dev/vd", // VirtIO (KVM)
|
||||||
|
"/dev/xvd" // Xen
|
||||||
|
};
|
||||||
@Override
|
@Override
|
||||||
public List<DiskVO> diskList(long timestamp) {
|
public List<DiskVO> diskList(long timestamp) {
|
||||||
List<DiskVO> tempList = new ArrayList<>();
|
List<DiskVO> tempList = new ArrayList<>();
|
||||||
@@ -37,7 +47,16 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
int score = getNvmeHealthScore(diskFullName);
|
int score = getNvmeHealthScore(diskFullName);
|
||||||
diskVO.setHealthScore(score);
|
diskVO.setHealthScore(score);
|
||||||
}
|
}
|
||||||
if (!diskFullName.startsWith("/dev/dm")) {
|
// 在判断中使用
|
||||||
|
boolean isVirtualDisk = false;
|
||||||
|
for (String prefix : VIRTUAL_DISK_PREFIXES) {
|
||||||
|
if (diskFullName.startsWith(prefix)) {
|
||||||
|
isVirtualDisk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isVirtualDisk) {
|
||||||
diskVO.setHealthStatus(isHealth);
|
diskVO.setHealthStatus(isHealth);
|
||||||
}
|
}
|
||||||
String diskName = diskFullName.replace("/dev/", "");
|
String diskName = diskFullName.replace("/dev/", "");
|
||||||
@@ -252,6 +271,141 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int getNvmeHealthScore(String diskDevice) {
|
public int getNvmeHealthScore(String diskDevice) {
|
||||||
|
int nvmeScore = getNvmeHealthScoreByNvmeCli(diskDevice);
|
||||||
|
if (nvmeScore >= 0) {
|
||||||
|
return nvmeScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退到 smartctl
|
||||||
|
return getNvmeHealthScoreBySmartctl(diskDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNvmeHealthScoreByNvmeCli(String diskDevice) {
|
||||||
|
try {
|
||||||
|
Process process = new ProcessBuilder("nvme", "smart-log", diskDevice)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream()))) {
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
return -1; // 命令执行失败
|
||||||
|
}
|
||||||
|
|
||||||
|
int percentageUsed = -1;
|
||||||
|
int availableSpare = -1;
|
||||||
|
String line;
|
||||||
|
|
||||||
|
// 解析 nvme smart-log 输出
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理下划线和空格两种格式
|
||||||
|
if (isPercentageUsedLine(line)) {
|
||||||
|
percentageUsed = extractNvmeValueFromLine(line);
|
||||||
|
}
|
||||||
|
// 处理 available spare,排除 threshold
|
||||||
|
else if (isAvailableSpareLine(line)) {
|
||||||
|
availableSpare = extractNvmeValueFromLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算健康分数
|
||||||
|
if (percentageUsed >= 0) {
|
||||||
|
// 健康分数 = 100 - 已用百分比
|
||||||
|
return 100 - percentageUsed;
|
||||||
|
} else if (availableSpare >= 0) {
|
||||||
|
// 如果没有 Percentage Used,使用 Available Spare
|
||||||
|
return availableSpare;
|
||||||
|
} else {
|
||||||
|
return -1; // 无法获取健康信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPercentageUsedLine(String line) {
|
||||||
|
String lowerLine = line.toLowerCase();
|
||||||
|
|
||||||
|
// 支持多种格式:
|
||||||
|
// 1. percentage_used : 0%
|
||||||
|
// 2. percentage used : 0%
|
||||||
|
// 3. Percentage Used: 0%
|
||||||
|
// 4. percentage used: 0%
|
||||||
|
|
||||||
|
// 移除所有空格和下划线,统一比较
|
||||||
|
String unified = lowerLine.replaceAll("[_\\s]", "");
|
||||||
|
return unified.startsWith("percentageused");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAvailableSpareLine(String line) {
|
||||||
|
String lowerLine = line.toLowerCase();
|
||||||
|
|
||||||
|
// 排除 threshold 行
|
||||||
|
if (lowerLine.contains("threshold")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持多种格式:
|
||||||
|
// 1. available_spare : 100%
|
||||||
|
// 2. available spare : 100%
|
||||||
|
// 3. Available Spare: 100%
|
||||||
|
// 4. available spare: 100%
|
||||||
|
|
||||||
|
// 移除所有空格和下划线,统一比较
|
||||||
|
String unified = lowerLine.replaceAll("[_\\s]", "");
|
||||||
|
return unified.startsWith("availablespare");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int extractNvmeValueFromLine(String line) {
|
||||||
|
try {
|
||||||
|
// 按冒号分割
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
String valuePart = parts[1].trim();
|
||||||
|
|
||||||
|
// 移除可能存在的逗号(如 1,234% -> 1234%)
|
||||||
|
valuePart = valuePart.replace(",", "");
|
||||||
|
|
||||||
|
// 查找百分比符号
|
||||||
|
int percentIndex = valuePart.indexOf('%');
|
||||||
|
if (percentIndex != -1) {
|
||||||
|
// 提取%前面的数字部分
|
||||||
|
String numberStr = valuePart.substring(0, percentIndex).trim();
|
||||||
|
// 移除所有非数字字符
|
||||||
|
numberStr = numberStr.replaceAll("[^0-9]", "").trim();
|
||||||
|
if (!numberStr.isEmpty()) {
|
||||||
|
return Integer.parseInt(numberStr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有百分号,尝试解析第一个数字
|
||||||
|
String[] tokens = valuePart.split("\\s+");
|
||||||
|
if (tokens.length > 0) {
|
||||||
|
String numberStr = tokens[0].trim();
|
||||||
|
// 移除所有非数字字符
|
||||||
|
numberStr = numberStr.replaceAll("[^0-9]", "").trim();
|
||||||
|
if (!numberStr.isEmpty()) {
|
||||||
|
return Integer.parseInt(numberStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 解析失败
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int getNvmeHealthScoreBySmartctl(String diskDevice) {
|
||||||
try {
|
try {
|
||||||
// 检查是否为 NVMe 设备
|
// 检查是否为 NVMe 设备
|
||||||
if (!diskDevice.startsWith("/dev/nvme")) {
|
if (!diskDevice.startsWith("/dev/nvme")) {
|
||||||
@@ -276,29 +430,18 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
|
|
||||||
// 解析 smartctl 输出
|
// 解析 smartctl 输出
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 查找 Percentage Used(已用百分比)
|
// 查找 Percentage Used(已用百分比)
|
||||||
if (line.contains("Percentage Used:")) {
|
if (line.contains("Percentage Used:")) {
|
||||||
String[] parts = line.split(":");
|
percentageUsed = extractSmartctlValueFromLine(line);
|
||||||
if (parts.length > 1) {
|
|
||||||
String value = parts[1].trim().split("\\s+")[0];
|
|
||||||
try {
|
|
||||||
percentageUsed = Integer.parseInt(value.replace("%", ""));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// 忽略解析错误
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 查找 Available Spare(可用备用空间)
|
// 查找 Available Spare(可用备用空间)
|
||||||
else if (line.contains("Available Spare:")) {
|
else if (line.contains("Available Spare:") && !line.contains("Available Spare Threshold")) {
|
||||||
String[] parts = line.split(":");
|
availableSpare = extractSmartctlValueFromLine(line);
|
||||||
if (parts.length > 1) {
|
|
||||||
String value = parts[1].trim().split("\\s+")[0];
|
|
||||||
try {
|
|
||||||
availableSpare = Integer.parseInt(value.replace("%", ""));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// 忽略解析错误
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +455,6 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
} else {
|
} else {
|
||||||
return -1; // 无法获取健康信息
|
return -1; // 无法获取健康信息
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -320,6 +462,35 @@ public class DiskServiceImpl implements DiskService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int extractSmartctlValueFromLine(String line) {
|
||||||
|
try {
|
||||||
|
// 按冒号分割
|
||||||
|
String[] parts = line.split(":");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
String valuePart = parts[1].trim();
|
||||||
|
|
||||||
|
// 处理格式: "Percentage Used: 5%"
|
||||||
|
if (valuePart.contains("%")) {
|
||||||
|
String[] valueParts = valuePart.split("\\s+");
|
||||||
|
for (String part : valueParts) {
|
||||||
|
if (part.endsWith("%")) {
|
||||||
|
return Integer.parseInt(part.substring(0, part.length() - 1).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 处理格式: "Percentage Used: 5"
|
||||||
|
String[] valueParts = valuePart.split("\\s+");
|
||||||
|
if (valueParts.length > 0) {
|
||||||
|
return Integer.parseInt(valueParts[0].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 解析失败
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PointVO> pointList(long timestamp) {
|
public List<PointVO> pointList(long timestamp) {
|
||||||
List<PointVO> list = new ArrayList<>();
|
List<PointVO> list = new ArrayList<>();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ spring:
|
|||||||
matching-strategy: ant_path_matcher
|
matching-strategy: ant_path_matcher
|
||||||
application:
|
application:
|
||||||
name: tr-agent-client
|
name: tr-agent-client
|
||||||
version: 1.1.14
|
version: 1.1.16
|
||||||
conf-path: /usr/local/tongran/conf
|
conf-path: /usr/local/tongran/conf
|
||||||
script-path: /usr/local/tongran/sbin
|
script-path: /usr/local/tongran/sbin
|
||||||
tmp-path: /usr/local/tongran/tmp
|
tmp-path: /usr/local/tongran/tmp
|
||||||
|
|||||||
Reference in New Issue
Block a user