mtr探测优化、临时路由添加异常完善

This commit is contained in:
gaoyutao
2025-12-02 18:42:59 +08:00
parent aa4af48c7a
commit 8ae81db4e8
6 changed files with 620 additions and 99 deletions
@@ -9,11 +9,9 @@ import java.util.List;
public class MtrVO {
private String targetIp; // 目标IP
private String clientId; // 对应的clientId
private Long policyId; // 策略ID
private double firstLossPercent; // 第一次探测丢包率
private double finalLossPercent; // 最终丢包率
private long timestamp; // 探测时间戳
private boolean hasRetry; // 是否重试
private String errorMsg; // 错误信息
private List<HopInfoVO> hopInfos; // 每跳路由信息列表
}
@@ -0,0 +1,71 @@
package com.tongran.agent.client.scheduler.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* MTR线程池配置(优化版)
*/
@Configuration
public class MtrThreadPoolConfig {
@Bean("mtrExecutor")
public ExecutorService mtrExecutor() {
int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; // 根据CPU核心数动态设置
int maxPoolSize = corePoolSize * 2;
int queueCapacity = 200;
long keepAliveTime = 30L; // 缩短空闲线程存活时间
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity),
new MtrThreadFactory(),
new MtrRejectedExecutionHandler() // 自定义拒绝策略
);
// 允许核心线程超时销毁
executor.allowCoreThreadTimeOut(true);
return executor;
}
/**
* MTR线程工厂
*/
private static class MtrThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix = "mtr-worker-";
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
thread.setDaemon(false); // 设置为非守护线程
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
}
/**
* MTR拒绝策略
*/
private static class MtrRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录拒绝的线程信息
System.err.println("MTR任务被拒绝,线程池状态: " +
"活跃线程数: " + executor.getActiveCount() +
", 队列大小: " + executor.getQueue().size());
// 使用调用者线程执行,避免任务丢失
if (!executor.isShutdown()) {
r.run();
}
}
}
}
@@ -1,33 +1,57 @@
package com.tongran.agent.client.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.eo.MtrPolicyConfigEO;
import com.tongran.agent.client.core.vo.HopInfoVO;
import com.tongran.agent.client.core.vo.MtrVO;
import com.tongran.agent.client.core.vo.NetVO;
import com.tongran.agent.client.service.MtrService;
import com.tongran.agent.client.utils.AgentUtil;
import com.tongran.agent.client.utils.AssertLog;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.util.FormatUtil;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* MTR探测服务实现类
* 支持异步批量探测,解析每一跳的路由信息
*/
@Service
public class MtrServiceImpl implements MtrService {
private static final int MTR_TIMEOUT_MINUTES = 3;
private static final int RETRY_DELAY_MS = 30000;
private static final double LOSS_PERCENT_THRESHOLD = 5.0;
private static final int MTR_PACKET_COUNT = 10;
// 内网IP地址段正则表达式
private static final Pattern PRIVATE_IP_PATTERN = Pattern.compile(
"^(10\\.|172\\.(1[6-9]|2[0-9]|3[0-1])\\.|192\\.168\\.|127\\.|169\\.254\\.|0\\.|255\\.)"
);
private final OnlineIPQueryService onlineIPQueryService;
private final ExecutorService mtrExecutor;
// 通过构造器注入线程池
public MtrServiceImpl(OnlineIPQueryService onlineIPQueryService,
@Qualifier("mtrExecutor") ExecutorService mtrExecutor) {
this.onlineIPQueryService = onlineIPQueryService;
this.mtrExecutor = mtrExecutor;
}
/**
* 执行MTR探测列表 - 异步版本
* @param timestamp 探测时间戳
* @param mtrPolicyConfigEOList 探测策略配置列表
* @return MTR探测结果列表
*/
@Override
public List<MtrVO> mtrList(long timestamp, List<MtrPolicyConfigEO> mtrPolicyConfigEOList) {
List<CompletableFuture<MtrVO>> futures = new ArrayList<>();
List<MtrVO> mtrResults = new ArrayList<>();
@@ -37,37 +61,23 @@ public class MtrServiceImpl implements MtrService {
return mtrResults;
}
// 为每个IP创建异步任务
// 为每个IP创建异步探测任务
for (MtrPolicyConfigEO policy : mtrPolicyConfigEOList) {
if (!isPolicyTimeValid(policy, timestamp)) {
continue;
}
for (Map.Entry<String, List<String>> entry : policy.getClientIdToIpsMap().entrySet()) {
String clientId = entry.getKey();
List<String> ips = entry.getValue();
if (ips != null) {
for (String ip : ips) {
String targetIp = ip.trim();
if (!targetIp.isEmpty()) {
// 异步执行每个IP的探测
CompletableFuture<MtrVO> future = CompletableFuture.supplyAsync(() ->
executeSingleMtrProbe(targetIp, clientId, timestamp)
);
futures.add(future);
}
}
}
}
createMtrTasksForPolicy(policy, timestamp, futures);
}
// 等待所有任务完成
// 等待所有异步任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
// 获取所有结果(设置超时时间,比如2分钟)
allFutures.get(2, TimeUnit.MINUTES);
// 设置超时时间
allFutures.get(MTR_TIMEOUT_MINUTES, TimeUnit.MINUTES);
// 收集所有结果
for (CompletableFuture<MtrVO> future : futures) {
MtrVO result = future.get();
if (result != null) {
@@ -82,20 +92,59 @@ public class MtrServiceImpl implements MtrService {
return mtrResults;
}
/**
* 为策略创建MTR探测任务
*/
private void createMtrTasksForPolicy(MtrPolicyConfigEO policy, long timestamp,
List<CompletableFuture<MtrVO>> futures) {
for (Map.Entry<String, List<String>> entry : policy.getClientIdToIpsMap().entrySet()) {
String clientId = entry.getKey();
List<String> ips = entry.getValue();
if (ips != null) {
for (String ip : ips) {
String targetIp = ip.trim();
if (!targetIp.isEmpty() && !isPrivateIp(targetIp)) {
CompletableFuture<MtrVO> future = CompletableFuture.supplyAsync(() ->
executeSingleMtrProbe(targetIp, clientId, timestamp), mtrExecutor
);
futures.add(future);
} else if (isPrivateIp(targetIp)) {
AssertLog.info("跳过内网IP探测: {}", targetIp);
}
}
}
}
}
/**
* 检查是否为内网IP
*/
private boolean isPrivateIp(String ip) {
if (ip == null || ip.isEmpty()) {
return true;
}
// 检查常见的非IP地址情况
if (ip.equals("???") || ip.equals("*") || ip.equals("localhost")) {
return true;
}
// 使用正则表达式匹配内网IP段
return PRIVATE_IP_PATTERN.matcher(ip).find();
}
/**
* 检查策略时间是否有效
*/
private boolean isPolicyTimeValid(MtrPolicyConfigEO policy, long currentTimestamp) {
try {
// 将时间戳转换为Date对象进行比较
Date currentTime = new Date(currentTimestamp * 1000);
Date startTime = policy.getStartTime();
Date endTime = policy.getEndTime();
// 开始时间为空表示立即开始
// 开始时间为空表示立即开始,结束时间为空表示永不过期
boolean startValid = startTime == null || !currentTime.before(startTime);
// 结束时间为空表示永不过期
boolean endValid = endTime == null || !currentTime.after(endTime);
return startValid && endValid;
@@ -109,83 +158,73 @@ public class MtrServiceImpl implements MtrService {
* 执行单个IP的MTR探测
*/
private MtrVO executeSingleMtrProbe(String targetIp, String clientId, long timestamp) {
// 构建IP到clientId的反向映射
MtrVO mtrVO = new MtrVO();
mtrVO.setClientId(clientId);
mtrVO.setTargetIp(targetIp);
mtrVO.setTimestamp(timestamp);
MtrVO mtrVO = createMtrVO(targetIp, clientId, timestamp);
try {
// 第一次MTR探测
double firstLossPercent = executeMtrCommand(targetIp);
List<HopInfoVO> firstHops = executeMtrCommand(targetIp);
double firstLossPercent = calculateFinalLossPercent(firstHops);
mtrVO.setFirstLossPercent(firstLossPercent);
mtrVO.setHopInfos(firstHops);
// 如果丢包率超过5%30秒后重试
if (firstLossPercent > 5.0) {
AssertLog.info("目标IP {} 丢包率 {}% > 5%30秒后重试探测", targetIp, firstLossPercent);
// 如果丢包率超过阈值,进行重试探测
if (firstLossPercent > LOSS_PERCENT_THRESHOLD) {
AssertLog.info("目标IP {} 丢包率 {}% > {}%{}秒后重试探测",
targetIp, firstLossPercent, LOSS_PERCENT_THRESHOLD, RETRY_DELAY_MS/1000);
// 等待30秒
Thread.sleep(30000);
Thread.sleep(RETRY_DELAY_MS);
// 第二次MTR探测
double secondLossPercent = executeMtrCommand(targetIp);
mtrVO.setFinalLossPercent(secondLossPercent);
mtrVO.setHasRetry(true);
List<HopInfoVO> finalHops = executeMtrCommand(targetIp);
double finalLossPercent = calculateFinalLossPercent(finalHops);
mtrVO.setFinalLossPercent(finalLossPercent);
mtrVO.setHopInfos(finalHops);
AssertLog.info("目标IP {} 重试探测完成,最终丢包率: {}%", targetIp, secondLossPercent);
AssertLog.info("目标IP {} 重试探测完成,最终丢包率: {}%", targetIp, finalLossPercent);
} else {
mtrVO.setFinalLossPercent(firstLossPercent);
mtrVO.setHasRetry(false);
AssertLog.info("目标IP {} 丢包率 {}%,无需重试", targetIp, firstLossPercent);
}
return mtrVO;
} catch (Exception e) {
AssertLog.error("MTR探测异常 - IP: {}", targetIp, e);
mtrVO.setFinalLossPercent(-1.0); // -1表示探测失败
mtrVO.setErrorMsg(e.getMessage());
return mtrVO;
handleMtrProbeError(mtrVO, targetIp, e);
}
return mtrVO;
}
/**
* 执行mtr -r命令并解析结果
* 创建MTR结果对象
*/
private double executeMtrCommand(String targetIp) {
private MtrVO createMtrVO(String targetIp, String clientId, long timestamp) {
MtrVO mtrVO = new MtrVO();
mtrVO.setTargetIp(targetIp);
mtrVO.setClientId(clientId);
mtrVO.setTimestamp(timestamp);
return mtrVO;
}
/**
* 处理MTR探测异常
*/
private void handleMtrProbeError(MtrVO mtrVO, String targetIp, Exception e) {
AssertLog.error("MTR探测异常 - IP: {}", targetIp, e);
mtrVO.setFinalLossPercent(-1.0);
mtrVO.setErrorMsg(e.getMessage());
}
/**
* 执行mtr命令并解析结果
* @return 跳数信息列表
*/
private List<HopInfoVO> executeMtrCommand(String targetIp) {
Process process = null;
try {
// 执行mtr -r -c 10命令
ProcessBuilder processBuilder = new ProcessBuilder("mtr", "-r", "-c", "10", targetIp);
ProcessBuilder processBuilder = new ProcessBuilder("mtr", "-r", "-c",
String.valueOf(MTR_PACKET_COUNT), targetIp);
process = processBuilder.start();
// 读取命令输出
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
String lastValidLine = null;
while ((line = reader.readLine()) != null) {
// 跳过标题行,记录最后一条有效数据行
if (!line.trim().isEmpty() &&
!line.startsWith("Start:") &&
!line.startsWith("HOST:") &&
Character.isDigit(line.trim().charAt(0))) {
lastValidLine = line;
}
}
// 等待命令执行完成
boolean finished = process.waitFor(2, TimeUnit.MINUTES);
if (!finished) {
process.destroy();
throw new RuntimeException("MTR命令执行超时");
}
if (lastValidLine == null) {
throw new RuntimeException("MTR命令无有效输出");
}
// 解析丢包率
return parseLossPercentFromMtrOutput(lastValidLine);
return parseMtrOutput(process);
} catch (Exception e) {
throw new RuntimeException("MTR命令执行异常: " + e.getMessage());
@@ -197,21 +236,167 @@ public class MtrServiceImpl implements MtrService {
}
/**
* MTR输出中解析丢包率
* 解析MTR命令输出
*/
private double parseLossPercentFromMtrOutput(String mtrOutputLine) {
private List<HopInfoVO> parseMtrOutput(Process process) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
List<HopInfoVO> hops = new ArrayList<>();
while ((line = reader.readLine()) != null) {
if (isValidHopLine(line)) {
HopInfoVO hop = parseHopLine(line);
if (hop != null) {
hops.add(hop);
}
}
}
// 等待命令执行完成
boolean finished = process.waitFor(MTR_TIMEOUT_MINUTES, TimeUnit.MINUTES);
if (!finished) {
process.destroy();
throw new RuntimeException("MTR命令执行超时");
}
if (hops.isEmpty()) {
throw new RuntimeException("MTR命令无有效输出");
}
return hops;
}
/**
* 检查是否为有效的跳数行
*/
private boolean isValidHopLine(String line) {
return !line.trim().isEmpty() &&
!line.startsWith("Start:") &&
!line.startsWith("HOST:") &&
Character.isDigit(line.trim().charAt(0));
}
/**
* 解析单跳数据行
*/
private HopInfoVO parseHopLine(String line) {
try {
// 使用正则表达式匹配Loss%列
Pattern pattern = Pattern.compile("(\\d+\\.?\\d*)%");
Matcher matcher = pattern.matcher(mtrOutputLine);
// 匹配格式: "1.|-- 26.10.194.38 20.0% 10 1.6 1.5 1.3 1.8 0.0"
Pattern pattern = Pattern.compile(
"^\\s*(\\d+)\\.\\|--\\s+(\\S+)\\s+(\\d+\\.?\\d*)%\\s+(\\d+)\\s+(\\d+\\.\\d+)\\s+(\\d+\\.\\d+).*"
);
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
return Double.parseDouble(matcher.group(1));
HopInfoVO hop = new HopInfoVO();
hop.setHopNumber(Integer.parseInt(matcher.group(1)));
String host = matcher.group(2);
setHostInfo(hop, host);
hop.setLossPercent(Double.parseDouble(matcher.group(3)));
hop.setAvgLatency(Double.parseDouble(matcher.group(6))); // 平均延迟
return hop;
}
throw new RuntimeException("未找到Loss%数据: " + mtrOutputLine);
// 处理未知主机的情况(如???
return parseUnknownHostLine(line);
} catch (Exception e) {
throw new RuntimeException("解析Loss%失败: " + e.getMessage());
throw new RuntimeException("解析MTR跳数数据失败: " + e.getMessage() + ", 行: " + line);
}
}
}
/**
* 设置主机信息(IP地址解析和地理位置查询)
*/
private void setHostInfo(HopInfoVO hop, String host) {
if (isValidIpAddress(host)) {
hop.setIpAddress(host);
// 如果是内网IP,不进行地理位置查询
if (!isPrivateIp(host)) {
queryIpLocationInfo(hop, host);
} else {
setPrivateIpLocationInfo(hop, host);
}
} else {
hop.setHostname(host);
}
}
/**
* 检查是否为有效的IP地址
*/
private boolean isValidIpAddress(String host) {
return host.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}") && !host.equals("???");
}
/**
* 为内网IP设置特殊的地理位置信息
*/
private void setPrivateIpLocationInfo(HopInfoVO hop, String ipAddress) {
hop.setCountry("内网");
hop.setProvince("内网");
hop.setCity("内网");
hop.setIsp("内网");
}
/**
* 使用在线服务查询IP地理位置信息
*/
private void queryIpLocationInfo(HopInfoVO hop, String ipAddress) {
try {
Map<String, String> locationInfo = onlineIPQueryService.queryIPLocation(ipAddress);
if (locationInfo != null) {
hop.setCountry(locationInfo.get("country"));
hop.setProvince(locationInfo.get("province"));
hop.setCity(locationInfo.get("city"));
hop.setIsp(locationInfo.get("isp"));
}
} catch (Exception e) {
AssertLog.error("查询IP地理位置信息失败: {}", ipAddress, e);
// 设置默认值
setDefaultLocationInfo(hop);
}
}
/**
* 设置默认地理位置信息
*/
private void setDefaultLocationInfo(HopInfoVO hop) {
hop.setCountry("未知");
hop.setProvince("未知");
hop.setCity("未知");
hop.setIsp("未知");
}
/**
* 解析未知主机行
*/
private HopInfoVO parseUnknownHostLine(String line) {
Pattern pattern = Pattern.compile("^\\s*(\\d+)\\.\\|--\\s+(\\S+).*");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
HopInfoVO hop = new HopInfoVO();
hop.setHopNumber(Integer.parseInt(matcher.group(1)));
hop.setHostname(matcher.group(2));
hop.setLossPercent(0.0);
hop.setAvgLatency(0.0);
return hop;
}
return null;
}
/**
* 计算最终丢包率(取最后一跳的丢包率)
*/
private double calculateFinalLossPercent(List<HopInfoVO> hops) {
if (CollectionUtil.isEmpty(hops)) {
return 0.0;
}
return hops.get(hops.size() - 1).getLossPercent();
}
}
@@ -0,0 +1,200 @@
package com.tongran.agent.client.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* 在线IP查询服务
*/
@Component
public class OnlineIPQueryService {
private final RestTemplate restTemplate;
// 内网IP地址段正则表达式
private static final Pattern PRIVATE_IP_PATTERN = Pattern.compile(
"^(10\\.|172\\.(1[6-9]|2[0-9]|3[0-1])\\.|192\\.168\\.|127\\.|169\\.254\\.|0\\.|255\\.)"
);
public OnlineIPQueryService() {
this.restTemplate = new RestTemplate();
}
/**
* 查询IP地理位置信息 - 使用ip-api.com(免费无需key
*/
public Map<String, String> queryIPLocation(String ip) {
Map<String, String> result = new HashMap<>();
// 如果是内网IP,直接返回内网标识
if (isPrivateIp(ip)) {
return createPrivateIpResult(ip);
}
try {
// 使用ip-api.com免费服务
String url = "http://ip-api.com/json/" + ip + "?lang=zh-CN";
HttpHeaders headers = new HttpHeaders();
headers.set("User-Agent", "Mozilla/5.0");
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, entity, String.class);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
return parseIpApiResponse(response.getBody(), ip);
}
} catch (Exception e) {
// 如果ip-api.com失败,尝试备用服务
return queryBackupService(ip);
}
return createUnknownResult();
}
/**
* 检查是否为内网IP
*/
private boolean isPrivateIp(String ip) {
if (ip == null || ip.isEmpty()) {
return true;
}
// 检查常见的非IP地址情况
if (ip.equals("???") || ip.equals("*") || ip.equals("localhost")) {
return true;
}
// 使用正则表达式匹配内网IP段
return PRIVATE_IP_PATTERN.matcher(ip).find();
}
/**
* 解析ip-api.com响应
*/
private Map<String, String> parseIpApiResponse(String response, String ip) {
Map<String, String> result = new HashMap<>();
try {
JSONObject json = JSON.parseObject(response);
if (!"success".equals(json.getString("status"))) {
return createUnknownResult();
}
result.put("country", json.getString("country"));
result.put("province", json.getString("regionName"));
result.put("city", json.getString("city"));
result.put("isp", parseISP(json.getString("isp")));
result.put("query", ip);
result.put("source", "ip-api.com");
return result;
} catch (Exception e) {
return createUnknownResult();
}
}
/**
* 备用查询服务
*/
private Map<String, String> queryBackupService(String ip) {
try {
// 使用IP.SB作为备用服务
String url = "https://api.ip.sb/geoip/" + ip;
HttpHeaders headers = new HttpHeaders();
headers.set("User-Agent", "Mozilla/5.0");
headers.set("Accept", "application/json");
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, entity, String.class);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
return parseIpSbResponse(response.getBody(), ip);
}
} catch (Exception e) {
// 备用服务也失败
}
return createUnknownResult();
}
/**
* 解析IP.SB响应
*/
private Map<String, String> parseIpSbResponse(String response, String ip) {
Map<String, String> result = new HashMap<>();
try {
JSONObject json = JSON.parseObject(response);
result.put("country", json.getString("country"));
result.put("province", json.getString("region"));
result.put("city", json.getString("city"));
result.put("isp", json.getString("isp"));
result.put("query", ip);
result.put("source", "ip.sb");
return result;
} catch (Exception e) {
return createUnknownResult();
}
}
/**
* 创建内网IP结果
*/
private Map<String, String> createPrivateIpResult(String ip) {
Map<String, String> result = new HashMap<>();
result.put("country", "内网");
result.put("province", "内网");
result.put("city", "内网");
result.put("isp", "内网");
result.put("query", ip);
result.put("source", "private");
return result;
}
/**
* 标准化运营商名称
*/
private String parseISP(String isp) {
if (isp == null) return "未知";
if (isp.contains("电信") || isp.contains("China Telecom")) return "中国电信";
if (isp.contains("联通") || isp.contains("China Unicom")) return "中国联通";
if (isp.contains("移动") || isp.contains("China Mobile")) return "中国移动";
if (isp.contains("铁通")) return "中国铁通";
if (isp.contains("教育网")) return "教育网";
return isp;
}
private Map<String, String> createUnknownResult() {
Map<String, String> result = new HashMap<>();
result.put("country", "未知");
result.put("province", "未知");
result.put("city", "未知");
result.put("isp", "未知");
result.put("source", "unknown");
return result;
}
}
@@ -557,11 +557,51 @@ public class AgentUtil {
}
/**
* dmidecode -s system-serial-number命令获取SN序列号
* @return SN序列号,如果获取失败则返回空字符串
*/
public static String getDeviceSN() {
BufferedReader reader = null;
try {
// 执行dmidecode命令获取系统序列号
Process process = Runtime.getRuntime().exec("dmidecode -s system-serial-number");
// 读取命令输出
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String sn = reader.readLine();
// 等待命令执行完成
int exitCode = process.waitFor();
// 检查命令是否执行成功且输出不为空
if (exitCode == 0 && sn != null && !sn.trim().isEmpty()) {
return sn.trim();
} else {
AssertLog.error("Failed to get SN: Command exited with code " + exitCode);
return "";
}
} catch (Exception e) {
// 处理其他未知异常
AssertLog.error("Unexpected error while getting device SN: " + e.getMessage());
return "";
} finally {
// 确保关闭资源
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
AssertLog.error("Error closing reader: " + e.getMessage());
}
}
}
}
/**
* 获取 /etc/issue 文件的第二行内容
* @return 第二行字符串,如果不存在则返回 null
*/
public static String getDeviceSN() {
public static String getDeviceSNBak() {
Path issuePath = Paths.get("/etc/issue");
if (!Files.exists(issuePath)) {
System.err.println("文件不存在: /etc/issue");
@@ -1,19 +1,46 @@
package com.tongran.agent.client.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public class NetworkUtil {
public static boolean addRoute(String ip, String prefix, String gateway, String dev) {
try {
ProcessBuilder pb = new ProcessBuilder("ip", "route", "add",
ip + "/" + prefix, "via", gateway, "dev", dev);
ip + "/" + prefix, "via", gateway, "dev", dev);
pb.redirectErrorStream(true);
Process p = pb.start();
return p.waitFor() == 0;
String output = readProcessOutput(p);
int exitCode = p.waitFor();
if (exitCode == 0) {
return true;
} else {
// 检查是否是路由已存在的错误
if (output != null && (output.contains("File exists") || output.contains("RTNETLINK answers: File exists"))) {
AssertLog.info("Route already exists");
return true;
}
AssertLog.error("Failed to add route: " + output);
return false;
}
} catch (Exception e) {
e.printStackTrace();
AssertLog.error("Exception while adding route: " + e.getMessage());
return false;
}
}
private static String readProcessOutput(Process p) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
return "Error reading output: " + e.getMessage();
}
}
public static boolean deleteRoute(String ip, String prefix, String gateway, String dev) {
try {
ProcessBuilder pb = new ProcessBuilder("ip", "route", "del",