diff --git a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java index 50d8db1..404153a 100644 --- a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java +++ b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java @@ -71,6 +71,8 @@ public enum MsgEnum { macvlan状态上报("MACVLAN_STATUS_RSP"), + iops结果上报("IOPS_RESULT"), + 多网IP探测上报("NETWORK_DETECT"); private String value; diff --git a/src/main/java/com/tongran/agent/client/core/vo/DiskVO.java b/src/main/java/com/tongran/agent/client/core/vo/DiskVO.java index adb67d4..4e272c7 100644 --- a/src/main/java/com/tongran/agent/client/core/vo/DiskVO.java +++ b/src/main/java/com/tongran/agent/client/core/vo/DiskVO.java @@ -45,6 +45,10 @@ public class DiskVO implements Serializable { @Schema(description = "时间戳") private long timestamp; + @Schema(description = "磁盘类型") + private String type; + @Schema(description = "已用空间") + private long usedSpace; } diff --git a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java index 0b3fced..67bcf0e 100644 --- a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java +++ b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java @@ -907,8 +907,157 @@ public class AgentServiceImpl implements AgentService { PppoeEO pppoeEO = JSON.parseObject(pppoeConfig, PppoeEO.class); handlePppoeConfig(pppoeEO); } + if(jsonObject.containsKey("diskIopsTest")){ + String diskName = jsonObject.getString("diskIopsTest"); + handleDiskIops(diskName); + } + if(jsonObject.containsKey("umountDisk")){ + String diskName = jsonObject.getString("umountDisk"); + handleUmountDisk(diskName); + } } + + private void handleUmountDisk(String diskName) { + try { + // 查询所有挂载点 + String[] grepCmd = {"sh", "-c", "mount | grep '" + diskName + "' | awk '{print $1}'"}; + ProcessBuilder grepPb = new ProcessBuilder(grepCmd); + Process grepProcess = grepPb.start(); + + BufferedReader grepReader = new BufferedReader(new InputStreamReader(grepProcess.getInputStream())); + String partition; + List partitions = new ArrayList<>(); + + // 收集所有分区 + while ((partition = grepReader.readLine()) != null) { + partition = partition.trim(); + if (!partition.isEmpty() && partition.startsWith("/dev/")) { + partitions.add(partition); + } + } + + grepReader.close(); + grepProcess.waitFor(); + + // 如果没有分区,直接返回 + if (partitions.isEmpty()) { + AssertLog.info("没有找到与{}相关的挂载分区", diskName); + return; + } + + // 卸载所有分区 + for (String part : partitions) { + try { + AssertLog.info("正在卸载:{}", part); + ProcessBuilder umountPb = new ProcessBuilder("umount", part); + Process umountProcess = umountPb.start(); + int exitCode = umountProcess.waitFor(); + + if (exitCode == 0) { + AssertLog.info("成功卸载: {}", part); + } else { + // 尝试强制卸载 + ProcessBuilder forcePb = new ProcessBuilder("umount", "-f", part); + Process forceProcess = forcePb.start(); + int forceCode = forceProcess.waitFor(); + + if (forceCode == 0) { + AssertLog.info("强制卸载成功:{} ", part); + } else { + AssertLog.info("卸载失败: ", part); + } + } + } catch (Exception e) { + AssertLog.info("卸载 {} 时出错: {}",diskName, e.getMessage()); + } + } + + } catch (IOException | InterruptedException e) { + AssertLog.info("卸载 {} 时出错: {}",diskName, e.getMessage()); + } + } + + private void handleDiskIops(String diskName) { + JSONObject json = new JSONObject(); + AssertLog.info("开始测试磁盘{}的iops", diskName); + // 使用CompletableFuture并行执行 + CompletableFuture readFuture = CompletableFuture.supplyAsync(() -> { + return testIops(diskName, "read"); + }); + + CompletableFuture writeFuture = CompletableFuture.supplyAsync(() -> { + return testIops(diskName, "write"); + }); + + try { + long timestamp = System.currentTimeMillis(); + timestamp = Math.round(timestamp / 1000.0); + // 等待两个测试都完成(最多70秒) + String readIops = readFuture.get(70, TimeUnit.SECONDS); + String writeIops = writeFuture.get(70, TimeUnit.SECONDS); + + json.put("name", diskName); + json.put("readIops", readIops); + json.put("writeIops", writeIops); + json.put("timestamp",timestamp); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.iops结果上报.getValue()) + .data(json.toString()).build(); + sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); + AssertLog.info("发送iops结果上报应答={}",JSON.toJSONString(message)); + } + + } catch (Exception e) { + AssertLog.info("测试磁盘IOPS失败:{}", e.getMessage()); + } + } + + private String testIops(String diskName, String rwType) { + try { + String cmd = String.format( + "fio --name=tt1 --filename=/dev/%s --size=1G --bs=4k --rw=%s --ioengine=libaio --runtime=60 --direct=1 --time_based --group_reporting", + diskName, rwType + ); + + Process process = Runtime.getRuntime().exec(new String[]{"bash", "-c", cmd}); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + StringBuilder output = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + + process.waitFor(); + reader.close(); + + return parseIops(output.toString()); + + } catch (Exception e) { + AssertLog.error("测试磁盘IOPS失败:{}", e.getMessage()); + return null; + } + } + + private String parseIops(String output) { + // 示例:" read: IOPS=12.7k, BW=49.6MiB/s (52.1MB/s)(2979MiB/60001msec)" + String[] lines = output.split("\n"); + for (String line : lines) { + if (line.trim().startsWith("read:") || line.trim().startsWith("write:")) { + // 查找 IOPS= 部分 + String[] parts = line.split("IOPS="); + if (parts.length > 1) { + String iopsPart = parts[1].split(",")[0]; + return iopsPart.trim(); + } + } + } + + return null; + } + /** * 处理PPPoE配置 * @param pppoeEO PPPoE配置对象 diff --git a/src/main/java/com/tongran/agent/client/service/impl/DiskServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/DiskServiceImpl.java index e88baac..f70d952 100644 --- a/src/main/java/com/tongran/agent/client/service/impl/DiskServiceImpl.java +++ b/src/main/java/com/tongran/agent/client/service/impl/DiskServiceImpl.java @@ -9,9 +9,9 @@ import oshi.SystemInfo; import oshi.hardware.HWDiskStore; import oshi.software.os.OSFileStore; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.*; @Service public class DiskServiceImpl implements DiskService { @@ -19,29 +19,47 @@ public class DiskServiceImpl implements DiskService { public List diskList(long timestamp) { List tempList = new ArrayList<>(); List resultList = new ArrayList<>(); + Map typeMap = getDiskTypeMap(); + Map usedSpaceMap = getDiskUsedSpaceMap(); SystemInfo si = new SystemInfo(); System.out.println("========================================================="); + // 获取磁盘IO信息 System.out.println("\n=== 磁盘IO信息 ==="); for (HWDiskStore disk : si.getHardware().getDiskStores()) { DiskVO diskVO = DiskVO.builder().timestamp(timestamp).build(); - diskVO.setName(disk.getName());//磁盘名称 - diskVO.setSerial(disk.getSerial());//序列号 - diskVO.setTotal(disk.getSize());//磁盘大小 - diskVO.setWriteTimes(disk.getWrites());//磁盘写入次数 - diskVO.setReadTimes(disk.getReads());//磁盘读取次数 - diskVO.setWriteBytes(disk.getReadBytes());//磁盘写入字节 - diskVO.setReadBytes(disk.getWriteBytes());//磁盘读取字节 + + // 处理磁盘名称:去掉/dev/前缀 + String diskFullName = disk.getName(); + String diskName = diskFullName.replace("/dev/", ""); + diskVO.setName(diskName); // 保持原样存储 + diskVO.setSerial(disk.getSerial()); // 序列号 + diskVO.setTotal(disk.getSize()); // 磁盘大小 + diskVO.setWriteTimes(disk.getWrites()); // 磁盘写入次数 + diskVO.setReadTimes(disk.getReads()); // 磁盘读取次数 + diskVO.setWriteBytes(disk.getReadBytes()); // 磁盘写入字节 + diskVO.setReadBytes(disk.getWriteBytes()); // 磁盘读取字节 + + // 获取磁盘类型和已用空间(使用不带/dev/前缀的名称) + String type = typeMap.get(diskName); + Long usedSpace = usedSpaceMap.get(diskName); + + diskVO.setType(type != null ? type : "UNKNOWN"); + diskVO.setUsedSpace(usedSpace != null ? usedSpace : 0L); + tempList.add(diskVO); System.out.println("磁盘名称: " + diskVO.getName()); System.out.println("序列号: " + diskVO.getSerial()); + System.out.println("磁盘类型: " + diskVO.getType()); + System.out.println("已用空间: " + diskVO.getUsedSpace()); System.out.println("磁盘大小: " + diskVO.getTotal()); System.out.println("磁盘写入次数: " + diskVO.getWriteTimes()); System.out.println("磁盘读取次数: " + diskVO.getReadTimes()); System.out.println("磁盘写入字节: " + diskVO.getWriteBytes()); System.out.println("磁盘读取字节: " + diskVO.getReadBytes()); } + try{ // 第一次采样 List disks1 = si.getHardware().getDiskStores(); @@ -64,6 +82,8 @@ public class DiskServiceImpl implements DiskService { diskVO.setWriteSpeed(readDiff);//磁盘写入速率 diskVO.setReadSpeed(writeDiff);//磁盘读取速率 System.out.println("磁盘名称: " + diskVO.getName()); + System.out.println("磁盘类型: " + diskVO.getType()); + System.out.println("已用空间: " + diskVO.getUsedSpace()); System.out.println("磁盘写入速率: " + diskVO.getWriteSpeed()); System.out.println("磁盘读取速率: " + diskVO.getReadSpeed()); } @@ -75,6 +95,128 @@ public class DiskServiceImpl implements DiskService { return resultList; } + /** + * 获取磁盘类型映射(设备名 -> 类型) + */ + public Map getDiskTypeMap() { + Map diskTypeMap = new HashMap<>(); + try { + ProcessBuilder pb = new ProcessBuilder("lsblk", "-d", "-o", "name,rota"); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + reader.readLine(); // 跳过标题行 + while ((line = reader.readLine()) != null) { + String[] parts = line.trim().split("\\s+"); + if (parts.length >= 2) { + String diskName = parts[0]; + String rota = parts[1]; + String type = "1".equals(rota) ? "HDD" : "SSD"; + + diskTypeMap.put(diskName, type); + } + } + process.waitFor(); + reader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return diskTypeMap; + } + + /** + * 获取磁盘已用空间映射(设备名 -> 已用字节数) + * 使用lsblk获取准确的磁盘-分区关系 + */ + public Map getDiskUsedSpaceMap() { + Map diskUsedMap = new HashMap<>(); + Map partitionToDisk = getPartitionToDiskMap(); + + try { + ProcessBuilder pb = new ProcessBuilder("df", "-B1", "--output=source,used"); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + reader.readLine(); // 跳过标题行 + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("Filesystem")) { + continue; + } + + String[] parts = line.split("\\s+"); + if (parts.length >= 2 && parts[0].startsWith("/dev/")) { + String device = parts[0]; + String usedStr = parts[1]; + + try { + long used = Long.parseLong(usedStr); + String deviceName = device.replace("/dev/", ""); + + // 通过lsblk获取的映射关系找到对应的物理磁盘 + String diskName = partitionToDisk.get(deviceName); + if (diskName == null) { + // 如果没有找到映射,可能是磁盘本身(不是分区) + diskName = deviceName; + } + + if (diskName != null) { + diskUsedMap.merge(diskName, used, Long::sum); + } + } catch (NumberFormatException ignored) { + } + } + } + process.waitFor(); + reader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return diskUsedMap; + } + + /** + * 使用lsblk获取磁盘-分区映射关系 + * @return Map<分区设备名, 物理磁盘设备名> + */ + private Map getPartitionToDiskMap() { + Map partitionToDiskMap = new HashMap<>(); + try { + ProcessBuilder pb = new ProcessBuilder("lsblk", "-l", "-o", "name,type"); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + reader.readLine(); // 跳过标题行 + String currentDisk = null; + + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; + + String[] parts = line.split("\\s+"); + if (parts.length >= 2) { + String device = parts[0]; + String type = parts[1]; + + if ("disk".equals(type)) { + // 这是物理磁盘 + currentDisk = device; + } else if ("part".equals(type) && currentDisk != null) { + // 这是分区,关联到当前磁盘 + partitionToDiskMap.put(device, currentDisk); + } + } + } + process.waitFor(); + reader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return partitionToDiskMap; + } @Override public List pointList(long timestamp) { List list = new ArrayList<>();