Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e145974aad | |||
| 5e4ebf5c9c | |||
| e786599310 | |||
| 30d44561d1 | |||
| 2ba1134c82 | |||
| e60f14ab11 | |||
| 26d8ef298a | |||
| 630558f4d6 | |||
| f453f17903 | |||
| 3ff73519f9 | |||
| 95922cac09 | |||
| 01fd405e0b | |||
| 3e7c9ec5cf | |||
| 04a51bc201 | |||
| c5353aaafd | |||
| 057c879b59 | |||
| 421b7f1073 | |||
| 3820d11353 | |||
| 6c5010119f | |||
| 158fddd0b2 | |||
| f5c3ae500b | |||
| f9a4de8c97 | |||
| b4155ea9c8 | |||
| bb87f80593 | |||
| fc97e66dc2 | |||
| b9afa99623 | |||
| 8bcc017d22 | |||
| 0f649a2cfc | |||
| 2ad6ea2ad5 | |||
| 13968d60aa | |||
| cebcf56adf | |||
| 6dbee99194 | |||
| ed801a05c9 | |||
| 1c1cdad5e0 | |||
| a9ca7f7ca6 | |||
| 154e57a0a6 | |||
| eebc2de645 | |||
| 196961ff11 | |||
| 5f67018d35 | |||
| 45840e00e3 | |||
| 1b3ec66803 | |||
| 620c97ee5b | |||
| 73cfc796d4 | |||
| 9c3db28249 | |||
| b8b85d0e76 | |||
| 504cf8f65b | |||
| eb7fba8529 | |||
| 8175f939f8 | |||
| b433c8480e | |||
| c12efbf410 | |||
| b2d6f2f655 | |||
| 4669573373 | |||
| ae5c50e5b4 | |||
| c4bb76049a | |||
| 85ab66fe6e | |||
| 9c08f97347 | |||
| 8237833624 | |||
| 1a58e37e7c | |||
| 72af737163 | |||
| a2e3e296b5 | |||
| 1485d5d46c | |||
| 62c8736728 | |||
| 36ff2dbb48 |
+46
@@ -0,0 +1,46 @@
|
||||
######################################################################
|
||||
# Build Tools
|
||||
|
||||
.gradle
|
||||
/build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
######################################################################
|
||||
# IDE
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### JRebel ###
|
||||
rebel.xml
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/*
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
######################################################################
|
||||
# Others
|
||||
*.log
|
||||
*.xml.versionsBackup
|
||||
*.swp
|
||||
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
||||
@@ -143,6 +143,15 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- 打包跳过测试 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -3,9 +3,13 @@ package com.tongran.agent.client;
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@ComponentScan(basePackages = {"com.tongran.agent.client", "cn.hutool.extra.spring"})
|
||||
public class TrAgentClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
package com.tongran.agent.client.core.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "spring.application")
|
||||
@Data
|
||||
public class ApplicationProperties {
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
|
||||
// Getter 和 Setter 方法
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
private String confPath;
|
||||
private String scriptPath;
|
||||
private String tmpPath;
|
||||
private String tempPath;
|
||||
private String frpPath;
|
||||
private String poePath;
|
||||
private String tempTrafficPath;
|
||||
private String tempPcapPath;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.tongran.agent.client.core.config;
|
||||
import com.tongran.agent.client.core.eo.AlarmEO;
|
||||
import com.tongran.agent.client.core.eo.NativeNetworkInterfaceEO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -14,51 +15,58 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
|
||||
public class GlobalConfig {
|
||||
|
||||
/**
|
||||
* pppoe配置方式(1静态ip配置,2非saas平台配置)
|
||||
*/
|
||||
public static String PPPOEMODE = "";
|
||||
/**
|
||||
* 业务网卡名称
|
||||
*/
|
||||
public static String NETNAME = "";
|
||||
/**
|
||||
* tcpdump探测时间
|
||||
*/
|
||||
public static String TCPDUMP_TIMES = "";
|
||||
/**
|
||||
* 服务启动时间
|
||||
*/
|
||||
public static long startupTime;
|
||||
|
||||
/**
|
||||
* 注册标识
|
||||
*/
|
||||
public static boolean isRegister = false;
|
||||
|
||||
|
||||
/**
|
||||
* 客户端ID - 对应平台唯一SN
|
||||
* 客户端ID
|
||||
*/
|
||||
public static String CLIENT_ID;
|
||||
|
||||
|
||||
/**
|
||||
* 交换机信息
|
||||
* 设备SN
|
||||
*/
|
||||
public static String SWITCH_COMMUNITY; //团名
|
||||
public static String SWITCH_IP; //交换机IP
|
||||
public static int SWITCH_PORT; //交换机端口
|
||||
public static LinkedHashMap<String, String> SWITCH_NET_OID = new LinkedHashMap<>(); //交换机网络端口发现OID
|
||||
public static LinkedHashMap<String, String> SWITCH_MODULE_OID = new LinkedHashMap<>(); //交换机光模块发现OID
|
||||
public static LinkedHashMap<String, String> SWITCH_MPU_OID = new LinkedHashMap<>(); //交换机MPU发现OID
|
||||
public static LinkedHashMap<String, String> SWITCH_PWR_OID = new LinkedHashMap<>(); //交换机电源发现OID
|
||||
public static LinkedHashMap<String, String> SWITCH_FAN_OID = new LinkedHashMap<>(); //交换机风扇发现OID
|
||||
public static LinkedHashMap<String, String> SWITCH_OTHER_OID = new LinkedHashMap<>(); //交换机系统其他OID
|
||||
public static String OTHER_INDEX_PARAM = "entIndex"; //交换机其他监控项索引参数
|
||||
public static String OTHER_INDEX_OID = "";
|
||||
public static List<String> OTHER_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String NET_INDEX_PARAM = "ifIndex"; //交换机网络端口索引参数
|
||||
public static String NET_INDEX_OID = "";
|
||||
public static List<String> NET_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String MODULE_INDEX_PARAM = "fiberEntIndex"; //交换机光模块端口索引参数
|
||||
public static String MODULE_INDEX_OID = "";
|
||||
public static List<String> MODULE_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String MPU_INDEX_PARAM = "mpuEntIndex"; //交换机MPU索引参数
|
||||
public static String MPU_INDEX_OID = "";
|
||||
public static List<String> MPU_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String PWR_INDEX_PARAM = "pwrEntIndex"; //交换机电源索引参数
|
||||
public static String PWR_INDEX_OID = "";
|
||||
public static List<String> PWR_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String FAN_INDEX_PARAM = "fanEntIndex"; //交换机风扇索引参数
|
||||
public static String FAN_INDEX_OID = "";
|
||||
public static List<String> FAN_FILTER = new ArrayList<>(); //过滤值
|
||||
public static String DEVICE_SN;
|
||||
|
||||
/**
|
||||
* 系统 HZ (每秒tick数)
|
||||
*/
|
||||
public static Integer systemHz = null;
|
||||
|
||||
/**
|
||||
* 逻辑标识
|
||||
*/
|
||||
public static LocalDateTime LOGICAL_NODE_LAST_TIME;
|
||||
public static String LOGICAL_NODE;
|
||||
|
||||
/**
|
||||
* 最新策略信息
|
||||
*/
|
||||
public static long MONITOR_TIME = 0L;
|
||||
public static long SCRIPT_TIME = 0L;
|
||||
public static long VERSION_TIME = 0L;
|
||||
public static long ROUTE_TIME = 0L;
|
||||
/**
|
||||
* 采集标识
|
||||
*/
|
||||
@@ -114,51 +122,6 @@ public class GlobalConfig {
|
||||
public static long systemUptimeInterval = 300; //系统正常运行时间采集间隔
|
||||
public static boolean procNumCollect = false; //进程数采集
|
||||
public static long procNumInterval = 300; //进程数采集间隔
|
||||
/**
|
||||
* 采集交换机监控
|
||||
*/
|
||||
public static boolean switchNetCollect = false; //交换机网络采集
|
||||
public static long switchNetInterval = 300; //交换机网络采集间隔
|
||||
public static boolean switchModuleCollect = false; //光模块采集
|
||||
public static long switchModuleInterval = 300; //光模块采集间隔
|
||||
public static boolean switchMpuCollect = false; //MPU采集
|
||||
public static long switchMpuInterval = 300; //MPU采集间隔
|
||||
public static boolean switchPwrCollect = false; //电源采集
|
||||
public static long switchPwrInterval = 300; //电源采集间隔
|
||||
public static boolean switchFanCollect = false; //风扇采集
|
||||
public static long switchFanInterval = 300; //风扇采集间隔
|
||||
/**
|
||||
* 采集交换机其他监控
|
||||
*/
|
||||
public static boolean switchSysDescrCollect = false; //系统描述采集
|
||||
public static long switchSysDescrInterval = 300; //系统描述采集间隔
|
||||
public static boolean switchSysObjectIDCollect = false; //系统Object ID采集
|
||||
public static long switchSysObjectIDInterval = 300; //系统Object ID采集间隔
|
||||
public static boolean switchSysUpTimeCollect = false; //系统运行时间采集
|
||||
public static long switchSysUpTimeInterval = 300; //系统运行时间采集间隔
|
||||
public static boolean switchSysContactCollect = false; //系统联系信息采集
|
||||
public static long switchSysContactInterval = 300; //系统联系信息采集间隔
|
||||
public static boolean switchSysNameCollect = false; //系统名称采集
|
||||
public static long switchSysNameInterval = 300; //系统名称采集间隔
|
||||
public static boolean switchSysLocationCollect = false; //系统位置采集
|
||||
public static long switchSysLocationInterval = 300; //系统位置采集间隔
|
||||
public static boolean switchHwStackSystemMacCollect = false; //系统MAC地址采集
|
||||
public static long switchHwStackSystemMacInterval = 300; //系统MAC地址采集间隔
|
||||
public static boolean switchEntIndexCollect = false; //设备索引采集
|
||||
public static long switchEntIndexInterval = 300; //设备索引采集间隔
|
||||
public static boolean switchEntPhysicalNameCollect = false; //设备名称采集
|
||||
public static long switchEntPhysicalNameInterval = 300; //设备名称采集间隔
|
||||
public static boolean switchEntPhysicalSoftwareRevCollect = false; //设备软件版本采集
|
||||
public static long switchEntPhysicalSoftwareRevInterval = 300; //设备软件版本采集间隔
|
||||
public static boolean switchHwEntityCpuUsageCollect = false; //设备CPU使用率(%)采集
|
||||
public static long switchHwEntityCpuUsageInterval = 300; //设备CPU使用率(%)采集间隔
|
||||
public static boolean switchHwEntityMemUsageCollect = false; //设备内存使用率(%)采集
|
||||
public static long switchHwEntityMemUsageInterval = 300; //设备内存使用率(%)采集间隔
|
||||
public static boolean switchHwAveragePowerCollect = false; //系统平均功率(mW)采集
|
||||
public static long switchHwAveragePowerInterval = 300; //系统平均功率(mW)采集间隔
|
||||
public static boolean switchHwCurrentPowerCollect = false; //系统实时功率(mW)采集
|
||||
public static long switchHwCurrentPowerInterval = 300; //系统实时功率(mW)采集间隔
|
||||
|
||||
|
||||
/**
|
||||
* 告警
|
||||
@@ -166,18 +129,6 @@ public class GlobalConfig {
|
||||
public static boolean IS_ALARM = false;
|
||||
public static long ALARM_INTERVAL = 60;
|
||||
public static List<AlarmEO> ALARM_LIST = new ArrayList<>(); //告警设置信息
|
||||
/**
|
||||
* 告警监控
|
||||
*/
|
||||
public static boolean systemCpuUti = false; //CPU使用率
|
||||
public static boolean memoryUtilization = false; //内存利用率
|
||||
public static boolean systemSwapSizePercent = false; //可用交换空间百分比
|
||||
public static boolean systemUsersNum = false; //登录用户数
|
||||
public static boolean vfsFsUtil = false; //挂载点的空间利用率
|
||||
public static boolean netIfStatus = false; //网络运行状态UP变down
|
||||
public static boolean netUtil = false; //网络带宽使用率
|
||||
public static boolean containerMemUtil = false; //容器内存使用率
|
||||
public static boolean extraPorts = false; //多余端口
|
||||
|
||||
/**
|
||||
* 所有网络接口
|
||||
|
||||
@@ -11,11 +11,18 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public enum MsgEnum {
|
||||
建立连接("CONNECT"),
|
||||
|
||||
建立连接应答("CONNECT_RSP"),
|
||||
|
||||
注册("REGISTER"),
|
||||
|
||||
注册应答("REGISTER_RSP"),
|
||||
|
||||
获取最新策略("GET_POLICY"),
|
||||
|
||||
获取最新策略应答("GET_POLICY_RSP"),
|
||||
|
||||
断开("DISCONNECT"),
|
||||
|
||||
断开应答("DISCONNECT_RSP"),
|
||||
@@ -32,6 +39,10 @@ public enum MsgEnum {
|
||||
|
||||
网络上报("NET"),
|
||||
|
||||
网络上报重试("NET_RECOVER"),
|
||||
|
||||
业务网络上报("BUSINESS_NET"),
|
||||
|
||||
挂载上报("POINT"),
|
||||
|
||||
交换机上报("SWITCHBOARD"),
|
||||
@@ -48,14 +59,6 @@ public enum MsgEnum {
|
||||
|
||||
关闭所有系统采集应答("SYSTEM_COLLECT_STOP_RSP"),
|
||||
|
||||
开启或更新交换机采集("SWITCH_COLLECT_START"),
|
||||
|
||||
开启或更新交换机采集应答("SWITCH_COLLECT_START_RSP"),
|
||||
|
||||
关闭所有交换机采集("SWITCH_COLLECT_STOP"),
|
||||
|
||||
关闭所有交换机采集应答("SWITCH_COLLECT_STOP_RSP"),
|
||||
|
||||
告警设置("ALARM_SET"),
|
||||
|
||||
告警设置应答("ALARM_SET_RSP"),
|
||||
@@ -66,7 +69,19 @@ public enum MsgEnum {
|
||||
|
||||
Agent版本更新("AGENT_VERSION_UPDATE"),
|
||||
|
||||
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP");
|
||||
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"),
|
||||
|
||||
修改frp配置文件应答("UPDATE_FRP_RSP"),
|
||||
|
||||
macvlan状态上报("MACVLAN_STATUS_RSP"),
|
||||
|
||||
iops结果上报("IOPS_RESULT"),
|
||||
|
||||
tcpdump结果上报("TCPDUMP_RESULT"),
|
||||
|
||||
内存详情上报("MEMORY_DETAILS"),
|
||||
|
||||
多网IP探测上报("NETWORK_DETECT");
|
||||
|
||||
private String value;
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@@ -16,11 +14,8 @@ public class AgentVersionUpdateEO {
|
||||
//文件地址,外网HTTP(S)地址
|
||||
private String fileUrl;
|
||||
|
||||
//保存文件地址
|
||||
private String filePath;
|
||||
|
||||
//命令
|
||||
private List<String> commands;
|
||||
//文件MD5
|
||||
private String fileMd5;
|
||||
|
||||
//执行方式:0、立即执行;1、定时执行;
|
||||
private int method;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.tongran.agent.client.core.eo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FrpMsgEO {
|
||||
/** 远程端口 */
|
||||
private Integer remotePort;
|
||||
|
||||
/** 服务器端口 */
|
||||
private String serverPort;
|
||||
|
||||
/** 服务器地址 */
|
||||
private String serverAddr;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.tongran.agent.client.core.eo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PppoeConfigSubEO {
|
||||
/** 序号 */
|
||||
private Long serialNumber;
|
||||
|
||||
/** VLANID */
|
||||
private Long vlanId;
|
||||
|
||||
/** IPv4地址 */
|
||||
private String ipv4Address;
|
||||
|
||||
/** IPv4掩码位数 */
|
||||
private Long ipv4MaskBits;
|
||||
|
||||
/** IPv4网关 */
|
||||
private String ipv4Gateway;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.tongran.agent.client.core.eo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PppoeEO {
|
||||
/** 是否启用PPPoE(0-否,1-是) */
|
||||
private Long pppoeEnabled;
|
||||
|
||||
/** PPPoE配置方式 */
|
||||
private String pppoeConfigMode;
|
||||
|
||||
/** PPPoE网卡名称 */
|
||||
private String pppoeInterface;
|
||||
/** pppoe子表集合 */
|
||||
private List<PppoeConfigSubEO> subList;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@@ -16,17 +15,15 @@ public class ScriptPolicyEO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1267013167162440612L;
|
||||
|
||||
private String scriptId;
|
||||
|
||||
private String policyName;
|
||||
/**
|
||||
* 文件
|
||||
*/
|
||||
private List<ScriptFile> files;
|
||||
|
||||
//目标路径地址
|
||||
private String filePath;
|
||||
//文件类型为1、外网HTTP(S)时必传
|
||||
private String fileUrl;
|
||||
|
||||
//命令
|
||||
private List<String> commands;
|
||||
//脚本参数
|
||||
private String commandParams;
|
||||
|
||||
//执行方式:0、立即执行;1、定时执行;
|
||||
private int method;
|
||||
@@ -34,25 +31,4 @@ public class ScriptPolicyEO implements Serializable {
|
||||
//执行方式为1、定时执行时必传-指定时间
|
||||
private long policyTime;
|
||||
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class ScriptFile{
|
||||
|
||||
//文件类型:0、平台文件地址;1、外网HTTP(S)
|
||||
private int fileType;
|
||||
|
||||
//文件类型为1、外网HTTP(S)时必传
|
||||
private String fileUrl;
|
||||
|
||||
//文件类型为0、平台文件地址时必传(文件名)
|
||||
private String fileName;
|
||||
|
||||
//文件类型为0、平台文件地址时必传(文件流)
|
||||
private String fileData;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.tongran.agent.client.core.eo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TcpdumpEO {
|
||||
/** 开启探测(0:否,1:是) */
|
||||
private Integer detectFlag;
|
||||
|
||||
/** 探测频率1 每天 2立即执行 */
|
||||
private String frequency;
|
||||
|
||||
/** 探测时间列表(多个时间用,分隔) */
|
||||
private String detectTimes;
|
||||
}
|
||||
@@ -25,8 +25,8 @@ public class CpuVO implements Serializable {
|
||||
@Schema(description = "CPU15分钟负载")
|
||||
private double avg15;
|
||||
|
||||
@Schema(description = "CPU硬件中断提供服务时间")
|
||||
private long interrupt;
|
||||
@Schema(description = "CPU硬件中断提供服务时间:秒")
|
||||
private double interrupt;
|
||||
|
||||
@Schema(description = "CPU使用率%")
|
||||
private double uti;
|
||||
@@ -34,23 +34,29 @@ public class CpuVO implements Serializable {
|
||||
@Schema(description = "CPU数量")
|
||||
private int num;
|
||||
|
||||
@Schema(description = "CPU正常运行时间/秒")
|
||||
@Schema(description = "CPU核数")
|
||||
private int cores;
|
||||
|
||||
@Schema(description = "CPU正常运行时间:秒")
|
||||
private long normal;
|
||||
|
||||
@Schema(description = "CPU空闲时间")
|
||||
private long idle;
|
||||
@Schema(description = "CPU空闲时间:秒")
|
||||
private double idle;
|
||||
|
||||
@Schema(description = "CPU等待响应时间")
|
||||
@Schema(description = "CPU等待响应时间:秒")
|
||||
private double iowait;
|
||||
|
||||
@Schema(description = "CPU系统时间")
|
||||
private long system;
|
||||
@Schema(description = "CPU系统时间:秒")
|
||||
private double system;
|
||||
|
||||
@Schema(description = "CPU软件无响应时间")
|
||||
private double noresp;
|
||||
|
||||
@Schema(description = "CPU用户进程所花费的时间")
|
||||
private long user;
|
||||
@Schema(description = "CPU用户进程所花费的时间:秒")
|
||||
private double user;
|
||||
|
||||
@Schema(description = "cpu温度")
|
||||
private double temperature;
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private long timestamp;
|
||||
|
||||
@@ -45,6 +45,15 @@ public class DiskVO implements Serializable {
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private long timestamp;
|
||||
@Schema(description = "磁盘类型")
|
||||
private String type;
|
||||
@Schema(description = "已用空间")
|
||||
private long usedSpace;
|
||||
/** 健康状态 0 不健康,1健康 */
|
||||
@Schema(description = "健康状态")
|
||||
private Long healthStatus;
|
||||
@Schema(description = "健康分数")
|
||||
private Integer healthScore;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.tongran.agent.client.core.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MacVlanVO {
|
||||
/** 虚拟网卡id */
|
||||
private String vlanId;
|
||||
/** 编号 */
|
||||
private String mid;
|
||||
/** 状态 */
|
||||
private String status;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.tongran.agent.client.core.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MemoryDetailsVO {
|
||||
private String name; // 名称(如 DIMM000)
|
||||
private String location; // 位置(mainboard)
|
||||
private String manufacturer; // 厂商(Samsung)
|
||||
private Long capacity; // 容量(MB,如 32768)
|
||||
private Integer frequency; // 主频(MHz,如 2133)
|
||||
private String serialNumber; // 序列号(如 0x32A1EA07)
|
||||
private String type; // 类型(DDR4)
|
||||
private Integer minVoltage; // 最小电压(mV,如 1200)
|
||||
private String rank; // RANK(列)(如 4 rank)
|
||||
private String bitWidth; // 位宽(如 72 bit)
|
||||
private String technology; // 技术(Synchronous|Registered (B N/A)
|
||||
private String partNumber; // 部件编码(如 M386A4G40DM0-CPB)
|
||||
private String healthStatus; // 健康状态(1正常/0配置错误)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tongran.agent.client.core.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "网络业务信息")
|
||||
public class NetBusinessVO implements Serializable {
|
||||
private static final long serialVersionUID = 9L;
|
||||
|
||||
private String name; // 网卡名称
|
||||
private String mac; // mac地址
|
||||
private int pid; // 进程ID
|
||||
private String processName; // 进程名
|
||||
|
||||
private Long inSpeed; // 累计接收字节数
|
||||
private Long outSpeed; // 累计发送字节数
|
||||
/** IPv4接收流量 */
|
||||
private Long ipv4InSpeed;
|
||||
|
||||
/** IPv4发送流量 */
|
||||
private Long ipv4OutSpeed;
|
||||
|
||||
/** IPv6接收流量 */
|
||||
private Long ipv6InSpeed;
|
||||
|
||||
/** IPv6发送流量 */
|
||||
private Long ipv6OutSpeed;
|
||||
|
||||
private int connectionCount; // 当前连接数
|
||||
|
||||
private long timestamp; // 采集时间戳
|
||||
|
||||
}
|
||||
@@ -31,18 +31,36 @@ public class NetVO implements Serializable {
|
||||
@Schema(description = "IPv4")
|
||||
private String ipV4;
|
||||
|
||||
@Schema(description = "IPv6")
|
||||
private String ipV6;
|
||||
|
||||
@Schema(description = "入站丢包")
|
||||
private long inDropped;
|
||||
|
||||
@Schema(description = "出站丢包")
|
||||
private long outDropped;
|
||||
|
||||
@Schema(description = "发送流量")
|
||||
@Schema(description = "ping丢包率")
|
||||
private Double pingDropped;
|
||||
|
||||
@Schema(description = "发送流量(发送总字节)")
|
||||
private long outSpeed;
|
||||
|
||||
@Schema(description = "接收流量")
|
||||
@Schema(description = "接收流量(接收总字节)")
|
||||
private long inSpeed;
|
||||
|
||||
@Schema(description = "IPv4接收流量")
|
||||
private Long ipv4InSpeed;
|
||||
|
||||
@Schema(description = "IPv4发送流量")
|
||||
private Long ipv4OutSpeed;
|
||||
|
||||
@Schema(description = "IPv6接收流量")
|
||||
private Long ipv6InSpeed;
|
||||
|
||||
@Schema(description = "IPv6发送流量")
|
||||
private Long ipv6OutSpeed;
|
||||
|
||||
@Schema(description = "协商速度")
|
||||
private String speed;
|
||||
|
||||
@@ -51,5 +69,7 @@ public class NetVO implements Serializable {
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private long timestamp;
|
||||
/** 是否最后一次 */
|
||||
private boolean lastTrafficFlag;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tongran.agent.client.core.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "网卡信息")
|
||||
public class NetworkInterfaceInfo {
|
||||
String name; // 接口名称:eth0, enp3s0
|
||||
String mac; // MAC 地址
|
||||
String type; // 接口类型:Ethernet
|
||||
String ipv4; // IPv4 地址
|
||||
String gateway; // 网关
|
||||
String publicIp; // 公网 IP
|
||||
String carrier; // 运营商
|
||||
String province; // 省
|
||||
String city; // 市
|
||||
String ipv6; // 市
|
||||
String status; // 与外网连通状态
|
||||
// 如果是子接口,存储父接口名称
|
||||
private String parentInterface;
|
||||
/** 网卡创建时间 */
|
||||
private Long netCreateTime;
|
||||
|
||||
// 如果是父接口,存储子接口列表
|
||||
private List<NetworkInterfaceInfo> subInterfaces;
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.tongran.agent.client.netty;
|
||||
|
||||
import com.tongran.agent.client.netty.config.BaseNettyConfig;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Netty配置
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ConfigurationProperties(prefix = "tcp.netty.charge")
|
||||
public class AgentNettyConfig extends BaseNettyConfig {
|
||||
|
||||
private static final long serialVersionUID = -4284214721383107291L;
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.tongran.agent.client.netty;
|
||||
|
||||
|
||||
import com.tongran.agent.client.netty.handler.AgentDecoderHandler;
|
||||
import com.tongran.agent.client.netty.handler.AgentDispatcherHandler;
|
||||
import com.tongran.agent.client.netty.handler.AgentEncoderHandler;
|
||||
import com.tongran.agent.client.netty.handler.TCPListenHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
public class AgentNettyServer extends BaseNettyServer {
|
||||
|
||||
@Resource
|
||||
private AgentNettyConfig config;
|
||||
|
||||
@Resource
|
||||
private TCPListenHandler tcpListenHandler;
|
||||
|
||||
@Resource
|
||||
private AgentDecoderHandler decoderHandler;
|
||||
|
||||
@Resource
|
||||
private AgentEncoderHandler encoderHandler;
|
||||
|
||||
@Resource
|
||||
private AgentDispatcherHandler dispatcherHandler;
|
||||
|
||||
|
||||
protected AgentNettyServer(AgentNettyConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Bean(name = "NettyCharge", initMethod = "start", destroyMethod = "stop")
|
||||
BaseNettyServer run() {
|
||||
config.setHander(new ChannelInitializer<NioSocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(NioSocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime));// 心跳
|
||||
ch.pipeline().addLast(tcpListenHandler);// 监听器
|
||||
//入栈
|
||||
ch.pipeline().addLast(decoderHandler);//解码器
|
||||
//出栈
|
||||
ch.pipeline().addLast(encoderHandler);//加码器
|
||||
// 业务分发是入站最后一个处理器, 出站第一个处理器, 位置要放在最后
|
||||
ch.pipeline().addLast(businessGroup, dispatcherHandler);//业务分发 这里使用业务线程组
|
||||
}
|
||||
});
|
||||
return new AgentNettyServer(config);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package com.tongran.agent.client.netty;
|
||||
|
||||
import com.tongran.agent.client.netty.handler.AgentDecoderHandler;
|
||||
import com.tongran.agent.client.netty.handler.AgentDispatcherHandler;
|
||||
import com.tongran.agent.client.netty.handler.AgentEncoderHandler;
|
||||
import com.tongran.agent.client.netty.model.Message;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class MultiTargetNettyClient {
|
||||
// 连接标识属性键
|
||||
private static final AttributeKey<String> CONNECTION_KEY = AttributeKey.valueOf("connectionKey");
|
||||
|
||||
// 连接管理器
|
||||
private final Map<String, Channel> connectionMap = new ConcurrentHashMap<>();
|
||||
private final Map<Channel, String> reverseConnectionMap = new ConcurrentHashMap<>();
|
||||
private EventLoopGroup workerGroup;
|
||||
|
||||
@Resource
|
||||
private AgentDecoderHandler decoderHandler;
|
||||
|
||||
@Resource
|
||||
private AgentEncoderHandler encoderHandler;
|
||||
|
||||
@Resource
|
||||
private AgentDispatcherHandler dispatcherHandler;
|
||||
|
||||
/**
|
||||
* 动态创建TCP连接
|
||||
* @param connectionKey 连接唯一标识(格式:ip:port 或自定义)
|
||||
* @param host 目标主机
|
||||
* @param port 目标端口
|
||||
* @param timeout 超时时间(秒)
|
||||
* @return 是否创建成功
|
||||
*/
|
||||
public boolean createConnection(String connectionKey, String host, int port, int timeout) {
|
||||
if (workerGroup == null) {
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
}
|
||||
|
||||
if (connectionMap.containsKey(connectionKey)) {
|
||||
System.out.println("连接已存在: " + connectionKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
Bootstrap bootstrap = new Bootstrap();
|
||||
bootstrap.group(workerGroup)
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout * 1000)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
// 添加编解码器
|
||||
pipeline.addLast(decoderHandler);
|
||||
pipeline.addLast(encoderHandler);
|
||||
// 添加心跳机制
|
||||
// pipeline.addLast(new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));
|
||||
// 添加自定义处理器
|
||||
pipeline.addLast(dispatcherHandler);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
final boolean[] success = {false};
|
||||
|
||||
bootstrap.connect(host, port).addListener((ChannelFuture future) -> {
|
||||
if (future.isSuccess()) {
|
||||
Channel channel = future.channel();
|
||||
connectionMap.put(connectionKey, channel);
|
||||
reverseConnectionMap.put(channel, connectionKey);
|
||||
success[0] = true;
|
||||
System.out.println("连接建立成功: " + connectionKey + " -> " + host + ":" + port);
|
||||
} else {
|
||||
System.err.println("连接建立失败: " + connectionKey + " - " + future.cause().getMessage());
|
||||
success[0] = false;
|
||||
}
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
// 等待连接结果
|
||||
return latch.await(timeout, TimeUnit.SECONDS) && success[0];
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定连接发送消息
|
||||
* @param connectionKey 连接标识
|
||||
* @param message 消息内容
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
public boolean sendMessage(String connectionKey, String message) {
|
||||
Channel channel = connectionMap.get(connectionKey);
|
||||
if (channel == null) {
|
||||
System.err.println("连接不存在: " + connectionKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!channel.isActive()) {
|
||||
System.err.println("连接已断开: " + connectionKey);
|
||||
removeConnection(connectionKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ChannelFuture future = channel.writeAndFlush(message).sync();
|
||||
System.out.println("消息发送成功到: " + connectionKey + " - " + message);
|
||||
return future.isSuccess();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sendMessages(String connectionKey, Message message) {
|
||||
Channel channel = connectionMap.get(connectionKey);
|
||||
if (channel == null) {
|
||||
System.err.println("连接不存在: " + connectionKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!channel.isActive()) {
|
||||
System.err.println("连接已断开: " + connectionKey);
|
||||
removeConnection(connectionKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ChannelFuture future = channel.writeAndFlush(message).sync();
|
||||
System.out.println("消息发送成功到: " + connectionKey + " - " + message);
|
||||
return future.isSuccess();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量发送消息到不同连接
|
||||
* @param messages Map<连接标识, 消息内容>
|
||||
*/
|
||||
public void sendMessages(Map<String, String> messages) {
|
||||
messages.forEach((connectionKey, message) -> {
|
||||
new Thread(() -> {
|
||||
if (sendMessage(connectionKey, message)) {
|
||||
System.out.println("批量发送成功: " + connectionKey);
|
||||
} else {
|
||||
System.err.println("批量发送失败: " + connectionKey);
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭指定连接
|
||||
* @param connectionKey 连接标识
|
||||
*/
|
||||
public void closeConnection(String connectionKey) {
|
||||
Channel channel = connectionMap.get(connectionKey);
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
removeConnection(connectionKey);
|
||||
System.out.println("连接已关闭: " + connectionKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除连接记录
|
||||
* @param connectionKey 连接标识
|
||||
*/
|
||||
private void removeConnection(String connectionKey) {
|
||||
Channel channel = connectionMap.remove(connectionKey);
|
||||
if (channel != null) {
|
||||
reverseConnectionMap.remove(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有连接
|
||||
*/
|
||||
public void shutdown() {
|
||||
// 关闭所有连接
|
||||
connectionMap.forEach((key, channel) -> {
|
||||
if (channel.isActive()) {
|
||||
channel.close();
|
||||
}
|
||||
});
|
||||
|
||||
connectionMap.clear();
|
||||
reverseConnectionMap.clear();
|
||||
|
||||
// 关闭EventLoopGroup
|
||||
if (workerGroup != null) {
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
System.out.println("所有连接已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
* @return 当前活跃连接数
|
||||
*/
|
||||
public int getActiveConnections() {
|
||||
return (int) connectionMap.values().stream()
|
||||
.filter(Channel::isActive)
|
||||
.count();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 客户端处理器
|
||||
// */
|
||||
// private class ClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
// @Override
|
||||
// protected void channelRead0(ChannelHandlerContext ctx, String msg) {
|
||||
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
|
||||
// System.out.println("收到来自 " + connectionKey + " 的消息: " + msg);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
|
||||
// System.err.println("连接 " + connectionKey + " 发生异常: " + cause.getMessage());
|
||||
// ctx.close();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void channelInactive(ChannelHandlerContext ctx) {
|
||||
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
|
||||
// System.out.println("连接断开: " + connectionKey);
|
||||
// removeConnection(connectionKey);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 使用示例
|
||||
*/
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
MultiTargetNettyClient client = new MultiTargetNettyClient();
|
||||
|
||||
try {
|
||||
// 动态创建多个不同IP和端口的连接
|
||||
boolean success1 = client.createConnection("server1", "127.0.0.1", 8080, 5);
|
||||
boolean success2 = client.createConnection("server2", "192.168.1.100", 8081, 5);
|
||||
boolean success3 = client.createConnection("server3", "10.0.0.50", 8082, 5);
|
||||
|
||||
// 等待连接建立
|
||||
Thread.sleep(2000);
|
||||
|
||||
if (success1) {
|
||||
client.sendMessage("server1", "Hello Server 1 from 8080");
|
||||
}
|
||||
|
||||
if (success2) {
|
||||
client.sendMessage("server2", "Hello Server 2 from 8081");
|
||||
}
|
||||
|
||||
if (success3) {
|
||||
client.sendMessage("server3", "Hello Server 3 from 8082");
|
||||
}
|
||||
|
||||
// // 批量发送示例
|
||||
// Map<String, String> batchMessages = Map.of(
|
||||
// "server1", "Batch message to server 1",
|
||||
// "server2", "Batch message to server 2",
|
||||
// "server3", "Batch message to server 3"
|
||||
// );
|
||||
//
|
||||
// client.sendMessages(batchMessages);
|
||||
|
||||
// 保持运行一段时间
|
||||
Thread.sleep(5000);
|
||||
|
||||
System.out.println("当前活跃连接数: " + client.getActiveConnections());
|
||||
|
||||
} finally {
|
||||
client.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public Channel get(String connectionKey){
|
||||
Channel channel = connectionMap.get(connectionKey);
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.tongran.agent.client.netty.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "netty.server")
|
||||
public class AgentNettyConfig {
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.tongran.agent.client.netty.config;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 连接配置信息
|
||||
*/
|
||||
public class ConnectionConfig {
|
||||
private final String connectionKey;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final int timeout;
|
||||
|
||||
public ConnectionConfig(String connectionKey, String host, int port, int timeout) {
|
||||
this.connectionKey = connectionKey;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
// Getter方法
|
||||
public String getConnectionKey() { return connectionKey; }
|
||||
public String getHost() { return host; }
|
||||
public int getPort() { return port; }
|
||||
public int getTimeout() { return timeout; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ConnectionConfig that = (ConnectionConfig) o;
|
||||
return Objects.equals(connectionKey, that.connectionKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(connectionKey);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.tongran.agent.client.core.config.ApplicationProperties;
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.enums.MsgEnum;
|
||||
import com.tongran.agent.client.core.eo.AgentVersionUpdateEO;
|
||||
@@ -13,8 +14,6 @@ import com.tongran.agent.client.netty.annotation.AgentDispatcher;
|
||||
import com.tongran.agent.client.netty.basics.AgentHandler;
|
||||
import com.tongran.agent.client.netty.model.UpMsgResponse;
|
||||
import com.tongran.agent.client.scheduler.service.AdvancedAsyncDownloader;
|
||||
import com.tongran.agent.client.scheduler.service.AppInitializer;
|
||||
import com.tongran.agent.client.scheduler.task.SpecificTimeTaskService;
|
||||
import com.tongran.agent.client.service.AgentService;
|
||||
import com.tongran.agent.client.utils.AgentDataUtil;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
@@ -24,262 +23,89 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class AgentEndpoint {
|
||||
|
||||
@Resource
|
||||
private AppInitializer appInitializer;
|
||||
|
||||
@Resource
|
||||
private SpecificTimeTaskService taskService;
|
||||
|
||||
@Resource
|
||||
private AgentService agentService;
|
||||
|
||||
@Resource
|
||||
private ApplicationProperties properties;
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.注册)
|
||||
@AgentDispatcher(msgId = MsgEnum.建立连接应答)
|
||||
public class ConnectHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.建立连接应答.getValue())
|
||||
.content(json.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.注册应答)
|
||||
public class RegisterHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
JSONObject jsonObject = JSONObject.parseObject(data);
|
||||
if(jsonObject.containsKey("switchBoard")){
|
||||
String switchBoard = jsonObject.getString("switchBoard");
|
||||
jsonObject = JSONObject.parseObject(switchBoard);
|
||||
if(jsonObject.containsKey("community")){
|
||||
GlobalConfig.SWITCH_COMMUNITY = jsonObject.getString("community");
|
||||
}
|
||||
if(jsonObject.containsKey("ip")){
|
||||
GlobalConfig.SWITCH_IP = jsonObject.getString("ip");
|
||||
}
|
||||
if(jsonObject.containsKey("port")){
|
||||
GlobalConfig.SWITCH_PORT = jsonObject.getInteger("port");
|
||||
}
|
||||
if(jsonObject.containsKey("netOID")){
|
||||
String netOID = jsonObject.getString("netOID");
|
||||
if(StringUtils.isNotBlank(netOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(netOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_NET_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.NET_INDEX_PARAM)){
|
||||
GlobalConfig.NET_INDEX_OID = m.get(GlobalConfig.NET_INDEX_PARAM);
|
||||
JSONObject object = JSONObject.parseObject(data);
|
||||
int resCode = 0;
|
||||
if(object.containsKey("resCode")){
|
||||
resCode = object.getInteger("resCode");
|
||||
}
|
||||
//注册成功,更新外置文件注册标识
|
||||
if(resCode == 1){
|
||||
String addRoute = "";
|
||||
if(object.containsKey("addRoute")) {
|
||||
addRoute = object.getString("addRoute");
|
||||
AssertLog.info("注册成功,路由addRoute={}",addRoute);
|
||||
if(StringUtils.isNotBlank(addRoute)){
|
||||
JSONObject addRouteJson = JSONObject.parseObject(addRoute);
|
||||
String name = "";
|
||||
String gateway = "";
|
||||
if(addRouteJson.containsKey("name")){
|
||||
name = addRouteJson.getString("name");
|
||||
}
|
||||
if(addRouteJson.containsKey("gateway")){
|
||||
gateway = addRouteJson.getString("gateway");
|
||||
}
|
||||
if(StringUtils.isNotBlank(name) && StringUtils.isNotBlank(gateway)){
|
||||
agentService.addRoute(name,gateway);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(jsonObject.containsKey("moduleOID")){
|
||||
String moduleOID = jsonObject.getString("moduleOID");
|
||||
if(StringUtils.isNotBlank(moduleOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(moduleOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_MODULE_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.MODULE_INDEX_PARAM)){
|
||||
GlobalConfig.MODULE_INDEX_OID = m.get(GlobalConfig.MODULE_INDEX_PARAM);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(jsonObject.containsKey("mpuOID")){
|
||||
String mpuOID = jsonObject.getString("mpuOID");
|
||||
if(StringUtils.isNotBlank(mpuOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(mpuOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_MPU_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.MPU_INDEX_PARAM)){
|
||||
GlobalConfig.MPU_INDEX_OID = m.get(GlobalConfig.MPU_INDEX_PARAM);
|
||||
}
|
||||
if(object.containsKey("netName")){
|
||||
String netName = object.getString("netName");;
|
||||
AssertLog.info("注册成功,业务网卡={}",netName);
|
||||
if(StringUtils.isNotBlank(netName)){
|
||||
// 添加ipv4 ipv6防火墙策略
|
||||
agentService.addFirewall(netName);
|
||||
}
|
||||
}
|
||||
if(jsonObject.containsKey("pwrOID")){
|
||||
String pwrOID = jsonObject.getString("pwrOID");
|
||||
if(StringUtils.isNotBlank(pwrOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(pwrOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_PWR_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.PWR_INDEX_PARAM)){
|
||||
GlobalConfig.PWR_INDEX_OID = m.get(GlobalConfig.PWR_INDEX_PARAM);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(jsonObject.containsKey("fanOID")){
|
||||
String fanOID = jsonObject.getString("fanOID");
|
||||
if(StringUtils.isNotBlank(fanOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(fanOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_FAN_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.FAN_INDEX_PARAM)){
|
||||
GlobalConfig.FAN_INDEX_OID = m.get(GlobalConfig.FAN_INDEX_PARAM);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(jsonObject.containsKey("otherOID")){
|
||||
String otherOID = jsonObject.getString("otherOID");
|
||||
if(StringUtils.isNotBlank(otherOID)){
|
||||
LinkedHashMap<String, String> map = JSON.parseObject(otherOID, new TypeReference<LinkedHashMap<String, String>>() {});
|
||||
GlobalConfig.SWITCH_OTHER_OID = map;
|
||||
LinkedHashMap<String,String> m = AgentUtil.swapMap(map);
|
||||
if(m.containsKey(GlobalConfig.OTHER_INDEX_PARAM)){
|
||||
GlobalConfig.OTHER_INDEX_OID = m.get(GlobalConfig.OTHER_INDEX_PARAM);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(jsonObject.containsKey("filters")){
|
||||
String filters = jsonObject.getString("filters");
|
||||
if(StringUtils.isNotBlank(filters)){
|
||||
JSONObject object = JSONObject.parseObject(filters);
|
||||
if(object.containsKey("netOID")){
|
||||
String netOID = object.getString("netOID");
|
||||
if(StringUtils.isNotBlank(netOID)){
|
||||
List<String> list = JSON.parseObject(netOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.NET_FILTER = list;
|
||||
}
|
||||
}
|
||||
if(object.containsKey("moduleOID")){
|
||||
String moduleOID = object.getString("moduleOID");
|
||||
if(StringUtils.isNotBlank(moduleOID)){
|
||||
List<String> list = JSON.parseObject(moduleOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.MODULE_FILTER = list;
|
||||
}
|
||||
}
|
||||
if(object.containsKey("mpuOID")){
|
||||
String mpuOID = object.getString("mpuOID");
|
||||
if(StringUtils.isNotBlank(mpuOID)){
|
||||
List<String> list = JSON.parseObject(mpuOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.MPU_FILTER = list;
|
||||
}
|
||||
}
|
||||
if(object.containsKey("pwrOID")){
|
||||
String pwrOID = object.getString("pwrOID");
|
||||
if(StringUtils.isNotBlank(pwrOID)){
|
||||
List<String> list = JSON.parseObject(pwrOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.PWR_FILTER = list;
|
||||
}
|
||||
}
|
||||
if(object.containsKey("fanOID")){
|
||||
String fanOID = object.getString("fanOID");
|
||||
if(StringUtils.isNotBlank(fanOID)){
|
||||
List<String> list = JSON.parseObject(fanOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.FAN_FILTER = list;
|
||||
}
|
||||
}
|
||||
if(object.containsKey("otherOID")){
|
||||
String otherOID = object.getString("otherOID");
|
||||
if(StringUtils.isNotBlank(otherOID)){
|
||||
List<String> list = JSON.parseObject(otherOID, new TypeReference<List<String>>() {});
|
||||
GlobalConfig.OTHER_FILTER = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
//检查外置目录是否存在
|
||||
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getConfPath())){
|
||||
String[] lines = {
|
||||
"register=1"
|
||||
};
|
||||
AgentUtil.bufferedWriter(properties.getConfPath()+"/register.conf",lines);
|
||||
}
|
||||
GlobalConfig.isRegister = true;
|
||||
//取消注册定时任务
|
||||
agentService.cancelTask("register");
|
||||
agentService.start();
|
||||
|
||||
}
|
||||
GlobalConfig.isCollect = true;
|
||||
GlobalConfig.CLIENT_ID = clientId;
|
||||
//调用采集任务
|
||||
try {
|
||||
appInitializer.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
AssertLog.info("SWITCH_NET_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_NET_OID));
|
||||
AssertLog.info("SWITCH_MODULE_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_MODULE_OID));
|
||||
AssertLog.info("SWITCH_MPU_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_MPU_OID));
|
||||
AssertLog.info("SWITCH_PWR_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_PWR_OID));
|
||||
AssertLog.info("SWITCH_FAN_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_FAN_OID));
|
||||
AssertLog.info("SWITCH_OTHER_OID={}",JSON.toJSONString(GlobalConfig.SWITCH_OTHER_OID));
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.注册应答.getValue())
|
||||
.content(json.toString()).build();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.断开)
|
||||
public class DisconnectHandler implements AgentHandler {
|
||||
@AgentDispatcher(msgId = MsgEnum.获取最新策略应答)
|
||||
public class GetPolicyRspHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
GlobalConfig.isCollect = false;
|
||||
//调用采集任务
|
||||
agentService.cancelCollect();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.断开应答.getValue())
|
||||
.content(json.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.开启或更新系统采集)
|
||||
public class SysCollectStartHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
agentService.systemCollectStart(data);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.开启或更新系统采集应答.getValue()).content(json.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.关闭所有系统采集)
|
||||
public class SysCollectStopHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
agentService.systemCollectStop();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.关闭所有系统采集应答.getValue()).content(json.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.开启或更新交换机采集)
|
||||
public class SwitchCollectStartHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
agentService.switchCollectStart(data);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.开启或更新交换机采集应答.getValue()).content(json.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@AgentDispatcher(msgId = MsgEnum.关闭所有交换机采集)
|
||||
public class SwitchCollectStopHandler implements AgentHandler {
|
||||
@Override
|
||||
public UpMsgResponse upHandle(String data, String clientId) {
|
||||
//更改全局变量
|
||||
agentService.switchCollectStop();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMag","");
|
||||
json.put("timestamp",timestamp);
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.关闭所有交换机采集应答.getValue()).content(json.toString()).build();
|
||||
agentService.getPolicy(data);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,18 +149,11 @@ public class AgentEndpoint {
|
||||
if(jsonObject.containsKey("policyName")) {
|
||||
policy.setPolicyName(jsonObject.getString("policyName"));
|
||||
}
|
||||
if(jsonObject.containsKey("files")) {
|
||||
String files = jsonObject.getString("files");
|
||||
List<ScriptPolicyEO.ScriptFile> fileList = JSON.parseObject(files, new TypeReference<List<ScriptPolicyEO.ScriptFile>>() {});
|
||||
policy.setFiles(fileList);
|
||||
if(jsonObject.containsKey("fileUrl")) {
|
||||
policy.setFileUrl(jsonObject.getString("fileUrl"));
|
||||
}
|
||||
if(jsonObject.containsKey("filePath")) {
|
||||
policy.setFilePath(jsonObject.getString("filePath"));
|
||||
}
|
||||
if(jsonObject.containsKey("commands")) {
|
||||
String commands = jsonObject.getString("commands");
|
||||
List<String> commandList = JSON.parseObject(commands, new TypeReference<List<String>>() {});
|
||||
policy.setCommands(commandList);
|
||||
if(jsonObject.containsKey("commandParams")) {
|
||||
policy.setCommandParams(jsonObject.getString("commandParams"));
|
||||
}
|
||||
if(jsonObject.containsKey("method")) {
|
||||
policy.setMethod(jsonObject.getInteger("method"));
|
||||
@@ -343,81 +162,30 @@ public class AgentEndpoint {
|
||||
policy.setPolicyTime(jsonObject.getLong("policyTime"));
|
||||
}
|
||||
boolean isFalse = false;
|
||||
if(StringUtils.isNotBlank(policy.getFilePath())){
|
||||
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(policy.getFilePath())){
|
||||
if(CollectionUtil.isNotEmpty(policy.getFiles())){
|
||||
isFalse = true;
|
||||
GlobalConfig.DOWN_FILES.put(policy.getPolicyName(), policy.getFiles().size());
|
||||
for (ScriptPolicyEO.ScriptFile f : policy.getFiles()) {
|
||||
String finalSaveDir = policy.getFilePath();
|
||||
//文件类型:0、平台文件地址;1、外网HTTP(S)
|
||||
if(f.getFileType() == 0){
|
||||
String filePath = policy.getFilePath() + "/" + f.getFileName();
|
||||
// 移除Base64 URL前缀(如果存在)
|
||||
// if (f.getFileData().contains(",")) {
|
||||
// f.setFileData(f.getFileData().split(",")[1]);
|
||||
// }
|
||||
try {
|
||||
AgentUtil.base64ToFile(f.getFileData(), filePath);
|
||||
System.out.println("文件保存成功: " + filePath);
|
||||
isFalse = true;
|
||||
try {
|
||||
AgentDataUtil.chmod(filePath,"+x");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
isFalse = false;
|
||||
System.err.println("文件保存失败: " + e.getMessage());
|
||||
}
|
||||
}else{
|
||||
AdvancedAsyncDownloader.downloadWithProgress(f.getFileUrl(), finalSaveDir, f.getFileName(), progress -> {
|
||||
System.out.printf("下载进度: %.1f%%\n", progress);
|
||||
}).thenAccept(filePath -> {
|
||||
System.out.println("下载完成: " + filePath);
|
||||
try {
|
||||
AgentDataUtil.chmod(finalSaveDir+"/"+f.getFileName(),"775");
|
||||
int count = GlobalConfig.DOWN_FILES.get(policy.getPolicyName());
|
||||
if(count > 1){
|
||||
GlobalConfig.DOWN_FILES.put(policy.getPolicyName(), count--);
|
||||
}else{
|
||||
//所有文件下载完成,执行脚本命令
|
||||
agentService.command(policy, clientId,MsgEnum.执行脚本策略应答.getValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
System.err.println("下载错误: " + ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
// Java8FileDownloader.DownloadResult result = Java8FileDownloader.downloadFile(f.getFileUrl(), finalSaveDir, f.getFileName());
|
||||
// // 验证下载是否完成
|
||||
// if (result.isSuccess()) {
|
||||
// boolean isComplete = Java8FileDownloader.verifyFileCompletion(
|
||||
// result.getFilePath(), result.getExpectedSize());
|
||||
// if(isComplete){
|
||||
// isFalse = true;
|
||||
// System.out.println("下载完成: " + finalSaveDir+"/"+f.getFileName());
|
||||
// try {
|
||||
// AgentDataUtil.chmod(finalSaveDir+"/"+f.getFileName(),"775");
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }else{
|
||||
// isFalse = false;
|
||||
// System.err.println("下载失败");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getTmpPath()+"/script")){
|
||||
if(StringUtils.isNotBlank(policy.getFileUrl())){
|
||||
isFalse = true;
|
||||
String fileName = policy.getFileUrl().substring(policy.getFileUrl().lastIndexOf('/') + 1);
|
||||
//异步下载脚本
|
||||
AdvancedAsyncDownloader.downloadWithProgress(policy.getFileUrl(), properties.getTmpPath()+"/script", fileName, progress -> {
|
||||
System.out.printf("下载进度: %.1f%%\n", progress);
|
||||
}).thenAccept(filePath -> {
|
||||
System.out.println("下载完成: " + filePath);
|
||||
try {
|
||||
AgentDataUtil.chmod(properties.getTmpPath()+"/script/"+fileName,"775");
|
||||
String params = properties.getTmpPath()+"/script/"+fileName+" " + policy.getCommandParams();
|
||||
policy.setCommandParams(params);
|
||||
//所有文件下载完成,执行脚本命令
|
||||
agentService.command(policy, GlobalConfig.CLIENT_ID,MsgEnum.执行脚本策略应答.getValue());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
System.err.println("下载错误: " + ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
//判断是否需要保存文件
|
||||
@@ -440,48 +208,61 @@ public class AgentEndpoint {
|
||||
AgentVersionUpdateEO versionUpdateEO = JSON.parseObject(data, AgentVersionUpdateEO.class);
|
||||
ScriptPolicyEO policy = ScriptPolicyEO.builder()
|
||||
.method(versionUpdateEO.getMethod())
|
||||
.commandParams("systemctl restart tragent")
|
||||
.policyTime(versionUpdateEO.getPolicyTime())
|
||||
.build();
|
||||
JSONObject jsonObject = JSONObject.parseObject(data);
|
||||
if(jsonObject.containsKey("commands")) {
|
||||
String commands = jsonObject.getString("commands");
|
||||
List<String> commandList = JSON.parseObject(commands, new TypeReference<List<String>>() {});
|
||||
policy.setCommands(commandList);
|
||||
}
|
||||
boolean isFalse = false;
|
||||
if(StringUtils.isNotBlank(versionUpdateEO.getFileUrl())){
|
||||
isFalse = true;
|
||||
String finalSaveDir = versionUpdateEO.getFilePath();
|
||||
AdvancedAsyncDownloader.downloadWithProgress(versionUpdateEO.getFileUrl(), finalSaveDir, null, progress -> {
|
||||
String fileName = versionUpdateEO.getFileUrl().substring(versionUpdateEO.getFileUrl().lastIndexOf('/') + 1);
|
||||
AdvancedAsyncDownloader.downloadWithProgress(versionUpdateEO.getFileUrl(), properties.getTempPath(), fileName, progress -> {
|
||||
System.out.printf("下载进度: %.1f%%\n", progress);
|
||||
}).thenAccept(filePath -> {
|
||||
System.out.println("下载完成: " + filePath);
|
||||
try {
|
||||
//更改全局变量
|
||||
GlobalConfig.isCollect = false;
|
||||
//调用采集任务
|
||||
agentService.cancelCollect();
|
||||
//所有文件下载完成,执行脚本命令
|
||||
agentService.command(policy, clientId,MsgEnum.Agent版本更新应答.getValue());
|
||||
//验证文件MD5
|
||||
String md5 = AgentUtil.getFileMD5(properties.getTempPath()+"/"+fileName);
|
||||
if(StringUtils.isNotBlank(md5) && StringUtils.isNotBlank(versionUpdateEO.getFileMd5())
|
||||
&& StringUtils.equals(md5,versionUpdateEO.getFileMd5())){
|
||||
//更改全局变量
|
||||
// GlobalConfig.isCollect = false;
|
||||
//调用采集任务
|
||||
// agentService.cancelCollect();
|
||||
//所有文件下载完成,执行脚本命令
|
||||
agentService.command(policy, clientId,MsgEnum.Agent版本更新应答.getValue());
|
||||
}else{
|
||||
//发送更新失败
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",0);
|
||||
json.put("resMsg", "版本策略MD5验证失败");
|
||||
json.put("timestamp",timestamp);
|
||||
agentService.sendMessage(MsgEnum.Agent版本更新应答.getValue(), json.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
System.err.println("下载错误: " + ex.getMessage());
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",0);
|
||||
json.put("resMsg", "版本策略文件下载失败");
|
||||
json.put("timestamp",timestamp);
|
||||
agentService.sendMessage(MsgEnum.Agent版本更新应答.getValue(), json.toString());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
//判断是否需要保存文件
|
||||
if(!isFalse){
|
||||
agentService.command(policy, clientId,MsgEnum.Agent版本更新应答.getValue());
|
||||
return null;
|
||||
}else{
|
||||
if(isFalse){
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMsg", "Agent版本更新文件保存中");
|
||||
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ public class AgentDecoderHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
byteBuf.release(); // 释放 ByteBuf 资源(重要!)
|
||||
} else {
|
||||
System.out.println("Unexpected message type: " + msg.getClass());
|
||||
AssertLog.error("Unexpected message type: " + msg.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.tongran.agent.client.netty.handler;
|
||||
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.session.SessionManager;
|
||||
import com.tongran.agent.client.scheduler.service.AppInitializer;
|
||||
import com.tongran.agent.client.service.AgentService;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Description: TCP消息适配器
|
||||
*/
|
||||
@Component
|
||||
@ChannelHandler.Sharable
|
||||
public class TCPListenHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Resource
|
||||
private AppInitializer appInitializer;
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
@Resource
|
||||
private AgentService agentService;
|
||||
|
||||
public TCPListenHandler() {
|
||||
this.sessionManager = SessionManager.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
AssertLog.info("<<<<<<[终端连接]{}", ctx.channel().remoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
AssertLog.info(">>>>>>[断开连接]{}", sessionManager.client(ctx));
|
||||
sessionManager.remove(ctx.channel());
|
||||
try {
|
||||
GlobalConfig.isCollect = false;
|
||||
agentService.cancelCollect();
|
||||
appInitializer.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
|
||||
if (e instanceof IOException) {
|
||||
AssertLog.info("<<<<<<[终端断开连接]{} {}", sessionManager.client(ctx), e.getMessage());
|
||||
agentService.cancelCollect();
|
||||
} else {
|
||||
AssertLog.info(">>>>>>[消息处理异常]" + sessionManager.client(ctx), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
IdleState state = event.state();
|
||||
if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE || state == IdleState.ALL_IDLE) {
|
||||
AssertLog.warn(">>>>>>[终端心跳超时]{} {}", state, sessionManager.client(ctx));
|
||||
sessionManager.remove(ctx.channel());
|
||||
ctx.close();
|
||||
try {
|
||||
GlobalConfig.isCollect = false;
|
||||
appInitializer.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.tongran.agent.client.netty.handler;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Description: UDP消息适配器
|
||||
*/
|
||||
@Component
|
||||
@ChannelHandler.Sharable
|
||||
public class UDPListenHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
DatagramPacket packet = (DatagramPacket) msg;
|
||||
ByteBuf buf = packet.content();
|
||||
buf.clear();// 暂未实现
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,4 +52,34 @@ public class SchedulerConfig {
|
||||
executor.initialize(); // 重要:必须调用initialize()
|
||||
return executor;
|
||||
}
|
||||
// 在SchedulerConfig中添加专用TCPDUMP线程池
|
||||
@Bean("tcpdumpTaskExecutor")
|
||||
public ThreadPoolTaskExecutor tcpdumpTaskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(2); // 核心线程数
|
||||
executor.setMaxPoolSize(10); // 最大线程数(根据网卡数量调整)
|
||||
executor.setQueueCapacity(20); // 队列容量
|
||||
executor.setKeepAliveSeconds(300);// 空闲线程存活时间
|
||||
executor.setThreadNamePrefix("tcpdump-worker-");
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
// 新增:心跳专用线程池
|
||||
@Bean("heartbeatExecutor")
|
||||
public ThreadPoolTaskExecutor heartbeatExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(2);
|
||||
executor.setMaxPoolSize(4);
|
||||
executor.setQueueCapacity(0); // 重要:设为0,不要队列!
|
||||
executor.setKeepAliveSeconds(30);
|
||||
executor.setThreadNamePrefix("heartbeat-");
|
||||
|
||||
// 重要:使用CallerRunsPolicy,确保心跳一定执行
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.tongran.agent.client.scheduler.job;
|
||||
|
||||
import com.tongran.agent.client.core.config.ApplicationProperties;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import com.tongran.agent.client.utils.FileCleaner;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DailyJob {
|
||||
|
||||
// @Value("${spring.profiles.active}")
|
||||
// private String active;
|
||||
|
||||
@Resource
|
||||
private ApplicationProperties properties;
|
||||
|
||||
@Scheduled(cron = "0 0 2 * * ?")
|
||||
void dailyTask() {
|
||||
// if (!StringUtils.equals(active, "prod")) {
|
||||
// return;
|
||||
// }
|
||||
LocalDateTime start = LocalDateTime.now();
|
||||
AssertLog.info("临时文件定时清理作业开始:{}", LocalDateTime.now());
|
||||
String[] paths = new String[]{properties.getTmpPath(), properties.getTempPath()};
|
||||
for (String path : paths) {
|
||||
if(StringUtils.isNotBlank(path)){
|
||||
FileCleaner.cleanOldFiles(path,30);
|
||||
}
|
||||
}
|
||||
LocalDateTime end = LocalDateTime.now();
|
||||
AssertLog.info("临时文件定时清理作业结束:{},耗时={}s", end, Duration.between(start, end).getSeconds());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,17 +1,36 @@
|
||||
package com.tongran.agent.client.scheduler.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.enums.MsgEnum;
|
||||
import com.tongran.agent.client.core.vo.NetworkInterfaceInfo;
|
||||
import com.tongran.agent.client.netty.MultiTargetNettyClient;
|
||||
import com.tongran.agent.client.netty.model.Message;
|
||||
import com.tongran.agent.client.service.AgentService;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import com.tongran.agent.client.utils.NetworkInterfaceUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class AppInitializer implements CommandLineRunner {
|
||||
|
||||
private final BusinessTasks businessTasks;
|
||||
private final DynamicTaskService dynamicTaskService;
|
||||
|
||||
@Resource
|
||||
private MultiTargetNettyClient client;
|
||||
|
||||
@Resource
|
||||
private AgentService agentService;
|
||||
|
||||
// 使用构造函数注入,避免循环依赖
|
||||
public AppInitializer(DynamicTaskService dynamicTaskService,
|
||||
@Lazy BusinessTasks businessTasks) {
|
||||
@@ -21,64 +40,105 @@ public class AppInitializer implements CommandLineRunner {
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
System.out.println("应用启动完成,开始初始化定时任务...");
|
||||
if(GlobalConfig.isCollect){
|
||||
// 开启定时任务
|
||||
initScheduledTasks();
|
||||
}
|
||||
System.out.println("定时任务初始化完成");
|
||||
AssertLog.info("创建连接");
|
||||
// 建立连接
|
||||
initConnection();
|
||||
// 检查保活脚本
|
||||
agentService.checkTrAgent();
|
||||
AssertLog.info("创建连接完成");
|
||||
}
|
||||
|
||||
private void initScheduledTasks() {
|
||||
// 每30秒执行心跳任务
|
||||
AssertLog.info("初始化启动心跳定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 30000);
|
||||
dynamicTaskService.scheduleTask("heartbeat",
|
||||
businessTasks::heartbeatTask, 15000, 30000);
|
||||
private void initConnection() {
|
||||
boolean success = true;
|
||||
int activeConnect = client.getActiveConnections();
|
||||
AssertLog.info("activeConnect={}",activeConnect);
|
||||
if(activeConnect == 0){
|
||||
success = agentService.connection();
|
||||
}
|
||||
if(success){
|
||||
AssertLog.info("连接成功,发送初始连接消息");
|
||||
//连接成功,发送初始连接消息
|
||||
// long timestamp = System.currentTimeMillis();
|
||||
// timestamp = Math.round(timestamp / 1000.0);
|
||||
// JSONObject object = new JSONObject();
|
||||
// object.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
// object.put("sn", GlobalConfig.DEVICE_SN);
|
||||
// object.put("timestamp", timestamp);
|
||||
// Message msg = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.建立连接.getValue()).data(object.toString()).build();
|
||||
// // 将对象转为 JSON 字符串
|
||||
// client.sendMessages(GlobalConfig.CLIENT_ID, msg);
|
||||
// // 等待3秒
|
||||
// try {
|
||||
// TimeUnit.SECONDS.sleep(3);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
//判断是否发送注册信息
|
||||
if(GlobalConfig.isRegister){
|
||||
//添加路由
|
||||
agentService.addRoute(null,null);
|
||||
//已注册,发送心跳
|
||||
// 创建心跳定时任务
|
||||
AssertLog.info("启动心跳定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 30000);
|
||||
dynamicTaskService.scheduleTask("heartbeat", businessTasks::heartbeatTask, 15000, 30000);
|
||||
// 创建获取最新策略定时任务
|
||||
long milli = AgentUtil.getMillisToNextMinute() + 60000;
|
||||
AssertLog.info("启动更新策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 60000);
|
||||
dynamicTaskService.scheduleTask("policy", businessTasks::policyTask, milli, 60000);
|
||||
|
||||
// long milli = AgentUtil.getMillisToNextMinute() + 60000;
|
||||
// // 每300秒执行CPU信息采集任务
|
||||
// AssertLog.info("初始化启动CPU信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("cpu",
|
||||
// businessTasks::cpuTask, milli, 300000); // 25秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行磁盘信息采集任务
|
||||
// AssertLog.info("初始化启动磁盘信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("disk",
|
||||
// businessTasks::diskTask, milli, 300000); // 35秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行系统信息采集任务
|
||||
// AssertLog.info("初始化启动系统信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("system",
|
||||
// businessTasks::systemTask, milli, 300000); // 40秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行容器信息采集任务
|
||||
// AssertLog.info("初始化启动容器信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("docker",
|
||||
// businessTasks::dockerTask, milli, 300000); // 45秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行内存信息采集任务
|
||||
// AssertLog.info("初始化启动内存信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("memory",
|
||||
// businessTasks::memoryTask, milli, 300000); // 55秒后开始,每300秒执行
|
||||
//
|
||||
//
|
||||
// //获取当前时间距离下一个“分钟为 0 或 5”的时间点还差多少毫秒
|
||||
// long millis = AgentUtil.millisecondsToNext5Minute();
|
||||
// // 每300秒执行网络信息采集任务
|
||||
// AssertLog.info("初始化启动网络信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", millis, 300000);
|
||||
// dynamicTaskService.scheduleTask("net",
|
||||
// businessTasks::netTask, millis, 300000); // 65秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行挂载信息采集任务
|
||||
// AssertLog.info("初始化启动挂载信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
// dynamicTaskService.scheduleTask("point",
|
||||
// businessTasks::pointTask, milli, 300000); // 75秒后开始,每300秒执行
|
||||
//
|
||||
// // 每300秒执行交换机信息采集任务
|
||||
// AssertLog.info("初始化启动交换机信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", millis, 300000);
|
||||
// dynamicTaskService.scheduleTask("switchBoard",
|
||||
// businessTasks::switchBoardTask, millis, 300000); // 85秒后开始,每300秒执行
|
||||
// 创建多网IP探测上报
|
||||
AssertLog.info("启动多网IP探测定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
dynamicTaskService.scheduleTask("networkDetect", businessTasks::networkDetectTask, milli, 300000);
|
||||
|
||||
// 检测监控策略配置
|
||||
AssertLog.info("检测监控策略配置");
|
||||
agentService.checkMonitor();
|
||||
// 检测防火墙策略配置
|
||||
AssertLog.info("检测防火墙策略配置");
|
||||
agentService.checkFirewall();
|
||||
AssertLog.info("启动检查防火墙策略定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000);
|
||||
dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000);
|
||||
AssertLog.info("检测agent更新配置");
|
||||
agentService.checkAgentUpdate();
|
||||
AssertLog.info("启动frpc保活定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000);
|
||||
dynamicTaskService.scheduleTask("checkFrpc", businessTasks::checkFrpcTask, 15000, 600000);
|
||||
AssertLog.info("启动frpc状态上报定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 300000);
|
||||
dynamicTaskService.scheduleTask("upFrpcMsg", businessTasks::upFrpcMsgTask, 15000, 300000);
|
||||
long milliFive = AgentUtil.millisecondsToNext5Minute();
|
||||
AssertLog.info("启动本地存储流量信息上报定时任务 - 延迟: {}ms, 间隔: {}ms", milliFive, 7200000);
|
||||
dynamicTaskService.scheduleTask("upTempTraffic", businessTasks::upTempTraffic, milliFive, 7200000);
|
||||
AssertLog.info("检测PppoE配置");
|
||||
agentService.handleRebootRecovery();
|
||||
AssertLog.info("检测tcpdump探测时间配置");
|
||||
agentService.checkTcpdumpTimes();
|
||||
}else{
|
||||
//未注册,发送注册
|
||||
try {
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
// List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
|
||||
List<NetworkInterfaceInfo> infos = NetworkInterfaceUtil.collectNetworkInfo();
|
||||
JSONObject objects = new JSONObject();
|
||||
objects.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
objects.put("sn", GlobalConfig.DEVICE_SN);
|
||||
objects.put("networkInfo", JSON.toJSONString(infos));
|
||||
objects.put("timestamp", timestamps);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.注册.getValue()).data(objects.toString()).build();
|
||||
// 将对象转为 JSON 字符串
|
||||
client.sendMessages(GlobalConfig.CLIENT_ID, message);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//设置注册定时任务,收注册成功后取消该定时任务
|
||||
dynamicTaskService.scheduleTask("register", businessTasks::registerTask, 60000, 300000);
|
||||
}
|
||||
}else{
|
||||
//连接建立失败,3分钟后重试
|
||||
dynamicTaskService.scheduleTask("connection", businessTasks::connectionTask, 60000, 180000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,19 +9,22 @@ import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.enums.MsgEnum;
|
||||
import com.tongran.agent.client.core.session.SessionManager;
|
||||
import com.tongran.agent.client.core.vo.*;
|
||||
import com.tongran.agent.client.netty.MultiTargetNettyClient;
|
||||
import com.tongran.agent.client.netty.config.AgentNettyConfig;
|
||||
import com.tongran.agent.client.netty.model.Message;
|
||||
import com.tongran.agent.client.service.*;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import com.tongran.agent.client.utils.NetworkInterfaceUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Component
|
||||
@@ -53,26 +56,15 @@ public class BusinessTasks {
|
||||
private final AtomicInteger systemUptimeTask = new AtomicInteger(0);
|
||||
private final AtomicInteger procNumTask = new AtomicInteger(0);
|
||||
|
||||
private final AtomicInteger switchNetTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchModuleTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchMpuTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchPwrTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchFanTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysDescrTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysObjectIDTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysUpTimeTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysContactTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysNameTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchSysLocationTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchHwStackSystemMacTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchEntIndexTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchEntPhysicalNameTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchEntPhysicalSoftwareRevTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchHwEntityCpuUsageTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchHwEntityMemUsageTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchHwAveragePowerTask = new AtomicInteger(0);
|
||||
private final AtomicInteger switchHwCurrentPowerTask = new AtomicInteger(0);
|
||||
|
||||
private final AtomicInteger policyTask = new AtomicInteger(0);
|
||||
private final AtomicInteger registerTask = new AtomicInteger(0);
|
||||
private final AtomicInteger connectionTask = new AtomicInteger(0);
|
||||
private final AtomicInteger networkDetectTask = new AtomicInteger(0);
|
||||
private final AtomicInteger checkFirewallTask = new AtomicInteger(0);
|
||||
private final AtomicInteger checkFrpcTask = new AtomicInteger(0);
|
||||
private final AtomicInteger upFrpcMsgTask = new AtomicInteger(0);
|
||||
private final AtomicInteger upMacvlanStatusTask = new AtomicInteger(0);
|
||||
private final AtomicInteger upTempTrafficTask = new AtomicInteger(0);
|
||||
|
||||
|
||||
|
||||
@@ -103,9 +95,8 @@ public class BusinessTasks {
|
||||
|
||||
@Resource
|
||||
private NetService netService;
|
||||
|
||||
@Resource
|
||||
private SwitchBoardService switchBoardService;
|
||||
private NetBusinessService netBusinessService;
|
||||
|
||||
@Resource
|
||||
private SystemService systemService;
|
||||
@@ -116,28 +107,52 @@ public class BusinessTasks {
|
||||
@Resource
|
||||
private ApplicationProperties properties;
|
||||
|
||||
@Resource
|
||||
private MultiTargetNettyClient client;
|
||||
|
||||
@Resource
|
||||
private AgentNettyConfig config;
|
||||
|
||||
@Resource
|
||||
private AgentService agentService;
|
||||
|
||||
/**
|
||||
* 任务1:心跳上报任务
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
@Async("heartbeatExecutor")
|
||||
public void heartbeatTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = heartbeatTask.incrementAndGet();
|
||||
AssertLog.info("心跳定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 业务处理
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送心跳包
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("strength","31");
|
||||
object.put("name", properties.getName());
|
||||
object.put("version", properties.getVersion());
|
||||
object.put("startupTime", GlobalConfig.startupTime);
|
||||
object.put("timestamp",timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.心跳上报.getValue()).data(object.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送心跳包={}",JSON.toJSONString(message));
|
||||
boolean success = true;
|
||||
int activeConnect = client.getActiveConnections();
|
||||
if(activeConnect == 0){
|
||||
success = agentService.connection();
|
||||
}
|
||||
if(success){
|
||||
// 业务处理
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
// 发送心跳包
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
object.put("logicalNode", agentService.getLogicalNode());
|
||||
object.put("sn", GlobalConfig.DEVICE_SN);
|
||||
object.put("strength","31");
|
||||
object.put("name", properties.getName());
|
||||
object.put("version", properties.getVersion());
|
||||
object.put("startupTime", GlobalConfig.startupTime);
|
||||
object.put("timestamp",timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.心跳上报.getValue()).data(object.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送心跳包={}",JSON.toJSONString(message));
|
||||
}
|
||||
}else{
|
||||
AssertLog.info("心跳定时任务执行失败-连接断开");
|
||||
}
|
||||
AssertLog.info("心跳定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
@@ -246,13 +261,31 @@ public class BusinessTasks {
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送网卡信息包
|
||||
String data = "";
|
||||
String businessData = "";
|
||||
List<NetVO> list = netService.netList(timestamp);
|
||||
if(CollectionUtil.isNotEmpty(list)){
|
||||
List<NetBusinessVO> businessNetList = netBusinessService.netList(timestamp);
|
||||
// 2. 优化MAC地址赋值(使用HashMap,避免重复分割)
|
||||
if (CollectionUtil.isNotEmpty(list)) {
|
||||
data = JSONArray.toJSONString(list);
|
||||
}
|
||||
if (CollectionUtil.isNotEmpty(businessNetList)) {
|
||||
businessData = JSONArray.toJSONString(businessNetList);
|
||||
}
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.网络上报.getValue()).data(data).build();
|
||||
// 将本次流量存储到本地文件
|
||||
String[] lines = {
|
||||
"traffic=" + data,
|
||||
};
|
||||
|
||||
// 检查目录并写入配置文件
|
||||
if (AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getTempTrafficPath())) {
|
||||
AgentUtil.bufferedWriter(properties.getTempTrafficPath() + "/" + timestamp+".conf", lines);
|
||||
}
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送网络信息包={}",JSON.toJSONString(message));
|
||||
Message businessMessage = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.业务网络上报.getValue()).data(businessData).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), businessMessage);
|
||||
AssertLog.info("发送业务网络信息包={}",JSON.toJSONString(businessMessage));
|
||||
}
|
||||
AssertLog.info("网络信息采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
@@ -354,8 +387,22 @@ public class BusinessTasks {
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.系统其他上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送系统其他信息-内存利用率信息包={}",JSON.toJSONString(message));
|
||||
// 执行内存详情包
|
||||
List<MemoryDetailsVO> memDetailsList = memoryService.getMemoryDetails();
|
||||
Integer total = memoryService.getMemorySlotsNum();
|
||||
JSONObject object = new JSONObject();
|
||||
if(memDetailsList != null && !memDetailsList.isEmpty()){
|
||||
object.put("details", memDetailsList);
|
||||
if(total != null){
|
||||
object.put("total", total);
|
||||
}
|
||||
}
|
||||
object.put("timestamp", timestamp);
|
||||
Message memDetails = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.内存详情上报.getValue()).data(object.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), memDetails);
|
||||
AssertLog.info("发送内存详情信息包={}",JSON.toJSONString(memDetails));
|
||||
}
|
||||
AssertLog.info("系统其他信息-内存利用率采集定时任务执行 - task #{} completed", count);
|
||||
AssertLog.info("系统其他信息-内存利用率采集/内存详情定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,480 +781,286 @@ public class BusinessTasks {
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务26:交换机网络采集
|
||||
* 任务26:获取最新策略
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchNetTask() {
|
||||
public void policyTask() {
|
||||
long timestamp = AgentUtil.roundMinutes();
|
||||
int count = switchNetTask.incrementAndGet();
|
||||
AssertLog.info("交换机网络采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
int count = policyTask.incrementAndGet();
|
||||
AssertLog.info("获取最新策略定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchNetCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
// 发送更新策略
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
object.put("sn",GlobalConfig.DEVICE_SN);
|
||||
object.put("timestamp",timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.获取最新策略.getValue()).data(object.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机网络信息包={}",JSON.toJSONString(message));
|
||||
AssertLog.info("发送获取最新策略信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机网络采集定时任务执行 - task #{} completed", count);
|
||||
AssertLog.info("获取最新策略定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务27:交换机光模块采集
|
||||
* 任务27:注册重试
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchModuleTask() {
|
||||
long timestamp = AgentUtil.roundMinutes();
|
||||
int count = switchModuleTask.incrementAndGet();
|
||||
AssertLog.info("交换机光模块采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchModuleCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机光模块信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机光模块采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务28:交换机MPU采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchMpuTask() {
|
||||
public void registerTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchMpuTask.incrementAndGet();
|
||||
AssertLog.info("交换机MPU采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchMpuCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机MPU信息包={}",JSON.toJSONString(message));
|
||||
int count = registerTask.incrementAndGet();
|
||||
AssertLog.info("注册重试定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
boolean success = true;
|
||||
int activeConnect = client.getActiveConnections();
|
||||
AssertLog.info("注册重试定时任务执行 - 时间: {},activeConnect={}", LocalDateTime.now(), activeConnect);
|
||||
if(activeConnect == 0 || count > 1){
|
||||
success = agentService.connection();
|
||||
}
|
||||
AssertLog.info("交换机MPU采集定时任务执行 - task #{} completed", count);
|
||||
if(success){
|
||||
AssertLog.info("连接成功,发送初始连接消息");
|
||||
try {
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
// List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
|
||||
List<NetworkInterfaceInfo> infos = NetworkInterfaceUtil.collectNetworkInfo();
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
object.put("sn", GlobalConfig.DEVICE_SN);
|
||||
object.put("networkInfo", JSON.toJSONString(infos));
|
||||
object.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.注册.getValue()).data(object.toString()).build();
|
||||
// 将对象转为 JSON 字符串
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送注册重试={}",JSON.toJSONString(message));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}else{
|
||||
AssertLog.info("注册重试定时任务执行 - 时间: {},建立连接失败", LocalDateTime.now());
|
||||
}
|
||||
AssertLog.info("注册重试定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务29:交换机电源采集
|
||||
* 任务28:建立连接重试
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchPwrTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchPwrTask.incrementAndGet();
|
||||
AssertLog.info("交换机电源采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchPwrCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
public void connectionTask() {
|
||||
int count = connectionTask.incrementAndGet();
|
||||
AssertLog.info("建立连接重试定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
boolean success = client.createConnection(GlobalConfig.CLIENT_ID, config.getHost(), config.getPort(), 5);
|
||||
if(success){
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机电源信息包={}",JSON.toJSONString(message));
|
||||
//连接成功,发送初始连接消息
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
object.put("sn", GlobalConfig.DEVICE_SN);
|
||||
object.put("timestamp", timestamp);
|
||||
Message msg = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.建立连接.getValue()).data(object.toString()).build();
|
||||
// 将对象转为 JSON 字符串
|
||||
client.sendMessages(GlobalConfig.CLIENT_ID, msg);
|
||||
//判断是否发送注册信息
|
||||
if(GlobalConfig.isRegister){
|
||||
//已注册,发送心跳
|
||||
// 创建心跳定时任务
|
||||
AssertLog.info("启动心跳定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 30000);
|
||||
dynamicTaskService.scheduleTask("heartbeat", businessTasks::heartbeatTask, 15000, 30000);
|
||||
// 创建获取最新策略定时任务
|
||||
long milli = AgentUtil.getMillisToNextMinute() + 60000;
|
||||
AssertLog.info("启动更新策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 60000);
|
||||
dynamicTaskService.scheduleTask("policy", businessTasks::policyTask, milli, 60000);
|
||||
// 创建多网IP探测上报
|
||||
AssertLog.info("启动多网IP探测定时任务Retry - 延迟: {}ms, 间隔: {}ms", milli, 300000);
|
||||
dynamicTaskService.scheduleTask("networkDetect", businessTasks::networkDetectTask, milli, 300000);
|
||||
|
||||
// 检测监控策略配置
|
||||
AssertLog.info("检测监控策略配置Retry");
|
||||
agentService.checkMonitor();
|
||||
// 检测防火墙策略配置
|
||||
AssertLog.info("检测防火墙策略配置");
|
||||
agentService.checkFirewall();
|
||||
AssertLog.info("启动检查防火墙策略定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000);
|
||||
dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000);
|
||||
AssertLog.info("检测agent更新配置");
|
||||
agentService.checkAgentUpdate();
|
||||
AssertLog.info("启动frpc保活定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000);
|
||||
dynamicTaskService.scheduleTask("checkFrpc", businessTasks::checkFrpcTask, 15000, 600000);
|
||||
AssertLog.info("启动frpc状态上报定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 300000);
|
||||
dynamicTaskService.scheduleTask("upFrpcMsg", businessTasks::upFrpcMsgTask, 15000, 300000);
|
||||
long milliFive = AgentUtil.millisecondsToNext5Minute();
|
||||
AssertLog.info("启动本地存储流量信息上报定时任务 - 延迟: {}ms, 间隔: {}ms", milliFive, 7200000);
|
||||
dynamicTaskService.scheduleTask("upTempTraffic", businessTasks::upTempTraffic, milliFive, 7200000);
|
||||
AssertLog.info("检测PppoE配置");
|
||||
agentService.handleRebootRecovery();
|
||||
AssertLog.info("检测tcpdump探测时间配置");
|
||||
agentService.checkTcpdumpTimes();
|
||||
}else{
|
||||
//未注册,发送注册
|
||||
try {
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
// List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
|
||||
List<NetworkInterfaceInfo> infos = NetworkInterfaceUtil.collectNetworkInfo();
|
||||
JSONObject objects = new JSONObject();
|
||||
objects.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
objects.put("sn", GlobalConfig.DEVICE_SN);
|
||||
objects.put("networkInfo", JSON.toJSONString(infos));
|
||||
objects.put("timestamp", timestamps);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.注册.getValue()).data(objects.toString()).build();
|
||||
// 将对象转为 JSON 字符串
|
||||
client.sendMessages(GlobalConfig.CLIENT_ID, message);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//设置注册定时任务,收注册成功后取消该定时任务
|
||||
dynamicTaskService.scheduleTask("register", businessTasks::registerTask, 0, 300000);
|
||||
}
|
||||
dynamicTaskService.cancelTask("connection");
|
||||
}
|
||||
AssertLog.info("交换机电源采集定时任务执行 - task #{} completed", count);
|
||||
AssertLog.info("建立连接重试定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务30:交换机风扇采集
|
||||
* 任务29:多网IP探测
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchFanTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchFanTask.incrementAndGet();
|
||||
AssertLog.info("交换机风扇采集采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
public void networkDetectTask() {
|
||||
int count = networkDetectTask.incrementAndGet();
|
||||
AssertLog.info("多网IP探测定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchFanCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
// 发送多网IP探测
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
List<NetworkInterfaceInfo> infos = null;
|
||||
try {
|
||||
// infos = AgentUtil.collectNetworkInfo();
|
||||
infos = NetworkInterfaceUtil.collectNetworkInfo();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
JSONObject objects = new JSONObject();
|
||||
objects.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
objects.put("sn", GlobalConfig.DEVICE_SN);
|
||||
objects.put("networkInfo", JSON.toJSONString(infos));
|
||||
objects.put("timestamp", timestamps);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.多网IP探测上报.getValue()).data(objects.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机风扇信息包={}",JSON.toJSONString(message));
|
||||
AssertLog.info("发送多网IP探测信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("发送多网IP探测定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
/**
|
||||
* 任务30:监测防火墙策略
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void checkFirewallTask() {
|
||||
int count = checkFirewallTask.incrementAndGet();
|
||||
AssertLog.info("监测防火墙策略定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 检测防火墙策略是否存在
|
||||
agentService.checkAndAddFirewallPeriodically();
|
||||
}else{
|
||||
AssertLog.info("断开连接--监测防火墙策略定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 任务31:frpc保活机制
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void checkFrpcTask() {
|
||||
int count = checkFrpcTask.incrementAndGet();
|
||||
AssertLog.info("frpc保活机制定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// frpc保活机制
|
||||
agentService.checkFrpc();
|
||||
}else{
|
||||
AssertLog.info("断开连接--frpc保活机制定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 任务32:frpc状态上报
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void upFrpcMsgTask() {
|
||||
int count = upFrpcMsgTask.incrementAndGet();
|
||||
AssertLog.info("frpc状态上报定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// frpc状态上报
|
||||
agentService.upFrpcMsg();
|
||||
}else{
|
||||
AssertLog.info("断开连接--frpc状态上报定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
AssertLog.info("交换机风扇采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务31:交换机其他信息-系统描述采集
|
||||
* 任务33:macvlan状态上报
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysDescrTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysDescrTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统描述采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
public void upMacvlanStatus() {
|
||||
int count = upMacvlanStatusTask.incrementAndGet();
|
||||
AssertLog.info("macvlan状态上报定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysDescrCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
|
||||
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
|
||||
}
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
List<MacVlanVO> macVlanVOList = null;
|
||||
try {
|
||||
macVlanVOList = agentService.upMacvlanStatus();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(macVlanVOList != null && !macVlanVOList.isEmpty()){
|
||||
JSONObject objects = new JSONObject();
|
||||
objects.put("clientId", GlobalConfig.CLIENT_ID);
|
||||
objects.put("macVlans", macVlanVOList);
|
||||
objects.put("timestamp", timestamps);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.macvlan状态上报.getValue()).data(objects.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送macvlan状态上报信息包={}",JSON.toJSONString(message));
|
||||
}else {
|
||||
AssertLog.info("未检测到虚拟网卡,不进行macvlan状态上报");
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统描述信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统描述采集定时任务执行 - task #{} completed", count);
|
||||
AssertLog.info("发送macvlan状态上报定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务32:交换机其他信息-系统ObjectID采集
|
||||
* 任务34:本地存储流量信息上报
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysObjectIDTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysObjectIDTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统ObjectID采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
public void upTempTraffic() {
|
||||
int count = upTempTrafficTask.incrementAndGet();
|
||||
AssertLog.info("本地存储流量信息上报定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysObjectIDCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统ObjectID信息包={}",JSON.toJSONString(message));
|
||||
agentService.checkTraffic();
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统ObjectID采集定时任务执行 - task #{} completed", count);
|
||||
AssertLog.info("本地存储流量信息上报定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务33:交换机其他信息-系统运行时间采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysUpTimeTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysUpTimeTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统运行时间采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysUpTimeCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统运行时间信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统运行时间采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务34:交换机其他信息-系统联系信息采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysContactTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysContactTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统联系信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysContactCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统联系信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统联系信息采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务35:交换机其他信息-系统名称采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysNameTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysNameTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统名称采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysNameCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统名称信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统名称信息采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务36:交换机其他信息-系统位置采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchSysLocationTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchSysLocationTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统位置采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchSysLocationCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统位置信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统位置采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务37:交换机其他信息-系统MAC地址采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchHwStackSystemMacTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchHwStackSystemMacTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统MAC地址采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchHwStackSystemMacCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统MAC地址信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统MAC地址采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务38:交换机其他信息-设备索引采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchEntIndexTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchEntIndexTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-设备索引采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchEntIndexCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-设备索引信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-设备索引采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务39:交换机其他信息-设备名称采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchEntPhysicalNameTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchEntPhysicalNameTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-设备名称采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchEntPhysicalNameCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-设备名称信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-设备名称采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务40:交换机其他信息-设备软件版本采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchEntPhysicalSoftwareRevTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchEntPhysicalSoftwareRevTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-设备软件版本采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchEntPhysicalSoftwareRevCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-设备软件版本信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-设备软件版本采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务41:交换机其他信息-设备CPU使用率采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchHwEntityCpuUsageTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchHwEntityCpuUsageTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-设备CPU使用率采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchHwEntityCpuUsageCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-设备CPU使用率信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-设备CPU使用率采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务42:交换机其他信息-设备内存使用率采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchHwEntityMemUsageTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchHwEntityMemUsageTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-设备内存使用率采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchHwEntityMemUsageCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-设备内存使用率信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-设备内存使用率采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务43:交换机其他信息-系统平均功率采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchHwAveragePowerTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchHwAveragePowerTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统平均功率采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchHwAveragePowerCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统平均功率信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统平均功率采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务44:交换机其他信息-系统实时功率采集
|
||||
*/
|
||||
@Async("taskExecutor")
|
||||
public void switchHwCurrentPowerTask() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.round(timestamp / 1000.0);
|
||||
int count = switchHwCurrentPowerTask.incrementAndGet();
|
||||
AssertLog.info("交换机其他信息-系统实时功率采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
|
||||
// 判定客户端与服务端是否连接
|
||||
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
|
||||
// 发送交换机信息包
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String data = switchBoardService.getSwitchDataByType("switchHwCurrentPowerCollect");
|
||||
if(StringUtils.isNotBlank(data)){
|
||||
jsonObject = JSONObject.parseObject(data);
|
||||
}
|
||||
jsonObject.put("timestamp", timestamp);
|
||||
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
|
||||
AssertLog.info("发送交换机其他信息-系统实时功率信息包={}",JSON.toJSONString(message));
|
||||
}
|
||||
AssertLog.info("交换机其他信息-系统实时功率采集定时任务执行 - task #{} completed", count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// @Async("taskExecutor")
|
||||
|
||||
@@ -6,7 +6,6 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@@ -15,8 +14,9 @@ import java.util.List;
|
||||
public class SpecificTimeRequest {
|
||||
private String taskId;
|
||||
private String taskName;
|
||||
private List<String> taskData;
|
||||
private String taskData;
|
||||
|
||||
private String scriptId;
|
||||
private String clientId;
|
||||
private String dataType;
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.tongran.agent.client.scheduler.task;
|
||||
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.scheduling.config.CronTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
@@ -20,6 +24,9 @@ public class SpecificTimeTaskConfig implements SchedulingConfigurer {
|
||||
private ScheduledTaskRegistrar taskRegistrar;
|
||||
private final Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
|
||||
private final Map<String, Object> taskMap = new ConcurrentHashMap<>();
|
||||
@Autowired
|
||||
@Qualifier("tcpdumpTaskExecutor")
|
||||
private ThreadPoolTaskExecutor tcpdumpExecutor;
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
@@ -31,13 +38,36 @@ public class SpecificTimeTaskConfig implements SchedulingConfigurer {
|
||||
*/
|
||||
public void addCronTask(String taskId, Runnable task, String cronExpression) {
|
||||
removeTaskIfExists(taskId);
|
||||
|
||||
|
||||
CronTask cronTask = new CronTask(task, cronExpression);
|
||||
ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(
|
||||
cronTask.getRunnable(),
|
||||
cronTask.getRunnable(),
|
||||
cronTask.getTrigger()
|
||||
);
|
||||
|
||||
|
||||
taskMap.put(taskId, cronTask);
|
||||
taskFutures.put(taskId, future);
|
||||
}
|
||||
public void addTcpdumpCronTask(String taskId, Runnable task, String cronExpression) {
|
||||
removeTaskIfExists(taskId);
|
||||
|
||||
// 使用轻量级调度,实际任务交给专用线程池
|
||||
CronTask cronTask = new CronTask(() -> {
|
||||
// 异步执行,不阻塞定时任务线程
|
||||
tcpdumpExecutor.execute(() -> {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("任务执行异常 {}: {}", taskId, e.getMessage());
|
||||
}
|
||||
});
|
||||
}, cronExpression);
|
||||
|
||||
ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(
|
||||
cronTask.getRunnable(),
|
||||
cronTask.getTrigger()
|
||||
);
|
||||
|
||||
taskMap.put(taskId, cronTask);
|
||||
taskFutures.put(taskId, future);
|
||||
}
|
||||
@@ -80,6 +110,19 @@ public class SpecificTimeTaskConfig implements SchedulingConfigurer {
|
||||
String cronExpression = String.format("%d %d %d * * ?", second, minute, hour);
|
||||
addCronTask(taskId, task, cronExpression);
|
||||
}
|
||||
public void addDailyTimeTcpdumpTask(String taskId, Runnable task, String time) {
|
||||
String[] timeParts = time.split(":");
|
||||
if (timeParts.length < 2) {
|
||||
throw new IllegalArgumentException("时间格式应为 HH:mm 或 HH:mm:ss");
|
||||
}
|
||||
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
int second = timeParts.length > 2 ? Integer.parseInt(timeParts[2]) : 0;
|
||||
|
||||
String cronExpression = String.format("%d %d %d * * ?", second, minute, hour);
|
||||
addTcpdumpCronTask(taskId, task, cronExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加延迟任务
|
||||
|
||||
+473
-51
@@ -1,34 +1,90 @@
|
||||
package com.tongran.agent.client.scheduler.task;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.tongran.agent.client.core.config.ApplicationProperties;
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.enums.MsgEnum;
|
||||
import com.tongran.agent.client.core.session.SessionManager;
|
||||
import com.tongran.agent.client.netty.model.Message;
|
||||
import com.tongran.agent.client.scheduler.service.AdvancedAsyncDownloader;
|
||||
import com.tongran.agent.client.scheduler.service.AsyncCommandExecutor;
|
||||
import com.tongran.agent.client.service.AgentService;
|
||||
import com.tongran.agent.client.utils.AgentDataUtil;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class SpecificTimeTaskService {
|
||||
|
||||
protected final SessionManager sessionManager;
|
||||
|
||||
|
||||
@Resource
|
||||
private ApplicationProperties properties;
|
||||
@Resource
|
||||
private AgentService agentService;
|
||||
|
||||
@Resource
|
||||
private SpecificTimeTaskConfig taskConfig;
|
||||
@Resource
|
||||
@Qualifier("tcpdumpTaskExecutor")
|
||||
private ThreadPoolTaskExecutor tcpdumpExecutor;
|
||||
|
||||
public SpecificTimeTaskService() {
|
||||
this.sessionManager = SessionManager.getInstance();
|
||||
}
|
||||
|
||||
public boolean createTcpdumpSpecificTimeTask(SpecificTimeRequest request){
|
||||
String taskId = request.getTaskId();
|
||||
Runnable task = createTcpdumpTaskRunnable(request);
|
||||
try {
|
||||
if (request.getTime() != null) {
|
||||
taskConfig.addDailyTimeTcpdumpTask(taskId, task, request.getTime());
|
||||
} else {
|
||||
throw new IllegalArgumentException("必须指定一种时间方式");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("创建任务失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
public CompletableFuture<Boolean> executeTcpdumpImmediatelyWithResult(SpecificTimeRequest request) {
|
||||
String taskId = request.getTaskId() != null ?
|
||||
request.getTaskId() : "tcpdump-immediate-" + System.currentTimeMillis();
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
AssertLog.info("立即执行TCPDUMP任务开始: {}", taskId);
|
||||
executeTcpdump(request);
|
||||
AssertLog.info("立即执行TCPDUMP任务完成: {}", taskId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("立即执行TCPDUMP任务异常 {}: {}", taskId, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}, tcpdumpExecutor); // 使用专用线程池
|
||||
}
|
||||
/**
|
||||
* 创建指定时间任务
|
||||
*/
|
||||
@@ -55,14 +111,351 @@ public class SpecificTimeTaskService {
|
||||
throw new RuntimeException("创建任务失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Runnable createTcpdumpTaskRunnable(SpecificTimeRequest request) {
|
||||
return () -> {
|
||||
// 直接执行,因为已经在专用线程池中了
|
||||
try {
|
||||
System.out.println("执行定时任务: " + request.getTaskName());
|
||||
System.out.println("执行时间: " + LocalDateTime.now());
|
||||
System.out.println("-----------------------------------");
|
||||
|
||||
executeTcpdump(request);
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("TCPDUMP任务执行异常: {}", e.getMessage(), e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void executeTcpdump(SpecificTimeRequest request) {
|
||||
try {
|
||||
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
List<IpCount> allIpCountList = new ArrayList<>();
|
||||
|
||||
// 获取业务网卡列表
|
||||
String businessNetName = GlobalConfig.NETNAME;
|
||||
if (businessNetName == null || businessNetName.trim().isEmpty()) {
|
||||
AssertLog.error("No business network interfaces configured");
|
||||
return;
|
||||
}
|
||||
|
||||
String[] businessNetNameArr = businessNetName.split(";");
|
||||
|
||||
// 遍历所有业务网卡进行抓包
|
||||
for (String interfaceName : businessNetNameArr) {
|
||||
if (interfaceName.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String cleanInterfaceName = interfaceName.trim();
|
||||
AssertLog.info("Starting tcpdump on interface: {}", cleanInterfaceName);
|
||||
|
||||
try {
|
||||
List<IpCount> interfaceResults = captureAndAnalyzeInterface(request, timestamp, cleanInterfaceName);
|
||||
allIpCountList.addAll(interfaceResults);
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("Failed to capture interface {}: {}", cleanInterfaceName, e.getMessage());
|
||||
// 继续处理其他网卡,不中断整个流程
|
||||
}
|
||||
}
|
||||
|
||||
// 合并所有网卡的统计结果(按IP聚合)
|
||||
List<IpCount> mergedResults = mergeIpCounts(allIpCountList);
|
||||
|
||||
// 保存合并后的结果
|
||||
saveTcpdumpResult(request, timestamp + "_merged", mergedResults);
|
||||
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("Error executing tcpdump task: {}", request.getTaskId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对单个网卡进行抓包和分析
|
||||
*/
|
||||
private List<IpCount> captureAndAnalyzeInterface(SpecificTimeRequest request,
|
||||
String timestamp, String interfaceName) {
|
||||
|
||||
if (!AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(properties.getTempPcapPath())) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
String pcapFileName = properties.getTempPcapPath() + timestamp + "_" + interfaceName + ".pcap";
|
||||
List<IpCount> ipCountList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 第一步:执行tcpdump抓包(带超时控制)
|
||||
CompletableFuture<Boolean> captureFuture = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
ProcessBuilder captureBuilder = new ProcessBuilder();
|
||||
// 设置抓包时间,根据实际需求调整
|
||||
captureBuilder.command("timeout", "10", "tcpdump",
|
||||
"-i", interfaceName, "-nn", "-n", "-w", pcapFileName);
|
||||
|
||||
Process captureProcess = captureBuilder.start();
|
||||
int captureExitCode = captureProcess.waitFor();
|
||||
|
||||
if (captureExitCode != 0 && captureExitCode != 124) {
|
||||
AssertLog.error("Tcpdump抓包失败,网卡: {},退出码: {}", interfaceName, captureExitCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("抓包过程异常 {}: {}", interfaceName, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 等待抓包完成(最长7分钟)
|
||||
boolean captureSuccess = captureFuture.get(20, TimeUnit.SECONDS);
|
||||
|
||||
if (!captureSuccess) {
|
||||
return ipCountList;
|
||||
}
|
||||
|
||||
// 第二步:分析PCAP文件
|
||||
CompletableFuture<List<IpCount>> analyzeFuture = CompletableFuture.supplyAsync(() -> {
|
||||
return analyzePcapFile(pcapFileName, interfaceName);
|
||||
});
|
||||
|
||||
// 分析阶段超时时间(2分钟应该足够)
|
||||
ipCountList = analyzeFuture.get(2, TimeUnit.MINUTES);
|
||||
|
||||
} catch (TimeoutException e) {
|
||||
AssertLog.warn("网卡 {} 抓包或分析超时,跳过处理", interfaceName);
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("处理网卡 {} 时异常: {}", interfaceName, e.getMessage());
|
||||
} finally {
|
||||
// 确保清理临时文件
|
||||
cleanupTempFile(pcapFileName);
|
||||
}
|
||||
|
||||
return ipCountList;
|
||||
}
|
||||
/**
|
||||
* 分析PCAP文件并统计IP信息
|
||||
*/
|
||||
private List<IpCount> analyzePcapFile(String pcapFileName, String interfaceName) {
|
||||
List<IpCount> ipCountList = new ArrayList<>();
|
||||
|
||||
File pcapFile = new File(pcapFileName);
|
||||
if (!pcapFile.exists() || pcapFile.length() == 0) {
|
||||
AssertLog.warn("PCAP文件不存在或为空: {}", pcapFileName);
|
||||
return ipCountList;
|
||||
}
|
||||
|
||||
try {
|
||||
ProcessBuilder analyzeBuilder = new ProcessBuilder();
|
||||
// 使用更可靠的命令分析PCAP文件
|
||||
analyzeBuilder.command("bash", "-c",
|
||||
"tcpdump -r " + pcapFileName + " -nn 2>/dev/null | " +
|
||||
"awk '/^[0-9]/ && ($2 == \"IP\" || $2 == \"IP6\") { " +
|
||||
" dest=$5; " +
|
||||
" if(match(dest,/^([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)/)) { " +
|
||||
" dest=substr(dest,RSTART,RLENGTH) " +
|
||||
" } else if(match(dest,/([0-9a-fA-F:]+)/)) { " +
|
||||
" dest=substr(dest,RSTART,RLENGTH); gsub(/:[0-9]+$/,\"\",dest) " +
|
||||
" } " +
|
||||
" print dest " +
|
||||
"}' | " +
|
||||
"sort | uniq -c | sort -rn");
|
||||
|
||||
Process analyzeProcess = analyzeBuilder.start();
|
||||
|
||||
// 读取分析结果
|
||||
StringBuilder result = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(analyzeProcess.getInputStream()))) {
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
result.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int analyzeExitCode = analyzeProcess.waitFor();
|
||||
|
||||
if (analyzeExitCode == 0) {
|
||||
AssertLog.debug("PCAP分析完成: {},文件大小: {} bytes", interfaceName, pcapFile.length());
|
||||
ipCountList = parseTcpdumpOutput(result.toString());
|
||||
} else {
|
||||
AssertLog.error("PCAP分析失败: {},退出码: {}", interfaceName, analyzeExitCode);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("分析PCAP文件异常 {}: {}", interfaceName, e.getMessage());
|
||||
}
|
||||
|
||||
return ipCountList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理临时文件
|
||||
*/
|
||||
private void cleanupTempFile(String fileName) {
|
||||
try {
|
||||
File file = new File(fileName);
|
||||
if (file.exists()) {
|
||||
boolean deleted = file.delete();
|
||||
if (!deleted) {
|
||||
AssertLog.warn("无法删除临时文件: {}", fileName);
|
||||
} else {
|
||||
AssertLog.debug("已清理临时文件: {}", fileName);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.warn("清理临时文件异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个网卡的IP统计结果(相同IP的数量相加)
|
||||
*/
|
||||
private List<IpCount> mergeIpCounts(List<IpCount> allIpCountList) {
|
||||
Map<String, Double> ipCountMap = new HashMap<>();
|
||||
final String excludedIp = "223.5.5.5";
|
||||
for (IpCount ipCount : allIpCountList) {
|
||||
String ip = ipCount.getIp();
|
||||
// 跳过要排除的IP
|
||||
if (excludedIp.equals(ip)) {
|
||||
continue;
|
||||
}
|
||||
Double count = ipCount.getCount();
|
||||
|
||||
if (ipCountMap.containsKey(ip)) {
|
||||
ipCountMap.put(ip, ipCountMap.get(ip) + count);
|
||||
} else {
|
||||
ipCountMap.put(ip, count);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为列表并按数量降序排序
|
||||
return ipCountMap.entrySet().stream()
|
||||
.map(entry -> new IpCount(entry.getKey(), entry.getValue()))
|
||||
.sorted((a, b) -> Double.compare(b.getCount(), a.getCount())) // 降序
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存合并后的结果
|
||||
*/
|
||||
private void saveTcpdumpResult(SpecificTimeRequest request, String fileName, List<IpCount> ipCountList) {
|
||||
try {
|
||||
AssertLog.info("Saving merged tcpdump result - Task: {}, File: {}, Total unique IPs: {}",
|
||||
request.getTaskId(), fileName, ipCountList.size());
|
||||
|
||||
String data = "";
|
||||
if (CollectionUtil.isNotEmpty(ipCountList)) {
|
||||
data = JSONArray.toJSONString(ipCountList);
|
||||
}
|
||||
|
||||
Message message = Message.builder()
|
||||
.clientId(request.getClientId())
|
||||
.dataType(MsgEnum.tcpdump结果上报.getValue())
|
||||
.data(data)
|
||||
.build();
|
||||
|
||||
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
|
||||
AssertLog.info("发送合并后的tcpdump包统计,共{}个IP", ipCountList.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("Failed to save merged tcpdump result for task: {}", request.getTaskId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析tcpdump输出为IP统计列表(优化版本)
|
||||
*/
|
||||
private List<IpCount> parseTcpdumpOutput(String output) {
|
||||
List<IpCount> ipCountList = new ArrayList<>();
|
||||
|
||||
if (output == null || output.trim().isEmpty()) {
|
||||
return ipCountList;
|
||||
}
|
||||
|
||||
String[] lines = output.split("\n");
|
||||
|
||||
for (String line : lines) {
|
||||
line = line.trim();
|
||||
|
||||
// 跳过空行和 tcpdump 信息行
|
||||
if (line.isEmpty() || line.contains("reading from file") || line.contains("link-type")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 修改:匹配 "数字 + IP" 的格式,IP 可以是 IPv4 或 IPv6
|
||||
String[] parts = line.trim().split("\\s+", 2);
|
||||
|
||||
if (parts.length == 2) {
|
||||
String countStr = parts[0].trim();
|
||||
String ipStr = parts[1].trim();
|
||||
|
||||
// 验证 count 部分是否是数字
|
||||
try {
|
||||
Double count = Double.valueOf(countStr);
|
||||
|
||||
// 验证 IP 格式(IPv4 或 IPv6)
|
||||
if (isValidIP(ipStr)) {
|
||||
ipCountList.add(new IpCount(ipStr, count));
|
||||
} else {
|
||||
AssertLog.debug("Invalid IP format: {}", ipStr);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
AssertLog.debug("Failed to parse count from line: {}", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ipCountList;
|
||||
}
|
||||
|
||||
// 添加 IP 验证方法
|
||||
private boolean isValidIP(String ip) {
|
||||
// IPv4 验证
|
||||
if (ip.contains(".")) {
|
||||
String ipv4Pattern =
|
||||
"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
|
||||
return ip.matches(ipv4Pattern);
|
||||
}
|
||||
// IPv6 验证
|
||||
else if (ip.contains(":")) {
|
||||
// 简化的 IPv6 验证(支持标准格式和压缩格式)
|
||||
String ipv6Pattern =
|
||||
"^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|" + // 完整格式
|
||||
"^([0-9a-fA-F]{1,4}:){1,7}:$|" + // 结尾压缩
|
||||
"^:(:[0-9a-fA-F]{1,4}){1,7}$|" + // 开头压缩
|
||||
"^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|" + // 中间压缩
|
||||
"^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|" +
|
||||
"^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|" +
|
||||
"^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|" +
|
||||
"^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|" +
|
||||
"^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$|" +
|
||||
"^:((:[0-9a-fA-F]{1,4}){1,7}|:)$";
|
||||
return ip.matches(ipv6Pattern);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* IP统计实体类
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class IpCount {
|
||||
private String ip;
|
||||
private Double count;
|
||||
}
|
||||
|
||||
|
||||
private Runnable createTaskRunnable(SpecificTimeRequest request) {
|
||||
return () -> {
|
||||
System.out.println("执行定时任务: " + request.getTaskName());
|
||||
System.out.println("任务数据: " + request.getTaskData());
|
||||
System.out.println("执行时间: " + LocalDateTime.now());
|
||||
System.out.println("-----------------------------------");
|
||||
|
||||
|
||||
// 具体的业务逻辑
|
||||
executeBusinessLogic(request);
|
||||
};
|
||||
@@ -73,56 +466,85 @@ public class SpecificTimeTaskService {
|
||||
try {
|
||||
System.out.println("处理业务: " + request.getTaskData());
|
||||
String key = request.getTaskName()+"-"+System.currentTimeMillis();
|
||||
for (String command : request.getTaskData()) {
|
||||
if(StringUtils.equals(request.getDataType(), MsgEnum.Agent版本更新应答.getValue())){
|
||||
try {
|
||||
System.out.println("重启进程已启动,当前服务退出");
|
||||
System.out.println("command="+command);
|
||||
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
|
||||
command);
|
||||
pb.start();
|
||||
System.exit(0);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}else{
|
||||
List<String> cmd = Arrays.asList(command.split("\\s+"));
|
||||
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
|
||||
AsyncCommandExecutor.executeCommandAsync(
|
||||
cmd,
|
||||
100, TimeUnit.SECONDS);
|
||||
future.thenAccept(result -> {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("command", command);
|
||||
jsonObject.put("resOut", result.getOutput());
|
||||
System.out.println("JSON: " + jsonObject.toJSONString()); // 注意:toJSONString()
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMsg", "");
|
||||
json.put("result", jsonObject.toJSONString());
|
||||
Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(jsonObject.toJSONString()).build();
|
||||
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
|
||||
System.out.println("发送执行结果: " + json.toJSONString()); // 注意:toJSONString()
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
System.err.println("执行失败: " + ex.getMessage());
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",0);
|
||||
json.put("resMsg", "执行失败:Policy execute filed");
|
||||
json.put("result", "");
|
||||
Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(json.toJSONString()).build();
|
||||
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if(StringUtils.equals(request.getDataType(), MsgEnum.Agent版本更新应答.getValue())){
|
||||
try {
|
||||
AssertLog.info("定时更新启动...暂停心跳和采集任务");
|
||||
//更改全局变量
|
||||
GlobalConfig.isCollect = false;
|
||||
//调用采集任务
|
||||
agentService.cancelCollect();
|
||||
//设置检查回滚任务
|
||||
String SCRIPT_PATH = properties.getScriptPath()+"/rollback-tragent.sh";
|
||||
AgentDataUtil.chmod(SCRIPT_PATH,"775");
|
||||
AgentUtil.scheduleScriptIn3Minutes(SCRIPT_PATH);
|
||||
System.out.println("重启进程已启动,当前服务退出");
|
||||
System.out.println("command="+request.getTaskData());
|
||||
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
|
||||
request.getTaskData());
|
||||
pb.start();
|
||||
System.exit(0);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}else{
|
||||
List<String> cmd = Arrays.asList(request.getTaskData().split("\\s+"));
|
||||
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
|
||||
AsyncCommandExecutor.executeCommandAsync(
|
||||
cmd,
|
||||
100, TimeUnit.SECONDS);
|
||||
future.thenAccept(result -> {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("scriptId", request.getScriptId());
|
||||
jsonObject.put("command", request.getTaskData());
|
||||
jsonObject.put("resOut", result.getOutput());
|
||||
System.out.println("JSON: " + jsonObject.toJSONString()); // 注意:toJSONString()
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",1);
|
||||
json.put("resMsg", "");
|
||||
json.put("result", jsonObject.toJSONString());
|
||||
json.put("timestamp",timestamps);
|
||||
Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(json.toJSONString()).build();
|
||||
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
|
||||
System.out.println("发送执行结果: " + json.toJSONString()); // 注意:toJSONString()
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
|
||||
}
|
||||
}).exceptionally(ex -> {
|
||||
System.err.println("执行失败: " + ex.getMessage());
|
||||
JSONObject rse = new JSONObject();
|
||||
rse.put("scriptId", request.getScriptId());
|
||||
rse.put("command", request.getTaskData());
|
||||
rse.put("resOut", "脚本执行失败");
|
||||
long timestamps = System.currentTimeMillis();
|
||||
timestamps = Math.round(timestamps / 1000.0);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("resCode",0);
|
||||
json.put("resMsg", "执行失败:Policy execute filed");
|
||||
json.put("result", rse.toString());
|
||||
json.put("timestamp",timestamps);
|
||||
Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(json.toJSONString()).build();
|
||||
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
|
||||
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
// 调用其他服务等
|
||||
} catch (Exception e) {
|
||||
System.err.println("任务执行异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void cancleTcpdumpSpecificTimeTask() {
|
||||
// 获取所有任务ID
|
||||
Set<String> taskIds = taskConfig.getTaskMap().keySet();
|
||||
|
||||
// 遍历并关闭所有以"tcpdump"开头的任务
|
||||
for (String taskId : taskIds) {
|
||||
if (taskId.startsWith("tcpdump")) {
|
||||
taskConfig.removeTask(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,54 @@
|
||||
package com.tongran.agent.client.service;
|
||||
|
||||
import com.tongran.agent.client.core.eo.ScriptPolicyEO;
|
||||
import com.tongran.agent.client.core.vo.MacVlanVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AgentService {
|
||||
void cancelCollect();
|
||||
|
||||
void systemCollectStart(String data);
|
||||
|
||||
void systemCollectStop();
|
||||
|
||||
void switchCollectStart(String data);
|
||||
|
||||
void switchCollectStop();
|
||||
|
||||
void alarmMonitor();
|
||||
|
||||
void command(ScriptPolicyEO policy, String clientId, String dataType);
|
||||
|
||||
void cancelTask(String taskId);
|
||||
|
||||
void start();
|
||||
|
||||
void getPolicy(String data);
|
||||
|
||||
void checkTrAgent();
|
||||
|
||||
void sendMessage(String type, String data);
|
||||
|
||||
boolean connection();
|
||||
|
||||
void addRoute(String name, String gateway);
|
||||
|
||||
void dellRoute(String name, String gateway);
|
||||
|
||||
String getLogicalNode();
|
||||
|
||||
void checkMonitor();
|
||||
|
||||
void addFirewall(String netName);
|
||||
|
||||
void checkFirewall();
|
||||
|
||||
void checkAndAddFirewallPeriodically();
|
||||
|
||||
void checkAgentUpdate();
|
||||
|
||||
void checkFrpc();
|
||||
|
||||
void upFrpcMsg();
|
||||
void handleRebootRecovery();
|
||||
void checkTraffic();
|
||||
|
||||
void checkTcpdumpTimes();
|
||||
List<MacVlanVO> upMacvlanStatus();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package com.tongran.agent.client.service;
|
||||
|
||||
import com.tongran.agent.client.core.vo.MemoryDetailsVO;
|
||||
import com.tongran.agent.client.core.vo.MemoryVO;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface MemoryService {
|
||||
MemoryVO get();
|
||||
|
||||
List<MemoryDetailsVO> getMemoryDetails();
|
||||
|
||||
Integer getMemorySlotsNum();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.tongran.agent.client.service;
|
||||
|
||||
import com.tongran.agent.client.core.vo.NetBusinessVO;
|
||||
import com.tongran.agent.client.core.vo.NetVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface NetBusinessService {
|
||||
List<NetBusinessVO> netList(long timestamp);
|
||||
}
|
||||
@@ -5,7 +5,4 @@ import com.tongran.agent.client.core.vo.SwitchBoardVO;
|
||||
import java.util.List;
|
||||
|
||||
public interface SwitchBoardService {
|
||||
List<SwitchBoardVO> switchBoardList(long timestamp);
|
||||
|
||||
String getSwitchDataByType(String type);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,14 @@ package com.tongran.agent.client.service.impl;
|
||||
|
||||
import com.tongran.agent.client.core.vo.CpuVO;
|
||||
import com.tongran.agent.client.service.CPUService;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import com.tongran.agent.client.utils.CpuUsageFromProcStat;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.Sensors;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -14,39 +17,78 @@ import java.util.concurrent.TimeUnit;
|
||||
@Service
|
||||
public class CPUServiceImpl implements CPUService {
|
||||
|
||||
// 改为单例,只初始化一次!
|
||||
private static final SystemInfo SI = new SystemInfo();
|
||||
private final HardwareAbstractionLayer hal = SI.getHardware();
|
||||
private final OperatingSystem os = SI.getOperatingSystem();
|
||||
@Override
|
||||
public CpuVO get() {
|
||||
CpuVO cpuVO = CpuVO.builder().build();
|
||||
try {
|
||||
SystemInfo si = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
CentralProcessor processor = hal.getProcessor();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
System.out.println("=========================================================");
|
||||
|
||||
// 1. CPU基本信息
|
||||
cpuVO.setNum(processor.getPhysicalProcessorCount()); // CUP数量
|
||||
cpuVO.setCores(processor.getLogicalProcessorCount()); // CUP核数
|
||||
System.out.println("=== CPU基本信息 ===");
|
||||
System.out.println("CPU型号: " + processor.getProcessorIdentifier().getName());
|
||||
System.out.println("物理核心数: " + processor.getPhysicalProcessorCount());
|
||||
System.out.println("逻辑核心数: " + processor.getLogicalProcessorCount());
|
||||
System.out.println("最大频率: " + processor.getMaxFreq() / 1_000_000.0 + " GHz");
|
||||
|
||||
// 1.1 CPU温度信息
|
||||
Sensors sensors = hal.getSensors();
|
||||
double cpuTemperature = sensors.getCpuTemperature();
|
||||
cpuVO.setTemperature(cpuTemperature);
|
||||
System.out.println("CPU温度: " + (cpuTemperature > 0 ? String.format("%.1f°C", cpuTemperature) : "N/A"));
|
||||
|
||||
// 2. CPU负载信息
|
||||
System.out.println("\n=== CPU负载信息 ===");
|
||||
double[] loadAverage = processor.getSystemLoadAverage(3);
|
||||
cpuVO.setAvg1(loadAverage[0]);// CUP1分钟负载
|
||||
cpuVO.setAvg1(loadAverage[1]);// CUP5分钟负载
|
||||
cpuVO.setAvg1(loadAverage[2]);// CUP15分钟负载
|
||||
cpuVO.setAvg5(loadAverage[1]);// CUP5分钟负载
|
||||
cpuVO.setAvg15(loadAverage[2]);// CUP15分钟负载
|
||||
System.out.println("1分钟平均负载: " + loadAverage[0]);
|
||||
System.out.println("5分钟平均负载: " + loadAverage[1]);
|
||||
System.out.println("15分钟平均负载: " + loadAverage[2]);
|
||||
|
||||
// 3. CPU使用率(需要两次采样)
|
||||
System.out.println("\n=== CPU使用率 ===");
|
||||
AssertLog.info("\n=== CPU使用率 ===");
|
||||
|
||||
// 预热:第一次调用可能不准
|
||||
processor.getSystemCpuLoadBetweenTicks(processor.getSystemCpuLoadTicks());
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
// 正式采样
|
||||
long[] prevTicks = processor.getSystemCpuLoadTicks();
|
||||
TimeUnit.SECONDS.sleep(1); // 等待1秒
|
||||
TimeUnit.SECONDS.sleep(1); // 更长间隔,减少抖动
|
||||
// 计算使用率
|
||||
double cpuUsage = processor.getSystemCpuLoadBetweenTicks(prevTicks);
|
||||
cpuVO.setUti(cpuUsage * 100); // CPU使用率
|
||||
System.out.printf("CPU 使用率: %.2f%%\n", cpuUsage * 100);
|
||||
if(cpuUsage < 1.0){
|
||||
cpuVO.setUti(cpuUsage * 100); // CPU使用率
|
||||
}else {
|
||||
cpuUsage = -1;
|
||||
try {
|
||||
// 预热
|
||||
CpuUsageFromProcStat.getCpuUsage();
|
||||
Thread.sleep(1000);
|
||||
// 正式采集
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cpuUsage = CpuUsageFromProcStat.getCpuUsage();
|
||||
System.out.printf("CPU 使用率: %.2f%%\n", cpuUsage * 100);
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(cpuUsage > 0){
|
||||
cpuVO.setUti(cpuUsage * 100); // CPU使用率
|
||||
}else{
|
||||
cpuVO.setUti(cpuUsage); // CPU使用率
|
||||
}
|
||||
}
|
||||
AssertLog.info("CPU 使用率: {}%",cpuUsage * 100);
|
||||
|
||||
long[] ticks = processor.getSystemCpuLoadTicks();
|
||||
long user = ticks[CentralProcessor.TickType.USER.getIndex()] -
|
||||
prevTicks[CentralProcessor.TickType.USER.getIndex()];
|
||||
@@ -65,34 +107,35 @@ public class CPUServiceImpl implements CPUService {
|
||||
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] -
|
||||
prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
|
||||
long total = user + nice + sys + idle + iowait + irq + softirq + steal;
|
||||
|
||||
// 4. CPU时间累计值
|
||||
System.out.println("\n=== CPU时间累计值 ===");
|
||||
long[] allTicks = processor.getSystemCpuLoadTicks();
|
||||
System.out.println("中断累计时间: " +
|
||||
(allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
|
||||
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]));
|
||||
System.out.println("空闲累计时间: " + allTicks[CentralProcessor.TickType.IDLE.getIndex()]);
|
||||
System.out.printf("I/O等待时间(CPU等待响应时间): %.2f%%\n", 100d * iowait / total);
|
||||
System.out.println("系统累计时间: " + allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]);
|
||||
System.out.println("中断累计时间: " + AgentUtil.ticksToSeconds((allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
|
||||
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])));
|
||||
System.out.println("空闲累计时间: " + AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.IDLE.getIndex()]));
|
||||
System.out.println("I/O等待时间(CPU等待响应时间): " + AgentUtil.ticksToSeconds(iowait));
|
||||
System.out.println("系统累计时间: " + AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]));
|
||||
long softwareTime = sys - irq - softirq;
|
||||
System.out.printf("软件相关时间(近似无响应时间): %.2f%%\n", 100d * softwareTime / total);
|
||||
System.out.println("用户进程累计时间: " + allTicks[CentralProcessor.TickType.USER.getIndex()]);
|
||||
System.out.println("软件相关时间(近似无响应时间): " + AgentUtil.ticksToSeconds(softwareTime));
|
||||
System.out.println("用户进程累计时间: " + AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.USER.getIndex()]));
|
||||
|
||||
cpuVO.setInterrupt((allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
|
||||
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])); // CPU硬件中断提供服务时间
|
||||
cpuVO.setIdle(allTicks[CentralProcessor.TickType.IDLE.getIndex()]);// CPU空闲时间
|
||||
cpuVO.setIowait(100d * iowait / total);// CPU等待响应时间
|
||||
cpuVO.setSystem(allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]);// CPU系统时间
|
||||
cpuVO.setNoresp(100d * softwareTime / total);// CPU软件无响应时间
|
||||
cpuVO.setUser(allTicks[CentralProcessor.TickType.USER.getIndex()]);// CPU用户进程所花费的时间
|
||||
cpuVO.setInterrupt(AgentUtil.ticksToSeconds((allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
|
||||
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]))); // CPU硬件中断提供服务时间:秒
|
||||
cpuVO.setIdle(AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.IDLE.getIndex()]));// CPU空闲时间:秒
|
||||
cpuVO.setIowait(AgentUtil.ticksToSeconds(iowait));// CPU等待响应时间:秒
|
||||
cpuVO.setSystem(AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]));// CPU系统时间:秒
|
||||
cpuVO.setNoresp(AgentUtil.ticksToSeconds(softwareTime));// CPU软件无响应时间:秒
|
||||
cpuVO.setUser(AgentUtil.ticksToSeconds(allTicks[CentralProcessor.TickType.USER.getIndex()]));// CPU用户进程所花费的时间:秒
|
||||
|
||||
// 5. 系统运行时间和CPU空闲时间
|
||||
long uptime = os.getSystemUptime();
|
||||
cpuVO.setNormal(uptime);// CPU正常运行时间
|
||||
cpuVO.setNormal(uptime);// CPU正常运行时间:秒
|
||||
System.out.println("CPU正常运行时间: " + cpuVO.getNormal());
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return cpuVO;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,41 +7,90 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.HWDiskStore;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
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 {
|
||||
// 改为单例,只初始化一次!
|
||||
private static final SystemInfo si = new SystemInfo();
|
||||
// 定义需要排除的虚拟设备前缀列表
|
||||
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
|
||||
"/dev/bcache"
|
||||
};
|
||||
@Override
|
||||
public List<DiskVO> diskList(long timestamp) {
|
||||
List<DiskVO> tempList = new ArrayList<>();
|
||||
List<DiskVO> resultList = new ArrayList<>();
|
||||
SystemInfo si = new SystemInfo();
|
||||
Map<String, String> typeMap = getDiskTypeMap();
|
||||
Map<String, Long> usedSpaceMap = getDiskUsedSpaceMap();
|
||||
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();
|
||||
// 获取磁盘是否健康
|
||||
Long isHealth = isDiskHealthy(diskFullName);
|
||||
if (diskFullName.startsWith("/dev/nvme")) {
|
||||
int score = getNvmeHealthScore(diskFullName);
|
||||
diskVO.setHealthScore(score);
|
||||
}
|
||||
// 在判断中使用
|
||||
boolean isVirtualDisk = false;
|
||||
for (String prefix : VIRTUAL_DISK_PREFIXES) {
|
||||
if (diskFullName.startsWith(prefix)) {
|
||||
isVirtualDisk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVirtualDisk) {
|
||||
diskVO.setHealthStatus(isHealth);
|
||||
}
|
||||
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.getWriteBytes()); // 磁盘写入字节
|
||||
diskVO.setReadBytes(disk.getReadBytes()); // 磁盘读取字节
|
||||
|
||||
// 获取磁盘类型和已用空间(使用不带/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<HWDiskStore> disks1 = si.getHardware().getDiskStores();
|
||||
@@ -59,11 +108,14 @@ public class DiskServiceImpl implements DiskService {
|
||||
long readDiff = disks2.get(i).getReadBytes() - readBytes1[i];
|
||||
long writeDiff = disks2.get(i).getWriteBytes() - writeBytes1[i];
|
||||
String serial = disks2.get(i).getSerial();
|
||||
DiskVO diskVO = tempList.stream().filter(d -> StringUtils.equals(d.getSerial(),serial)).findFirst().orElse(null);
|
||||
String name = disks2.get(i).getName().replace("/dev/", "");
|
||||
DiskVO diskVO = tempList.stream().filter(d -> StringUtils.equals(d.getName(),name)).findFirst().orElse(null);
|
||||
if(Objects.nonNull(diskVO)){
|
||||
diskVO.setWriteSpeed(readDiff);//磁盘写入速率
|
||||
diskVO.setReadSpeed(writeDiff);//磁盘读取速率
|
||||
diskVO.setWriteSpeed(writeDiff);//磁盘写入速率
|
||||
diskVO.setReadSpeed(readDiff);//磁盘读取速率
|
||||
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,10 +127,376 @@ public class DiskServiceImpl implements DiskService {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取磁盘类型映射(设备名 -> 类型)
|
||||
*/
|
||||
public Map<String, String> getDiskTypeMap() {
|
||||
Map<String, String> 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<String, Long> getDiskUsedSpaceMap() {
|
||||
Map<String, Long> diskUsedMap = new HashMap<>();
|
||||
Map<String, String> 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<String, String> getPartitionToDiskMap() {
|
||||
Map<String, String> 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;
|
||||
}
|
||||
|
||||
public Long isDiskHealthy(String diskDevice) {
|
||||
try {
|
||||
Process process = new ProcessBuilder("smartctl", "-H", diskDevice)
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()))) {
|
||||
|
||||
boolean isHealth = reader.lines()
|
||||
.anyMatch(line -> line.contains("PASSED") || line.contains("OK"))
|
||||
&& process.waitFor() == 0;
|
||||
if(isHealth){
|
||||
return 1L;
|
||||
}else{
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// 检查是否为 NVMe 设备
|
||||
if (!diskDevice.startsWith("/dev/nvme")) {
|
||||
return -1; // 非 NVMe 设备
|
||||
}
|
||||
|
||||
Process process = new ProcessBuilder("smartctl", "-a", 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;
|
||||
|
||||
// 解析 smartctl 输出
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找 Percentage Used(已用百分比)
|
||||
if (line.contains("Percentage Used:")) {
|
||||
percentageUsed = extractSmartctlValueFromLine(line);
|
||||
}
|
||||
// 查找 Available Spare(可用备用空间)
|
||||
else if (line.contains("Available Spare:") && !line.contains("Available Spare Threshold")) {
|
||||
availableSpare = extractSmartctlValueFromLine(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 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
|
||||
public List<PointVO> pointList(long timestamp) {
|
||||
List<PointVO> list = new ArrayList<>();
|
||||
SystemInfo si = new SystemInfo();
|
||||
// 获取文件系统信息
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("=== 挂载信息 ===");
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
package com.tongran.agent.client.service.impl;
|
||||
|
||||
import com.tongran.agent.client.core.vo.MemoryDetailsVO;
|
||||
import com.tongran.agent.client.core.vo.MemoryVO;
|
||||
import com.tongran.agent.client.service.MemoryService;
|
||||
import com.tongran.agent.client.utils.AgentDataUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
public class MemoryServiceImpl implements MemoryService {
|
||||
@@ -56,19 +64,347 @@ public class MemoryServiceImpl implements MemoryService {
|
||||
memInfo.getOrDefault("SReclaimable", 0L));
|
||||
// 总内存使用率
|
||||
double totalUsage = (double)(total - available) / total * 100;
|
||||
System.out.printf("总内存使用率: %.2f%%\n", totalUsage);
|
||||
AssertLog.info("总内存使用率: {}%",totalUsage);
|
||||
// System.out.printf("总内存使用率: %.2f%%\n", totalUsage);
|
||||
// 实际内存使用率
|
||||
long cached = memInfo.getOrDefault("Cached", 0L) +
|
||||
memInfo.getOrDefault("SReclaimable", 0L);
|
||||
long buffers = memInfo.getOrDefault("Buffers", 0L);
|
||||
double actualUsage = (double)(total - available - cached - buffers) / total * 100;
|
||||
System.out.printf("实际内存使用率: %.2f%%\n", actualUsage);
|
||||
memoryVO.setUntilzation(actualUsage); //内存利用率
|
||||
// long cached = memInfo.getOrDefault("Cached", 0L) +
|
||||
// memInfo.getOrDefault("SReclaimable", 0L);
|
||||
// long buffers = memInfo.getOrDefault("Buffers", 0L);
|
||||
// double actualUsage = (double)(total - available - cached - buffers) / total * 100;
|
||||
// System.out.printf("实际内存使用率: %.2f%%\n", actualUsage);
|
||||
memoryVO.setUntilzation(totalUsage); //内存利用率
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return memoryVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryDetailsVO> getMemoryDetails() {
|
||||
List<MemoryDetailsVO> memoryList = new ArrayList<>();
|
||||
String command = "dmidecode -t memory";
|
||||
Process process = null;
|
||||
BufferedReader reader = null;
|
||||
// 获取 ipmitool sdr type "Memory" 的输出,用于后续匹配
|
||||
List<String> sdrLines = getIpmitoolSdrLines();
|
||||
try {
|
||||
// 执行过滤命令
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
"bash", "-c", command + " | awk '/^Handle 0x/ {if (buffer && buffer ~ /Size: [0-9]/) print buffer; buffer=$0; next} {buffer=buffer ORS $0} END {if (buffer && buffer ~ /Size: [0-9]/) print buffer}'"
|
||||
);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
process = processBuilder.start();
|
||||
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
StringBuilder block = new StringBuilder();
|
||||
String line;
|
||||
int totalMemoryCount = 0;
|
||||
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
block.append(line).append("\n");
|
||||
|
||||
if (line.trim().isEmpty() && block.length() > 0) {
|
||||
MemoryDetailsVO vo = parseMemoryBlock(block.toString(), sdrLines);
|
||||
if (vo != null) {
|
||||
memoryList.add(vo);
|
||||
totalMemoryCount++;
|
||||
}
|
||||
block.setLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理最后一个块
|
||||
if (block.length() > 0) {
|
||||
MemoryDetailsVO vo = parseMemoryBlock(block.toString(), sdrLines);
|
||||
if (vo != null) {
|
||||
memoryList.add(vo);
|
||||
totalMemoryCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待命令完成
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
AssertLog.warn("dmidecode命令执行异常, 退出码: {}", exitCode);
|
||||
}
|
||||
|
||||
// 记录统计日志
|
||||
if (totalMemoryCount > 0) {
|
||||
AssertLog.info("成功获取内存信息, 共发现 {} 根内存条", totalMemoryCount);
|
||||
|
||||
// 计算总容量
|
||||
long totalCapacityGB = memoryList.stream()
|
||||
.mapToLong(MemoryDetailsVO::getCapacity)
|
||||
.sum() / 1024L;
|
||||
AssertLog.info("总内存容量: {} GB", totalCapacityGB);
|
||||
} else {
|
||||
AssertLog.warn("未检测到已安装的内存条");
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
AssertLog.error("执行内存信息获取命令失败: {}", e.getMessage(), e);
|
||||
} catch (InterruptedException e) {
|
||||
AssertLog.error("命令执行被中断: {}", e.getMessage(), e);
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("解析内存信息时发生未知错误: {}", e.getMessage(), e);
|
||||
} finally {
|
||||
// 清理资源
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
AssertLog.warn("关闭流时发生错误: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
// 处理 dmidecode 未监测到但 ipmitool 检测到异常的插槽
|
||||
if (!sdrLines.isEmpty()) {
|
||||
for (String sdrLine : sdrLines) {
|
||||
// 提取 DIMM 名称,如 "DIMM110"
|
||||
String dimmName = sdrLine.split("\\|")[0].trim();
|
||||
boolean alreadyExists = memoryList.stream()
|
||||
.anyMatch(mem -> dimmName.equals(mem.getName()));
|
||||
|
||||
// 如果 dmidecode 中没有这个插槽,但 ipmitool 检测到异常
|
||||
if (!alreadyExists) {
|
||||
MemoryDetailsVO errorSlot = new MemoryDetailsVO();
|
||||
errorSlot.setName(dimmName);
|
||||
errorSlot.setLocation("Unknown");
|
||||
errorSlot.setManufacturer("Unknown");
|
||||
errorSlot.setCapacity(0L); // 没有安装内存,容量为0
|
||||
errorSlot.setFrequency(0);
|
||||
errorSlot.setSerialNumber("Unknown");
|
||||
errorSlot.setType("Unknown");
|
||||
errorSlot.setMinVoltage(0);
|
||||
errorSlot.setRank("Unknown");
|
||||
errorSlot.setBitWidth("Unknown");
|
||||
errorSlot.setTechnology("Unknown");
|
||||
errorSlot.setPartNumber("Unknown");
|
||||
errorSlot.setHealthStatus("0"); // 异常状态
|
||||
|
||||
memoryList.add(errorSlot);
|
||||
AssertLog.debug("添加未在dmidecode中监测到的异常插槽: {}", dimmName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return memoryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMemorySlotsNum() {
|
||||
ProcessBuilder processBuilder = null;
|
||||
Process process = null;
|
||||
BufferedReader reader = null;
|
||||
|
||||
try {
|
||||
// 使用bash执行管道命令
|
||||
processBuilder = new ProcessBuilder(
|
||||
"bash", "-c",
|
||||
"dmidecode -t memory | grep 'Number Of Devices:' | awk '{print $4}'"
|
||||
);
|
||||
process = processBuilder.start();
|
||||
|
||||
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!line.trim().isEmpty()) {
|
||||
try {
|
||||
Integer slots = Integer.parseInt(line.trim());
|
||||
AssertLog.info("成功获取内存插槽数量: " + slots);
|
||||
return slots;
|
||||
} catch (NumberFormatException e) {
|
||||
AssertLog.info("解析内存插槽数量失败,输出内容: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssertLog.info("未找到内存插槽数量信息");
|
||||
return null;
|
||||
|
||||
} catch (IOException e) {
|
||||
AssertLog.info("执行命令时发生IO异常: " + e.getMessage());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
AssertLog.info("获取内存插槽数量时发生未知异常: " + e.getMessage());
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
AssertLog.info("关闭资源时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取 ipmitool sdr type "Memory" 的输出行列表
|
||||
private List<String> getIpmitoolSdrLines() {
|
||||
List<String> lines = new ArrayList<>();
|
||||
try {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
"bash", "-c", "ipmitool sdr type \"Memory\" | grep -E \"DIMM.*Configuration Error\""
|
||||
);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
Process process = processBuilder.start();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
lines.add(line.trim());
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
AssertLog.warn("ipmitool命令执行异常, 退出码: {}", exitCode);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("获取ipmitool sdr信息失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// 解析内存块,并注入健康状态
|
||||
private MemoryDetailsVO parseMemoryBlock(String block, List<String> sdrLines) {
|
||||
try {
|
||||
// 跳过空块
|
||||
if (block.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MemoryDetailsVO vo = new MemoryDetailsVO();
|
||||
|
||||
// 正则提取字段
|
||||
Pattern namePattern = Pattern.compile("Locator:\\s*(\\w+)"); // 提取 DIMM000
|
||||
Pattern locationPattern = Pattern.compile("Bank Locator:\\s*(.+)");
|
||||
Pattern manufacturerPattern = Pattern.compile("Manufacturer:\\s*(.+)");
|
||||
Pattern capacityPattern = Pattern.compile("Size:\\s*(\\d+)\\s*GB");
|
||||
Pattern frequencyPattern = Pattern.compile("Speed:\\s*(\\d+)", Pattern.CASE_INSENSITIVE);
|
||||
Pattern serialNumberPattern = Pattern.compile("Serial Number:\\s*(.+)");
|
||||
Pattern typePattern = Pattern.compile("Type:\\s*(.+)");
|
||||
Pattern minVoltagePattern = Pattern.compile("Minimum Voltage:\\s*(\\d+\\.?\\d*)\\s*V");
|
||||
Pattern rankPattern = Pattern.compile("Rank:\\s*(.+)");
|
||||
Pattern bitWidthPattern = Pattern.compile("Total Width:\\s*(.+) bit");
|
||||
Pattern technologyPattern = Pattern.compile("Type Detail:\\s*(.+)");
|
||||
Pattern partNumberPattern = Pattern.compile("Part Number:\\s*(.+)");
|
||||
|
||||
Matcher nameMatcher = namePattern.matcher(block);
|
||||
if (nameMatcher.find()) {
|
||||
vo.setName(nameMatcher.group(1).trim());
|
||||
} else {
|
||||
AssertLog.debug("未找到内存名称字段: {}", block);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 位置固定为 mainboard
|
||||
vo.setLocation("mainboard");
|
||||
|
||||
Matcher manufacturerMatcher = manufacturerPattern.matcher(block);
|
||||
if (manufacturerMatcher.find()) {
|
||||
vo.setManufacturer(manufacturerMatcher.group(1).trim());
|
||||
} else {
|
||||
vo.setManufacturer("Unknown");
|
||||
}
|
||||
|
||||
Matcher capacityMatcher = capacityPattern.matcher(block);
|
||||
if (capacityMatcher.find()) {
|
||||
long gb = Long.parseLong(capacityMatcher.group(1));
|
||||
vo.setCapacity(gb * 1024L); // GB 转 MB
|
||||
} else {
|
||||
AssertLog.debug("未找到内存容量字段: {}", block);
|
||||
return null;
|
||||
}
|
||||
|
||||
Matcher frequencyMatcher = frequencyPattern.matcher(block);
|
||||
if (frequencyMatcher.find()) {
|
||||
vo.setFrequency(Integer.parseInt(frequencyMatcher.group(1)));
|
||||
} else {
|
||||
vo.setFrequency(0);
|
||||
}
|
||||
|
||||
Matcher serialNumberMatcher = serialNumberPattern.matcher(block);
|
||||
if (serialNumberMatcher.find()) {
|
||||
vo.setSerialNumber(serialNumberMatcher.group(1).trim());
|
||||
} else {
|
||||
vo.setSerialNumber("Unknown");
|
||||
}
|
||||
|
||||
Matcher typeMatcher = typePattern.matcher(block);
|
||||
if (typeMatcher.find()) {
|
||||
vo.setType(typeMatcher.group(1).trim());
|
||||
} else {
|
||||
vo.setType("Unknown");
|
||||
}
|
||||
|
||||
Matcher minVoltageMatcher = minVoltagePattern.matcher(block);
|
||||
if (minVoltageMatcher.find()) {
|
||||
String voltageStr = minVoltageMatcher.group(1);
|
||||
// 处理 1.2 V -> 1200 mV
|
||||
double voltage = Double.parseDouble(voltageStr);
|
||||
vo.setMinVoltage((int)(voltage * 1000));
|
||||
} else {
|
||||
vo.setMinVoltage(0);
|
||||
}
|
||||
|
||||
Matcher rankMatcher = rankPattern.matcher(block);
|
||||
if (rankMatcher.find()) {
|
||||
vo.setRank(rankMatcher.group(1).trim() + " rank");
|
||||
} else {
|
||||
vo.setRank("Unknown");
|
||||
}
|
||||
|
||||
Matcher bitWidthMatcher = bitWidthPattern.matcher(block);
|
||||
if (bitWidthMatcher.find()) {
|
||||
vo.setBitWidth(bitWidthMatcher.group(1).trim() + " bit");
|
||||
} else {
|
||||
vo.setBitWidth("Unknown");
|
||||
}
|
||||
|
||||
Matcher technologyMatcher = technologyPattern.matcher(block);
|
||||
if (technologyMatcher.find()) {
|
||||
vo.setTechnology(technologyMatcher.group(1).trim());
|
||||
} else {
|
||||
vo.setTechnology("Unknown");
|
||||
}
|
||||
|
||||
Matcher partNumberMatcher = partNumberPattern.matcher(block);
|
||||
if (partNumberMatcher.find()) {
|
||||
vo.setPartNumber(partNumberMatcher.group(1).trim());
|
||||
} else {
|
||||
vo.setPartNumber("Unknown");
|
||||
}
|
||||
|
||||
// 检查健康状态
|
||||
String memoryName = vo.getName(); // 例如 "DIMM000"
|
||||
boolean hasConfigError = sdrLines.stream()
|
||||
.anyMatch(line -> line.startsWith(memoryName));
|
||||
|
||||
if (hasConfigError) {
|
||||
vo.setHealthStatus("0");
|
||||
} else {
|
||||
vo.setHealthStatus("1");
|
||||
}
|
||||
|
||||
// 调试日志
|
||||
AssertLog.debug("解析内存信息: {} - {}GB - {}MHz - 健康状态: {}",
|
||||
vo.getName(), vo.getCapacity()/1024L, vo.getFrequency(), vo.getHealthStatus());
|
||||
|
||||
return vo;
|
||||
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("解析内存块时发生错误: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
package com.tongran.agent.client.service.impl;
|
||||
|
||||
import com.tongran.agent.client.core.config.ApplicationProperties;
|
||||
import com.tongran.agent.client.core.vo.NetBusinessVO;
|
||||
import com.tongran.agent.client.service.NetBusinessService;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.annotation.Resource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class NetBusinessServiceImpl implements NetBusinessService {
|
||||
|
||||
private static final String CGROUP_BASE = "/sys/fs/cgroup/net_cls";
|
||||
private static final String CGROUP_PREFIX = "agent_";
|
||||
private static final int MAX_CHAIN_NAME_LEN = 28;
|
||||
|
||||
// 存储每个进程的 classid
|
||||
private final ConcurrentHashMap<String, Long> processClassIdMap = new ConcurrentHashMap<>();
|
||||
// 存储每个进程的 cgroup 路径
|
||||
private final ConcurrentHashMap<String, String> processCgroupPathMap = new ConcurrentHashMap<>();
|
||||
// 记录当前所有已创建的进程(无论是否有进程在运行)
|
||||
private final Set<String> activeProcesses = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private List<String> processList = new ArrayList<>();
|
||||
|
||||
@Resource
|
||||
private ApplicationProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
loadProcessListFromFile();
|
||||
|
||||
// 为配置文件中有的进程创建 cgroup 和 iptables 链,但只创建有PID的
|
||||
for (String processName : processList) {
|
||||
List<Integer> pids = getProcessPids(processName);
|
||||
if (!pids.isEmpty()) {
|
||||
createProcessCgroup(processName);
|
||||
initProcessChains("iptables", processName);
|
||||
initProcessChains("ip6tables", processName);
|
||||
addProcessToCgroup(processName, pids);
|
||||
activeProcesses.add(processName);
|
||||
System.out.println("进程 " + processName + " (PID: " + pids + ") 初始化完成");
|
||||
} else {
|
||||
System.out.println("进程 " + processName + " 当前无PID运行,跳过初始化");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("=== cgroup多进程监控初始化完成 ===");
|
||||
System.out.println("监控进程列表: " + processList);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("初始化失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
try {
|
||||
// 清理所有已创建的规则和 cgroup
|
||||
for (String processName : activeProcesses) {
|
||||
cleanupProcessRules("iptables", processName);
|
||||
cleanupProcessRules("ip6tables", processName);
|
||||
removeProcessCgroup(processName);
|
||||
}
|
||||
|
||||
System.out.println("已清理所有 iptables 规则和 cgroup");
|
||||
} catch (Exception e) {
|
||||
System.err.println("清理失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NetBusinessVO> netList(long timestamp) {
|
||||
List<NetBusinessVO> resultList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 保存旧的进程列表用于对比
|
||||
List<String> oldProcessList = new ArrayList<>(processList);
|
||||
|
||||
// 重新加载配置文件
|
||||
loadProcessListFromFile();
|
||||
|
||||
// 找出被剔除的进程(在旧列表中存在,在新列表中不存在)
|
||||
Set<String> removedProcesses = new HashSet<>(oldProcessList);
|
||||
removedProcesses.removeAll(processList);
|
||||
|
||||
// 清理被剔除的进程(只有这些才清理)
|
||||
if (!removedProcesses.isEmpty()) {
|
||||
System.out.println("检测到配置文件中已删除的进程: " + removedProcesses + ",开始清理...");
|
||||
for (String processName : removedProcesses) {
|
||||
cleanupProcessRules("iptables", processName);
|
||||
cleanupProcessRules("ip6tables", processName);
|
||||
removeProcessCgroup(processName);
|
||||
activeProcesses.remove(processName);
|
||||
System.out.println("已清理进程 " + processName + " 的所有规则");
|
||||
}
|
||||
}
|
||||
|
||||
// 找出新增的进程(在新列表中存在,在旧列表中不存在)
|
||||
Set<String> addedProcesses = new HashSet<>(processList);
|
||||
addedProcesses.removeAll(oldProcessList);
|
||||
|
||||
// 为新增的进程创建规则(但只创建有PID的)
|
||||
if (!addedProcesses.isEmpty()) {
|
||||
System.out.println("检测到新增的进程: " + addedProcesses);
|
||||
for (String processName : addedProcesses) {
|
||||
List<Integer> pids = getProcessPids(processName);
|
||||
if (!pids.isEmpty()) {
|
||||
// 只有有PID的才创建规则
|
||||
createProcessCgroup(processName);
|
||||
initProcessChains("iptables", processName);
|
||||
initProcessChains("ip6tables", processName);
|
||||
addProcessToCgroup(processName, pids);
|
||||
activeProcesses.add(processName);
|
||||
System.out.println("已为进程 " + processName + " (PID: " + pids + ") 创建规则");
|
||||
} else {
|
||||
System.out.println("进程 " + processName + " 当前无PID,暂不创建规则");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历当前配置文件中的进程列表获取统计信息
|
||||
for (String processName : processList) {
|
||||
List<Integer> pids = getProcessPids(processName);
|
||||
|
||||
// 如果有PID,确保规则存在(可能之前无PID时没创建,现在需要创建)
|
||||
if (!pids.isEmpty()) {
|
||||
// 检查是否已有规则,如果没有则需要创建
|
||||
if (!activeProcesses.contains(processName)) {
|
||||
System.out.println("进程 " + processName + " 已有PID运行但无规则,开始创建规则...");
|
||||
createProcessCgroup(processName);
|
||||
initProcessChains("iptables", processName);
|
||||
initProcessChains("ip6tables", processName);
|
||||
activeProcesses.add(processName);
|
||||
}
|
||||
|
||||
// 确保进程在cgroup中
|
||||
addProcessToCgroup(processName, pids);
|
||||
|
||||
// 确保链存在
|
||||
ensureProcessChainsExist("iptables", processName);
|
||||
ensureProcessChainsExist("ip6tables", processName);
|
||||
|
||||
// 获取该进程的流量统计
|
||||
long[] v4Stats = getTrafficStatsForProcess("iptables", processName);
|
||||
long[] v6Stats = getTrafficStatsForProcess("ip6tables", processName);
|
||||
|
||||
NetBusinessVO vo = new NetBusinessVO();
|
||||
vo.setIpv4InSpeed(v4Stats[0]);
|
||||
vo.setIpv4OutSpeed(v4Stats[1]);
|
||||
vo.setIpv6InSpeed(v6Stats[0]);
|
||||
vo.setIpv6OutSpeed(v6Stats[1]);
|
||||
vo.setInSpeed(v4Stats[0] + v6Stats[0]);
|
||||
vo.setOutSpeed(v4Stats[1] + v6Stats[1]);
|
||||
vo.setTimestamp(timestamp);
|
||||
vo.setName("total");
|
||||
vo.setProcessName(processName);
|
||||
|
||||
resultList.add(vo);
|
||||
|
||||
if (v4Stats[0] > 0 || v4Stats[1] > 0 || v6Stats[0] > 0 || v6Stats[1] > 0) {
|
||||
System.out.printf("进程 %s (PID: %s): IPv4收=%d 发=%d, IPv6收=%d 发=%d%n",
|
||||
processName, pids, v4Stats[0], v4Stats[1], v6Stats[0], v6Stats[1]);
|
||||
}
|
||||
} else {
|
||||
// 没有PID,但规则保留(因为配置还在)
|
||||
System.out.println("进程 " + processName + " 当前无PID运行,保留已有规则等待进程启动");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取网络统计失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为进程生成唯一的 classid
|
||||
*/
|
||||
private long generateClassId(String processName) {
|
||||
// classid 必须在 0x00000001 - 0xFFFFFFFF 之间
|
||||
// 使用进程名的hash,但限制在 0x1 - 0xFFFFF 之间
|
||||
int hash = Math.abs(processName.hashCode()) & 0xFFFFF;
|
||||
if (hash == 0) hash = 1; // 避免0值
|
||||
return hash & 0xFFFFFFFFL; // 确保在32位无符号范围内
|
||||
}
|
||||
|
||||
/**
|
||||
* 为进程创建独立的 cgroup
|
||||
*/
|
||||
private void createProcessCgroup(String processName) {
|
||||
try {
|
||||
long classId = generateClassId(processName);
|
||||
// 使用原始的 processName 来构建路径,不要用 safeName
|
||||
String cgroupPath = CGROUP_BASE + "/" + CGROUP_PREFIX + processName;
|
||||
|
||||
executeCommand("mkdir -p " + cgroupPath);
|
||||
executeCommand("echo " + classId + " > " + cgroupPath + "/net_cls.classid");
|
||||
|
||||
processClassIdMap.put(processName, classId);
|
||||
processCgroupPathMap.put(processName, cgroupPath);
|
||||
|
||||
System.out.println("进程 " + processName + " cgroup 创建完成, classid=" + classId + ", path=" + cgroupPath);
|
||||
} catch (Exception e) {
|
||||
System.err.println("创建进程 cgroup 失败 " + processName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除进程的 cgroup
|
||||
*/
|
||||
private void removeProcessCgroup(String processName) {
|
||||
try {
|
||||
String cgroupPath = processCgroupPathMap.get(processName);
|
||||
if (cgroupPath != null) {
|
||||
// 先移动所有进程到根 cgroup
|
||||
executeCommand("cat " + cgroupPath + "/tasks | while read pid; do echo $pid > " + CGROUP_BASE + "/tasks 2>/dev/null; done");
|
||||
// 删除目录
|
||||
executeCommand("rmdir " + cgroupPath + " 2>/dev/null");
|
||||
processClassIdMap.remove(processName);
|
||||
processCgroupPathMap.remove(processName);
|
||||
System.out.println("已删除进程 " + processName + " 的 cgroup");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("删除进程 cgroup 失败 " + processName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadProcessListFromFile() {
|
||||
List<String> newProcessList = new ArrayList<>();
|
||||
String path = properties.getConfPath() + "/process.conf";
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty() && !line.startsWith("#")) {
|
||||
newProcessList.add(line);
|
||||
}
|
||||
}
|
||||
System.out.println("从 " + path + " 加载进程列表: " + newProcessList);
|
||||
} catch (Exception e) {
|
||||
System.err.println("读取配置文件失败 " + path + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 processList
|
||||
synchronized (this) {
|
||||
processList.clear();
|
||||
processList.addAll(newProcessList);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Integer> getProcessPids(String processName) {
|
||||
List<Integer> pids = new ArrayList<>();
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder("sh", "-c",
|
||||
"pgrep -x " + processName + " 2>/dev/null");
|
||||
Process p = pb.start();
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
pids.add(Integer.parseInt(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略
|
||||
}
|
||||
return pids;
|
||||
}
|
||||
|
||||
private void addProcessToCgroup(String processName, List<Integer> pids) {
|
||||
String cgroupPath = processCgroupPathMap.get(processName);
|
||||
if (cgroupPath == null) return;
|
||||
|
||||
for (Integer pid : pids) {
|
||||
try {
|
||||
ProcessBuilder checkPb = new ProcessBuilder("sh", "-c",
|
||||
"cat " + cgroupPath + "/tasks 2>/dev/null | grep -q " + pid);
|
||||
|
||||
if (checkPb.start().waitFor() != 0) {
|
||||
executeCommand("echo " + pid + " > " + cgroupPath + "/tasks");
|
||||
System.out.println("进程 " + pid + "(" + processName + ") 已加入 cgroup");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String generateSafeChainName(String processName) {
|
||||
String safe = processName.replaceAll("[^a-zA-Z0-9]", "_");
|
||||
String hash = Integer.toHexString(processName.hashCode());
|
||||
while (hash.length() < 8) {
|
||||
hash = "0" + hash;
|
||||
}
|
||||
if (hash.length() > 8) {
|
||||
hash = hash.substring(0, 8);
|
||||
}
|
||||
String prefix = safe.length() > 12 ? safe.substring(0, 12) : safe;
|
||||
return prefix + "_" + hash;
|
||||
}
|
||||
|
||||
private String buildChainName(String safeName, String suffix, String direction) {
|
||||
// 先为 in 和 out 计算一个公共的 baseName(取较短的那个)
|
||||
String baseName = "am_" + safeName;
|
||||
|
||||
// 计算 in 和 out 分别需要的长度
|
||||
int inLen = baseName.length() + 6; // _v4_in
|
||||
int outLen = baseName.length() + 7; // _v4_out
|
||||
|
||||
// 取较长的作为标准,确保两者都满足
|
||||
int maxNeeded = Math.max(inLen, outLen);
|
||||
|
||||
if (maxNeeded > MAX_CHAIN_NAME_LEN) {
|
||||
// 需要截断 baseName
|
||||
int baseMaxLen = MAX_CHAIN_NAME_LEN - 7; // 按最长的 out 算
|
||||
if (baseName.length() > baseMaxLen) {
|
||||
String prefix = baseName.substring(0, 8);
|
||||
String suffix_part = baseName.substring(baseName.length() - (baseMaxLen - 9));
|
||||
baseName = prefix + "_" + suffix_part;
|
||||
}
|
||||
}
|
||||
|
||||
return baseName + "_" + suffix + "_" + direction;
|
||||
}
|
||||
|
||||
private long getProcessClassId(String processName) {
|
||||
Long classId = processClassIdMap.get(processName);
|
||||
if (classId == null) {
|
||||
classId = generateClassId(processName);
|
||||
processClassIdMap.put(processName, classId);
|
||||
}
|
||||
return classId;
|
||||
}
|
||||
|
||||
private void initProcessChains(String command, String processName) {
|
||||
long classId = getProcessClassId(processName);
|
||||
String safeName = generateSafeChainName(processName);
|
||||
String suffix = command.equals("iptables") ? "v4" : "v6";
|
||||
String inChain = buildChainName(safeName, suffix, "in");
|
||||
String outChain = buildChainName(safeName, suffix, "out");
|
||||
|
||||
initChain(command, inChain, "INPUT", classId);
|
||||
initChain(command, outChain, "OUTPUT", classId);
|
||||
}
|
||||
|
||||
private void ensureProcessChainsExist(String command, String processName) {
|
||||
long classId = getProcessClassId(processName);
|
||||
String safeName = generateSafeChainName(processName);
|
||||
String suffix = command.equals("iptables") ? "v4" : "v6";
|
||||
String inChain = buildChainName(safeName, suffix, "in");
|
||||
String outChain = buildChainName(safeName, suffix, "out");
|
||||
|
||||
ensureChainExists(command, inChain, "INPUT", classId);
|
||||
ensureChainExists(command, outChain, "OUTPUT", classId);
|
||||
}
|
||||
|
||||
private void ensureChainExists(String command, String chainName, String hookChain, long classId) {
|
||||
try {
|
||||
ProcessBuilder checkPb = new ProcessBuilder("sh", "-c",
|
||||
command + " -L " + chainName + " -n >/dev/null 2>&1");
|
||||
|
||||
if (checkPb.start().waitFor() != 0) {
|
||||
executeCommand(command + " -N " + chainName);
|
||||
executeCommand(command + " -A " + chainName +
|
||||
" -m cgroup --cgroup " + classId + " -j RETURN");
|
||||
executeCommand(command + " -A " + chainName + " -j RETURN");
|
||||
executeCommand(command + " -I " + hookChain + " 1 -m cgroup --cgroup " +
|
||||
classId + " -j " + chainName);
|
||||
System.out.println(command + " 链 " + chainName + " 初始化完成 (classid=" + classId + ")");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("确保链存在失败 " + chainName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void initChain(String command, String chainName, String hookChain, long classId) {
|
||||
try {
|
||||
// 1. 直接创建链(如果已存在会报错,但忽略)
|
||||
executeCommand(command + " -N " + chainName + " 2>/dev/null");
|
||||
|
||||
// 2. 清空并添加规则
|
||||
executeCommand(command + " -F " + chainName);
|
||||
executeCommand(command + " -A " + chainName +
|
||||
" -m cgroup --cgroup " + classId + " -j RETURN");
|
||||
executeCommand(command + " -A " + chainName + " -j RETURN");
|
||||
|
||||
// 3. 检查是否已存在相同的规则
|
||||
String checkRuleCmd = command + " -C " + hookChain + " -m cgroup --cgroup " +
|
||||
classId + " -j " + chainName + " 2>/dev/null";
|
||||
|
||||
ProcessBuilder checkPb = new ProcessBuilder("sh", "-c", checkRuleCmd);
|
||||
if (checkPb.start().waitFor() != 0) {
|
||||
// 规则不存在,才插入
|
||||
executeCommand(command + " -I " + hookChain + " 1 -m cgroup --cgroup " +
|
||||
classId + " -j " + chainName);
|
||||
System.out.println(command + " 插入规则到 " + hookChain + " 成功");
|
||||
} else {
|
||||
System.out.println(command + " 规则已存在于 " + hookChain + ",跳过插入");
|
||||
}
|
||||
|
||||
System.out.println(command + " 链 " + chainName + " 初始化完成 (classid=" + classId + ")");
|
||||
} catch (Exception e) {
|
||||
System.err.println("初始化链 " + chainName + " 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private long[] getTrafficStatsForProcess(String command, String processName) {
|
||||
long classId = getProcessClassId(processName);
|
||||
String safeName = generateSafeChainName(processName);
|
||||
String suffix = command.equals("iptables") ? "v4" : "v6";
|
||||
String inChain = buildChainName(safeName, suffix, "in");
|
||||
String outChain = buildChainName(safeName, suffix, "out");
|
||||
|
||||
long recvBytes = getChainBytes(command, inChain);
|
||||
long sentBytes = getChainBytes(command, outChain);
|
||||
return new long[]{recvBytes, sentBytes};
|
||||
}
|
||||
|
||||
private void cleanupProcessRules(String command, String processName) {
|
||||
try {
|
||||
Long classId = processClassIdMap.get(processName);
|
||||
if (classId == null) return;
|
||||
|
||||
String safeName = generateSafeChainName(processName);
|
||||
String suffix = command.equals("iptables") ? "v4" : "v6";
|
||||
String inChain = buildChainName(safeName, suffix, "in");
|
||||
String outChain = buildChainName(safeName, suffix, "out");
|
||||
|
||||
executeCommand(command + " -D INPUT -m cgroup --cgroup " + classId +
|
||||
" -j " + inChain + " 2>/dev/null");
|
||||
executeCommand(command + " -D OUTPUT -m cgroup --cgroup " + classId +
|
||||
" -j " + outChain + " 2>/dev/null");
|
||||
executeCommand(command + " -F " + inChain + " 2>/dev/null");
|
||||
executeCommand(command + " -F " + outChain + " 2>/dev/null");
|
||||
executeCommand(command + " -X " + inChain + " 2>/dev/null");
|
||||
executeCommand(command + " -X " + outChain + " 2>/dev/null");
|
||||
|
||||
System.out.println("已清理 " + command + " 中进程 " + processName + " 的规则");
|
||||
} catch (Exception e) {
|
||||
System.err.println("清理进程规则失败 " + processName + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private long getChainBytes(String command, String chainName) {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder("sh", "-c",
|
||||
command + " -L " + chainName + " -v -n -x | grep RETURN | head -1");
|
||||
Process p = pb.start();
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
||||
String line = br.readLine();
|
||||
if (line != null && !line.isEmpty()) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
return Long.parseLong(parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取链 " + chainName + " 统计失败: " + e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void executeCommand(String command) throws Exception {
|
||||
ProcessBuilder pb = new ProcessBuilder("sh", "-c", command);
|
||||
Process p = pb.start();
|
||||
int exitCode = p.waitFor();
|
||||
if (exitCode != 0) {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
|
||||
String error = br.lines().reduce("", (a, b) -> a + "\n" + b);
|
||||
if (!error.isEmpty() && !error.contains("No such file") && !error.contains("File exists")) {
|
||||
System.err.println("命令执行警告: " + command + " -> " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.tongran.agent.client.service.impl;
|
||||
|
||||
import com.tongran.agent.client.core.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.vo.NetVO;
|
||||
import com.tongran.agent.client.service.NetService;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.springframework.stereotype.Service;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
@@ -11,88 +15,499 @@ import oshi.util.FormatUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class NetServiceImpl implements NetService {
|
||||
|
||||
// 改为单例,只初始化一次!
|
||||
private static final SystemInfo SI = new SystemInfo();
|
||||
private final HardwareAbstractionLayer hal = SI.getHardware();
|
||||
// 缓存最近一次的ping结果
|
||||
private final Map<String, Double> pingCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public List<NetVO> netList(long timestamp) {
|
||||
List<NetVO> list = new ArrayList<>();
|
||||
try {
|
||||
SystemInfo si = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
// 获取所有网络接口
|
||||
List<NetworkIF> networkIFs = hal.getNetworkIFs();
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("===== 网卡流量统计 =====");
|
||||
List<NetVO> temp = new ArrayList<>();
|
||||
for (NetworkIF net : networkIFs) {
|
||||
System.out.println("接口名称: " + net.getName()+"("+net.getDisplayName()+")");
|
||||
System.out.println("MAC地址: " + net.getMacaddr());
|
||||
System.out.print("运行状态: ");
|
||||
System.out.println(net.isConnectorPresent() ? "已连接" : "未连接");
|
||||
System.out.println("接口类型: " + AgentUtil.getInterfaceType(net));
|
||||
System.out.println("IPv4地址: " + String.join(", ", net.getIPv4addr()));
|
||||
|
||||
NetVO netVO = NetVO.builder().build();
|
||||
netVO.setName(net.getName()+"("+net.getDisplayName()+")");//网卡名称
|
||||
netVO.setMac(net.getMacaddr());//MAC
|
||||
netVO.setStatus(net.isConnectorPresent() ? "已连接" : "未连接");//运行状态
|
||||
netVO.setType(AgentUtil.getInterfaceType(net));//接口类型
|
||||
netVO.setIpV4(String.join(", ", net.getIPv4addr()));//IPv4
|
||||
Map<String, String> map = getNetworkMode(net.getName());
|
||||
if (map != null && !map.isEmpty()) {
|
||||
netVO.setSpeed(map.get("speed"));
|
||||
netVO.setDuplex(map.get("duplex"));
|
||||
}
|
||||
temp.add(netVO);
|
||||
// 获取系统所有物理网卡
|
||||
Set<String> allPhysicalInterfaces = getAllPhysicalInterfaces();
|
||||
|
||||
System.out.println("系统所有物理网卡: " + allPhysicalInterfaces);
|
||||
System.out.println("OSHI识别的网卡: " + networkIFs.stream().map(NetworkIF::getName).collect(Collectors.toList()));
|
||||
|
||||
// 创建网卡名称到NetworkIF对象的映射
|
||||
Map<String, NetworkIF> networkIFMap = new HashMap<>();
|
||||
for (NetworkIF net : networkIFs) {
|
||||
networkIFMap.put(net.getName(), net);
|
||||
}
|
||||
// 2. 实时带宽监控(需要两次采样)
|
||||
System.out.println("\n=== 实时带宽监控 ===");
|
||||
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("===== 网卡流量统计(基于iptables统计) =====");
|
||||
|
||||
// 第一次采样
|
||||
for (NetworkIF net : networkIFs) {
|
||||
net.updateAttributes();
|
||||
}
|
||||
// 等待1秒
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
|
||||
// 第二次采样并计算速率
|
||||
for (NetworkIF net : networkIFs) {
|
||||
long prevBytesRecv = net.getBytesRecv();
|
||||
long prevBytesSent = net.getBytesSent();
|
||||
net.updateAttributes();
|
||||
long bytesRecv = net.getBytesRecv() - prevBytesRecv;
|
||||
long bytesSent = net.getBytesSent() - prevBytesSent;
|
||||
System.out.println("接口: " + net.getName());
|
||||
System.out.println("入站丢包: " + net.getInDrops());
|
||||
System.out.println("出站丢包: " + net.getCollisions());
|
||||
System.out.println("接收带宽: " + FormatUtil.formatBytes(bytesRecv) + "/s (" +
|
||||
bytesToMbps(bytesRecv) + " Mbps)");
|
||||
System.out.println("发送带宽: " + FormatUtil.formatBytes(bytesSent) + "/s (" +
|
||||
bytesToMbps(bytesSent) + " Mbps)");
|
||||
// 等待1秒(仅用于获取瞬时流量速率,实际计算总流量时不需要)
|
||||
Thread.sleep(1000);
|
||||
|
||||
NetVO netVO = temp.stream().filter(n -> n.getIpV4().equals(String.join(", ", net.getIPv4addr()))
|
||||
&& n.getMac().equals(net.getMacaddr())).findFirst().orElse(null);
|
||||
if(Objects.nonNull(netVO)){
|
||||
netVO.setInDropped(net.getInDrops());//入站丢包
|
||||
netVO.setOutDropped(net.getCollisions());//出站丢包
|
||||
netVO.setInSpeed(bytesRecv);//接收流量
|
||||
netVO.setOutSpeed(bytesSent);//发送流量
|
||||
netVO.setTimestamp(timestamp);
|
||||
// 获取iptables统计信息
|
||||
Map<String, Map<String, Long>> iptablesStats = getIptablesStatistics();
|
||||
|
||||
// 处理所有物理网卡
|
||||
for (String interfaceName : allPhysicalInterfaces) {
|
||||
NetworkIF net = networkIFMap.get(interfaceName);
|
||||
NetVO netVO = null;
|
||||
|
||||
if (net != null) {
|
||||
// OSHI能识别到的网卡
|
||||
netVO = processNetworkIF(net, iptablesStats, timestamp);
|
||||
} else {
|
||||
// OSHI未识别到的网卡(无IP配置的网卡)
|
||||
netVO = processMissingNetworkIF(interfaceName, timestamp);
|
||||
}
|
||||
|
||||
if (netVO != null) {
|
||||
list.add(netVO);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static double bytesToMbps(long bytes) {
|
||||
return bytes * 8.0 / 1_000_000; // bytes to megabits
|
||||
/**
|
||||
* 获取系统所有物理网卡(包括无IP的)
|
||||
*/
|
||||
private Set<String> getAllPhysicalInterfaces() {
|
||||
Set<String> interfaces = new HashSet<>();
|
||||
Path netPath = Paths.get("/sys/class/net");
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(netPath)) {
|
||||
for (Path interfacePath : stream) {
|
||||
String interfaceName = interfacePath.getFileName().toString();
|
||||
|
||||
// 跳过虚拟网卡(可选,根据需求调整)
|
||||
if (interfaceName.startsWith("lo")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
interfaces.add(interfaceName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("读取网卡列表失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从/sys读取网卡统计信息
|
||||
*/
|
||||
private Long getBytesFromSys(String interfaceName, String statFile) {
|
||||
try {
|
||||
Path statPath = Paths.get("/sys/class/net", interfaceName, "statistics", statFile);
|
||||
if (Files.exists(statPath)) {
|
||||
String content = Files.readAllLines(statPath).get(0).trim();
|
||||
return Long.parseLong(content);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("读取网卡 " + interfaceName + " 的 " + statFile + " 失败: " + e.getMessage());
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
/**
|
||||
* 启动异步ping监控
|
||||
*/
|
||||
private void startPingMonitorAsync(String interfaceName, long timeStapm) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
Double lossRate = getPingLossRateSync(interfaceName);
|
||||
if (lossRate != null) {
|
||||
AssertLog.info("网卡 " + interfaceName + " 5分钟平均丢包率: " + lossRate + "%");
|
||||
// 更新缓存,供下次使用
|
||||
pingCache.put(interfaceName+timeStapm, lossRate);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("网卡" + interfaceName + "ping监控异常: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步获取ping丢包率(在异步线程中调用)
|
||||
*/
|
||||
private Double getPingLossRateSync(String interfaceName) {
|
||||
String[] cmd = {
|
||||
"/bin/sh", "-c",
|
||||
String.format("ping -I %s -c 290 -i 1 -W 1 -w 290 %s 2>&1 | grep 'packet loss' | awk -F'[ ,%%]+' '{print $6}'",
|
||||
interfaceName, "223.5.5.5")
|
||||
};
|
||||
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(cmd);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String result = reader.readLine();
|
||||
if (result != null && !result.trim().isEmpty()) {
|
||||
return Double.parseDouble(result.trim());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("执行ping命令失败: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 处理OSHI能识别到的网卡
|
||||
*/
|
||||
private NetVO processNetworkIF(NetworkIF net, Map<String, Map<String, Long>> iptablesStats, long timestamp) {
|
||||
try {
|
||||
net.updateAttributes();
|
||||
|
||||
String interfaceName = net.getName();
|
||||
boolean isBusinessInterface = GlobalConfig.NETNAME.contains(interfaceName);
|
||||
String[] ipv6Arr = net.getIPv6addr();
|
||||
String publicIpv6 = "";
|
||||
for (String ipv6 : ipv6Arr) {
|
||||
if(AgentUtil.isPublicIPv6(ipv6)){
|
||||
publicIpv6 = ipv6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String[] ipv4Arr = net.getIPv4addr();
|
||||
CompletableFuture<Double> pingLossFuture = null;
|
||||
boolean hasIpv4 = false;
|
||||
Double pingDropped = null;
|
||||
if(ipv4Arr != null){
|
||||
for (String ipv4 : ipv4Arr) {
|
||||
if(ipv4 != "" && !ipv4.startsWith("127.")){
|
||||
hasIpv4 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("接口名称: " + interfaceName + "(" + net.getDisplayName() + ")");
|
||||
System.out.println("MAC地址: " + net.getMacaddr());
|
||||
System.out.println("运行状态: " + (net.isConnectorPresent() ? "已连接" : "未连接"));
|
||||
System.out.println("接口类型: " + AgentUtil.getInterfaceType(net));
|
||||
System.out.println("IPv4地址: " + String.join(", ", net.getIPv4addr()));
|
||||
System.out.println("公网IPv6地址: " + publicIpv6);
|
||||
System.out.println("业务网卡: " + (isBusinessInterface ? "是" : "否"));
|
||||
|
||||
// 从OSHI获取总流量
|
||||
long totalBytesRecv = net.getBytesRecv();
|
||||
long totalBytesSent = net.getBytesSent();
|
||||
|
||||
Long ipv4BytesRecv = null, ipv4BytesSent = null;
|
||||
Long ipv6BytesRecv = null, ipv6BytesSent = null;
|
||||
|
||||
if (isBusinessInterface) {
|
||||
Map<String, Long> interfaceStats = iptablesStats.get(interfaceName);
|
||||
if (interfaceStats != null) {
|
||||
ipv4BytesRecv = interfaceStats.get("ipv4Recv");
|
||||
ipv4BytesSent = interfaceStats.get("ipv4Sent");
|
||||
ipv6BytesRecv = interfaceStats.get("ipv6Recv");
|
||||
ipv6BytesSent = interfaceStats.get("ipv6Sent");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("总接收流量: " + FormatUtil.formatBytes(totalBytesRecv));
|
||||
System.out.println("总发送流量: " + FormatUtil.formatBytes(totalBytesSent));
|
||||
System.out.println("IPv4接收流量: " + (ipv4BytesRecv != null ? FormatUtil.formatBytes(ipv4BytesRecv) : "null"));
|
||||
System.out.println("IPv4发送流量: " + (ipv4BytesSent != null ? FormatUtil.formatBytes(ipv4BytesSent) : "null"));
|
||||
System.out.println("IPv6接收流量: " + (ipv6BytesRecv != null ? FormatUtil.formatBytes(ipv6BytesRecv) : "null"));
|
||||
System.out.println("IPv6发送流量: " + (ipv6BytesSent != null ? FormatUtil.formatBytes(ipv6BytesSent) : "null"));
|
||||
System.out.println("入站丢包: " + net.getInDrops());
|
||||
System.out.println("出站丢包: " + net.getCollisions());
|
||||
|
||||
NetVO netVO = NetVO.builder()
|
||||
.name(net.getName() + "(" + net.getDisplayName() + ")")
|
||||
.mac(net.getMacaddr())
|
||||
.status(net.isConnectorPresent() ? "已连接" : "未连接")
|
||||
.type(AgentUtil.getInterfaceType(net))
|
||||
.ipV4(String.join(", ", net.getIPv4addr()))
|
||||
.ipV6(publicIpv6)
|
||||
.inDropped(net.getInDrops())
|
||||
.outDropped(net.getCollisions())
|
||||
.inSpeed(totalBytesRecv) // 总接收流量
|
||||
.outSpeed(totalBytesSent) // 总发送流量
|
||||
.ipv4InSpeed(ipv4BytesRecv)
|
||||
.ipv4OutSpeed(ipv4BytesSent)
|
||||
.ipv6InSpeed(ipv6BytesRecv)
|
||||
.ipv6OutSpeed(ipv6BytesSent)
|
||||
.timestamp(timestamp)
|
||||
.build();
|
||||
if(hasIpv4){
|
||||
startPingMonitorAsync(interfaceName, timestamp);
|
||||
netVO.setPingDropped(pingCache.get(interfaceName+(timestamp-300)));
|
||||
// 使用完清除,防止内存泄漏
|
||||
pingCache.remove(interfaceName+(timestamp-300));
|
||||
}
|
||||
// 设置协商速度和工作模式
|
||||
Map<String, String> ethtoolMap = getNetworkMode(net.getName());
|
||||
if (ethtoolMap != null && !ethtoolMap.isEmpty()) {
|
||||
netVO.setSpeed(ethtoolMap.get("speed"));
|
||||
netVO.setDuplex(ethtoolMap.get("duplex"));
|
||||
}
|
||||
|
||||
System.out.println("-----------------------------------------");
|
||||
return netVO;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("处理网卡 " + net.getName() + " 失败: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理OSHI未识别的网卡(无IP配置的网卡)
|
||||
*/
|
||||
private NetVO processMissingNetworkIF(String interfaceName, long timestamp) {
|
||||
System.out.println("接口名称: " + interfaceName + "(无IP配置)");
|
||||
|
||||
try {
|
||||
// 获取MAC地址
|
||||
String macAddr = getMacAddress(interfaceName);
|
||||
System.out.println("MAC地址: " + (macAddr != null ? macAddr : "未知"));
|
||||
|
||||
// 获取网卡状态
|
||||
String status = getInterfaceStatus(interfaceName);
|
||||
System.out.println("运行状态: " + status);
|
||||
|
||||
// 从/sys获取总流量统计
|
||||
Long totalBytesRecv = getBytesFromSys(interfaceName, "rx_bytes");
|
||||
Long totalBytesSent = getBytesFromSys(interfaceName, "tx_bytes");
|
||||
|
||||
// 获取丢包数
|
||||
Long inDropped = getBytesFromSys(interfaceName, "rx_dropped");
|
||||
Long outDropped = getBytesFromSys(interfaceName, "tx_dropped");
|
||||
|
||||
System.out.println("总接收流量: " + FormatUtil.formatBytes(totalBytesRecv));
|
||||
System.out.println("总发送流量: " + FormatUtil.formatBytes(totalBytesSent));
|
||||
System.out.println("入站丢包: " + inDropped);
|
||||
System.out.println("出站丢包: " + outDropped);
|
||||
|
||||
// 获取协商速度和工作模式
|
||||
Map<String, String> ethtoolMap = getNetworkMode(interfaceName);
|
||||
String speed = null;
|
||||
String duplex = null;
|
||||
if (ethtoolMap != null && !ethtoolMap.isEmpty()) {
|
||||
speed = ethtoolMap.get("speed");
|
||||
duplex = ethtoolMap.get("duplex");
|
||||
}
|
||||
|
||||
NetVO netVO = NetVO.builder()
|
||||
.name(interfaceName + "(无IP配置)")
|
||||
.mac(macAddr != null ? macAddr : "00:00:00:00:00:00")
|
||||
.status(status)
|
||||
.type("Ethernet")
|
||||
.ipV4("")
|
||||
.ipV6("")
|
||||
.inDropped(inDropped)
|
||||
.outDropped(outDropped)
|
||||
.inSpeed(totalBytesRecv) // 总接收流量
|
||||
.outSpeed(totalBytesSent) // 总发送流量
|
||||
.ipv4InSpeed(null) // 无IP配置,无法区分IPv4/IPv6流量
|
||||
.ipv4OutSpeed(null)
|
||||
.ipv6InSpeed(null)
|
||||
.ipv6OutSpeed(null)
|
||||
.speed(speed)
|
||||
.duplex(duplex)
|
||||
.timestamp(timestamp)
|
||||
.build();
|
||||
|
||||
System.out.println("-----------------------------------------");
|
||||
return netVO;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("处理无IP网卡 " + interfaceName + " 失败: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网卡MAC地址
|
||||
*/
|
||||
private String getMacAddress(String interfaceName) {
|
||||
try {
|
||||
Path macPath = Paths.get("/sys/class/net", interfaceName, "address");
|
||||
if (Files.exists(macPath)) {
|
||||
String mac = Files.readAllLines(macPath).get(0).trim();
|
||||
return mac.isEmpty() ? null : mac;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取MAC地址失败: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网卡状态
|
||||
*/
|
||||
private String getInterfaceStatus(String interfaceName) {
|
||||
try {
|
||||
Path operstatePath = Paths.get("/sys/class/net", interfaceName, "operstate");
|
||||
if (Files.exists(operstatePath)) {
|
||||
String state = Files.readAllLines(operstatePath).get(0).trim();
|
||||
return "up".equalsIgnoreCase(state) ? "已连接" : "未连接";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取网卡状态失败: " + e.getMessage());
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取iptables统计信息 - 使用命令过滤模式优化
|
||||
*/
|
||||
private Map<String, Map<String, Long>> getIptablesStatistics() {
|
||||
Map<String, Map<String, Long>> stats = new HashMap<>();
|
||||
|
||||
try {
|
||||
System.out.println("开始获取iptables统计信息(过滤模式)...");
|
||||
|
||||
// 遍历所有业务网卡,为每个网卡分别获取统计信息
|
||||
for (String interfaceName : GlobalConfig.NETNAME.split(";")) {
|
||||
System.out.println("处理业务网卡: " + interfaceName);
|
||||
|
||||
Map<String, Long> interfaceStats = new HashMap<>();
|
||||
|
||||
// 获取IPv4 INPUT统计
|
||||
Long ipv4Recv = getIptablesStatForInterface("iptables", "INPUT", interfaceName);
|
||||
if (ipv4Recv != null) {
|
||||
interfaceStats.put("ipv4Recv", ipv4Recv);
|
||||
}
|
||||
|
||||
// 获取IPv4 OUTPUT统计
|
||||
Long ipv4Sent = getIptablesStatForInterface("iptables", "OUTPUT", interfaceName);
|
||||
if (ipv4Sent != null) {
|
||||
interfaceStats.put("ipv4Sent", ipv4Sent);
|
||||
}
|
||||
|
||||
// 获取IPv6 INPUT统计
|
||||
Long ipv6Recv = getIptablesStatForInterface("ip6tables", "INPUT", interfaceName);
|
||||
if (ipv6Recv != null) {
|
||||
interfaceStats.put("ipv6Recv", ipv6Recv);
|
||||
}
|
||||
|
||||
// 获取IPv6 OUTPUT统计
|
||||
Long ipv6Sent = getIptablesStatForInterface("ip6tables", "OUTPUT", interfaceName);
|
||||
if (ipv6Sent != null) {
|
||||
interfaceStats.put("ipv6Sent", ipv6Sent);
|
||||
}
|
||||
|
||||
if (!interfaceStats.isEmpty()) {
|
||||
stats.put(interfaceName, interfaceStats);
|
||||
System.out.println("网卡 " + interfaceName + " 的统计信息: " + interfaceStats);
|
||||
} else {
|
||||
System.out.println("警告: 未找到网卡 " + interfaceName + " 的iptables统计信息");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("最终iptables统计信息: " + stats);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取iptables统计失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定网卡的iptables统计信息
|
||||
* @param iptablesCmd iptables或ip6tables
|
||||
* @param chain 链名(INPUT/OUTPUT)
|
||||
* @param interfaceName 网卡名
|
||||
* @return 字节数,如果没有找到返回null
|
||||
*/
|
||||
private Long getIptablesStatForInterface(String iptablesCmd, String chain, String interfaceName) {
|
||||
try {
|
||||
// 构建命令:iptables -v -n -x -L INPUT | grep eth0
|
||||
ProcessBuilder pb = new ProcessBuilder("sh", "-c",
|
||||
iptablesCmd + " -v -n -x -L " + chain + " | grep " + interfaceName);
|
||||
|
||||
Process process = pb.start();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
|
||||
long totalBytes = 0;
|
||||
String line;
|
||||
boolean found = false;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.out.println("解析 " + iptablesCmd + " " + chain + " 行: " + line);
|
||||
Long bytes = parseIptablesLineForInterface(line);
|
||||
if (bytes != null) {
|
||||
totalBytes += bytes;
|
||||
found = true;
|
||||
System.out.println("找到匹配的规则: " + bytes + " bytes");
|
||||
}
|
||||
}
|
||||
|
||||
process.waitFor();
|
||||
|
||||
if (found) {
|
||||
System.out.println(iptablesCmd + " " + chain + " 接口 " + interfaceName + " 总流量: " + totalBytes + " bytes");
|
||||
return totalBytes;
|
||||
} else {
|
||||
System.out.println("未找到 " + iptablesCmd + " " + chain + " 接口 " + interfaceName + " 的统计信息");
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取 " + iptablesCmd + " " + chain + " 统计失败: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析iptables输出行
|
||||
*/
|
||||
private Long parseIptablesLineForInterface(String line) {
|
||||
line = line.trim();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("Chain") || line.startsWith("pkts") || line.startsWith("target")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用空格分割,处理多个连续空格
|
||||
String[] parts = line.split("\\s+");
|
||||
|
||||
if (parts.length >= 7) {
|
||||
try {
|
||||
// 字节数在第二列(索引1)
|
||||
long bytes = Long.parseLong(parts[1]);
|
||||
|
||||
// 由于已经用grep过滤了,直接返回字节数
|
||||
System.out.println("找到匹配的规则: " + bytes + " bytes");
|
||||
return bytes;
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("解析iptables行失败,无法解析数字: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网卡协商模式
|
||||
*/
|
||||
public static Map<String, String> getNetworkMode(String interfaceName) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
ProcessBuilder pb = new ProcessBuilder("ethtool", interfaceName);
|
||||
@@ -107,17 +522,12 @@ public class NetServiceImpl implements NetService {
|
||||
result.put("speed", line.split(":")[1].trim());
|
||||
} else if (line.contains("Duplex:")) {
|
||||
result.put("duplex", line.split(":")[1].trim());
|
||||
} else if (line.contains("Auto-negotiation:")) {
|
||||
result.put("auto-negotiation", line.split(":")[1].trim());
|
||||
}
|
||||
}
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
result.put("error", "ethtool command failed with exit code " + exitCode);
|
||||
}
|
||||
process.waitFor();
|
||||
} catch (Exception e) {
|
||||
result.put("error", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,571 +1,9 @@
|
||||
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.config.GlobalConfig;
|
||||
import com.tongran.agent.client.core.vo.SwitchBoardVO;
|
||||
import com.tongran.agent.client.service.SwitchBoardService;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.snmp4j.*;
|
||||
import org.snmp4j.event.ResponseEvent;
|
||||
import org.snmp4j.mp.SnmpConstants;
|
||||
import org.snmp4j.smi.GenericAddress;
|
||||
import org.snmp4j.smi.OID;
|
||||
import org.snmp4j.smi.OctetString;
|
||||
import org.snmp4j.smi.VariableBinding;
|
||||
import org.snmp4j.transport.DefaultUdpTransportMapping;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class SwitchBoardServiceImpl implements SwitchBoardService {
|
||||
@Override
|
||||
public List<SwitchBoardVO> switchBoardList(long timestamp) {
|
||||
List<SwitchBoardVO> list = new ArrayList<>();
|
||||
// System.out.println("==================== 交换机流量信息 ====================");
|
||||
try {
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
|
||||
// 3. 获取交换机基本信息
|
||||
getSystemInfo(snmp, target);
|
||||
|
||||
// 4. 获取接口信息
|
||||
list = getInterfaceInfo(snmp, target, timestamp);
|
||||
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSwitchDataByType(String type) {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("type",type);
|
||||
switch(type){
|
||||
case "switchNetCollect":
|
||||
json.put("value",handleSwitchNet());
|
||||
break;
|
||||
case "switchModuleCollect":
|
||||
json.put("value",handleSwitchModule());
|
||||
break;
|
||||
case "switchMpuCollect":
|
||||
json.put("value",handleSwitchMpu());
|
||||
break;
|
||||
case "switchPwrCollect":
|
||||
json.put("value",handleSwitchPwr());
|
||||
break;
|
||||
case "switchFanCollect":
|
||||
json.put("value",handleSwitchFan());
|
||||
break;
|
||||
default:
|
||||
json.put("value",handleSwitchOther(type));
|
||||
break;
|
||||
}
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
private String handleSwitchNet(){
|
||||
// System.out.println("==================== 交换机网络信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_NET_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, "switchNet", GlobalConfig.SWITCH_NET_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String handleSwitchModule(){
|
||||
// System.out.println("==================== 交换机光模块信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_MODULE_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, "switchModule", GlobalConfig.SWITCH_MODULE_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String handleSwitchMpu(){
|
||||
// System.out.println("==================== 交换机MPU信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_MPU_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, "switchMpu", GlobalConfig.SWITCH_MPU_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String handleSwitchPwr(){
|
||||
// System.out.println("==================== 交换机电源信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_PWR_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, "switchPwr", GlobalConfig.SWITCH_PWR_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String handleSwitchFan(){
|
||||
// System.out.println("==================== 交换机风扇信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_FAN_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, "switchFan", GlobalConfig.SWITCH_FAN_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String handleSwitchOther(String type){
|
||||
// System.out.println("==================== 交换机系统其他信息 ====================");
|
||||
String result = "";
|
||||
try {
|
||||
if(GlobalConfig.SWITCH_OTHER_OID.isEmpty()){
|
||||
return result;
|
||||
}
|
||||
// 1. 创建传输映射
|
||||
TransportMapping transport = new DefaultUdpTransportMapping();
|
||||
Snmp snmp = new Snmp(transport);
|
||||
transport.listen();
|
||||
// 2. 创建目标对象
|
||||
CommunityTarget target = new CommunityTarget();
|
||||
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
|
||||
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
|
||||
target.setRetries(2);
|
||||
target.setTimeout(5000);
|
||||
target.setVersion(SnmpConstants.version2c);
|
||||
result = getInterfaceInfoByType(snmp, target, type, GlobalConfig.SWITCH_OTHER_OID);
|
||||
snmp.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void getSystemInfo(Snmp snmp, Target target) throws IOException {
|
||||
// 系统描述 OID
|
||||
String[] systemOIDs = {
|
||||
"1.3.6.1.2.1.1.1.0", // sysDescr
|
||||
"1.3.6.1.2.1.1.5.0", // sysName
|
||||
"1.3.6.1.2.1.1.6.0", // sysLocation
|
||||
"1.3.6.1.2.1.1.4.0", // sysContact
|
||||
"1.3.6.1.2.1.1.3.0" // sysUpTime
|
||||
};
|
||||
|
||||
PDU pdu = new PDU();
|
||||
for (String oid : systemOIDs) {
|
||||
pdu.add(new VariableBinding(new OID(oid)));
|
||||
}
|
||||
pdu.setType(PDU.GET);
|
||||
|
||||
ResponseEvent event = snmp.send(pdu, target);
|
||||
if (event != null && event.getResponse() != null) {
|
||||
// System.out.println("\n=== 交换机基本信息 ===");
|
||||
for (VariableBinding vb : event.getResponse().getVariableBindings()) {
|
||||
System.out.printf("%-30s: %s%n",
|
||||
getOIDDescription(vb.getOid().toString()),
|
||||
vb.getVariable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<SwitchBoardVO> getInterfaceInfo(Snmp snmp, Target target, long timestamp) throws IOException {
|
||||
// 获取接口数量
|
||||
String ifNumberOID = "1.3.6.1.2.1.2.1.0";
|
||||
PDU pdu = new PDU();
|
||||
pdu.add(new VariableBinding(new OID(ifNumberOID)));
|
||||
pdu.setType(PDU.GET);
|
||||
|
||||
ResponseEvent event = snmp.send(pdu, target);
|
||||
if (event == null || event.getResponse() == null) {
|
||||
System.err.println("无法获取接口数量");
|
||||
return null;
|
||||
}
|
||||
|
||||
int ifNumber = event.getResponse().get(0).getVariable().toInt();
|
||||
System.out.println("\n=== 接口数量: " + ifNumber + " ===");
|
||||
|
||||
// 获取每个接口的信息
|
||||
String[] ifOIDs = {
|
||||
"1.3.6.1.2.1.2.2.1.2", // ifDescr
|
||||
"1.3.6.1.2.1.2.2.1.3", // ifType
|
||||
"1.3.6.1.2.1.2.2.1.5", // ifSpeed
|
||||
"1.3.6.1.2.1.2.2.1.8", // ifOperStatus
|
||||
"1.3.6.1.2.1.31.1.1.1.6", // ifInOctets
|
||||
"1.3.6.1.2.1.31.1.1.1.10" // ifOutOctets
|
||||
// "1.3.6.1.2.1.2.2.1.10", // ifInOctets
|
||||
// "1.3.6.1.2.1.2.2.1.16" // ifOutOctets
|
||||
};
|
||||
System.out.printf("%-5s %-15s %-10s %-15s %-8s %-12s %-12s%n",
|
||||
"Index", "Name", "Type", "Speed", "Status", "InBytes", "OutBytes");
|
||||
List<SwitchBoardVO> list = new ArrayList<>();
|
||||
for (int i = 1; i <= ifNumber; i++) {
|
||||
pdu = new PDU();
|
||||
for (String baseOID : ifOIDs) {
|
||||
pdu.add(new VariableBinding(new OID(baseOID + "." + i)));
|
||||
}
|
||||
pdu.setType(PDU.GET);
|
||||
|
||||
event = snmp.send(pdu, target);
|
||||
if (event != null && event.getResponse() != null) {
|
||||
VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]);
|
||||
String ifName = vbs[0].getVariable().toString();
|
||||
String ifType = getInterfaceType(vbs[1].getVariable().toInt());
|
||||
String speed = formatSpeed(vbs[2].getVariable().toInt());
|
||||
String status = getOperStatus(vbs[3].getVariable().toInt());
|
||||
long inBytes = vbs[4].getVariable().toLong();
|
||||
long outBytes = vbs[5].getVariable().toLong();
|
||||
SwitchBoardVO boardVO = SwitchBoardVO.builder()
|
||||
.name(ifName)
|
||||
.switchIp(GlobalConfig.SWITCH_IP)
|
||||
.type(ifType)
|
||||
.status(status)
|
||||
.inBytes(inBytes)
|
||||
.outBytes(outBytes)
|
||||
.timestamp(timestamp)
|
||||
.build();
|
||||
list.add(boardVO);
|
||||
System.out.printf("%-5d %-15s %-10s %-15s %-8s %-12d %-12d%n",
|
||||
i, ifName, ifType, speed, status, inBytes, outBytes);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// 辅助方法
|
||||
private static String getOIDDescription(String oid) {
|
||||
switch(oid) {
|
||||
case "1.3.6.1.2.1.1.1.0": return "系统描述";
|
||||
case "1.3.6.1.2.1.1.5.0": return "系统名称";
|
||||
case "1.3.6.1.2.1.1.6.0": return "物理位置";
|
||||
case "1.3.6.1.2.1.1.4.0": return "联系人";
|
||||
case "1.3.6.1.2.1.1.3.0": return "运行时间";
|
||||
default: return oid;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getInterfaceType(int type) {
|
||||
switch(type) {
|
||||
case 6: return "Ethernet";
|
||||
case 23: return "PPP";
|
||||
case 24: return "Loopback";
|
||||
case 53: return "VLAN";
|
||||
case 131: return "Tunnel";
|
||||
default: return "其他("+type+")";
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatSpeed(int speed) {
|
||||
if (speed >= 1000000000) {
|
||||
return (speed / 1000000000) + " Gbps";
|
||||
} else if (speed >= 1000000) {
|
||||
return (speed / 1000000) + " Mbps";
|
||||
} else if (speed >= 1000) {
|
||||
return (speed / 1000) + " Kbps";
|
||||
}
|
||||
return speed + " bps";
|
||||
}
|
||||
|
||||
private static String getOperStatus(int status) {
|
||||
switch(status) {
|
||||
case 1: return "Up";
|
||||
case 2: return "Down";
|
||||
case 3: return "Testing";
|
||||
case 4: return "Unknown";
|
||||
case 5: return "Dormant";
|
||||
case 6: return "NotPresent";
|
||||
case 7: return "LowerLayerDown";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getInterfaceInfoByType(Snmp snmp, Target target, String type, LinkedHashMap<String,String> oidParams) throws IOException {
|
||||
if(oidParams.isEmpty()){
|
||||
return "";
|
||||
}
|
||||
JSONObject json = new JSONObject();
|
||||
// 获取接口数量
|
||||
String ifNumberOID = "";
|
||||
List<String> filter_value = new ArrayList<>();
|
||||
if(StringUtils.equals(type,"switchNet")){
|
||||
ifNumberOID = GlobalConfig.NET_INDEX_OID;
|
||||
filter_value = GlobalConfig.NET_FILTER;
|
||||
}else if(StringUtils.equals(type,"switchModule")){
|
||||
ifNumberOID = GlobalConfig.MODULE_INDEX_OID;
|
||||
filter_value = GlobalConfig.MODULE_FILTER;
|
||||
}else if(StringUtils.equals(type,"switchMpu")){
|
||||
ifNumberOID = GlobalConfig.MPU_INDEX_OID;
|
||||
filter_value = GlobalConfig.MPU_FILTER;
|
||||
}else if(StringUtils.equals(type,"switchPwr")){
|
||||
ifNumberOID = GlobalConfig.PWR_INDEX_OID;
|
||||
filter_value = GlobalConfig.PWR_FILTER;
|
||||
}else if(StringUtils.equals(type,"switchFan")){
|
||||
ifNumberOID = GlobalConfig.FAN_INDEX_OID;
|
||||
filter_value = GlobalConfig.FAN_FILTER;
|
||||
}else {
|
||||
ifNumberOID = GlobalConfig.OTHER_INDEX_OID;
|
||||
filter_value = GlobalConfig.OTHER_FILTER;
|
||||
}
|
||||
AssertLog.info("交换机采集type={},筛选端口={}",type,JSON.toJSONString(filter_value));
|
||||
if(StringUtils.isBlank(ifNumberOID)){
|
||||
System.err.println("未初始化索引OID");
|
||||
return "";
|
||||
}
|
||||
OID oid = new OID(ifNumberOID);
|
||||
PDU pdu = new PDU();
|
||||
pdu.add(new VariableBinding(new OID(ifNumberOID)));
|
||||
pdu.setType(PDU.GETBULK);
|
||||
pdu.setMaxRepetitions(100); // 每次获取最多 100 个
|
||||
pdu.setNonRepeaters(0);
|
||||
List<Integer> indexes = new ArrayList<>();
|
||||
boolean finished = false;
|
||||
while (!finished) {
|
||||
ResponseEvent response = snmp.send(pdu, target);
|
||||
PDU responsePdu = response.getResponse();
|
||||
if (responsePdu == null) {
|
||||
System.err.println("Timeout: No response from device.");
|
||||
break;
|
||||
}
|
||||
if (responsePdu.getErrorStatus() != 0) {
|
||||
System.err.println("Error: " + responsePdu.getErrorStatusText());
|
||||
break;
|
||||
}
|
||||
// 处理每个返回结果
|
||||
for (VariableBinding vb : responsePdu.getVariableBindings()) {
|
||||
OID returnedOid = vb.getOid();
|
||||
// 检查是否仍在目标 OID 子树下
|
||||
if (!returnedOid.startsWith(oid)) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
// 提取最后一个值:就是 ifIndex 编号
|
||||
int ifIndex = returnedOid.get(returnedOid.size() - 1);
|
||||
int value = vb.getVariable().toInt();
|
||||
if(CollectionUtil.isEmpty(filter_value)){
|
||||
// AssertLog.info("交换机采集type={},索引返回:值={}",type,vb);
|
||||
indexes.add(ifIndex);
|
||||
}else{
|
||||
if(filter_value.contains(String.valueOf(value))){
|
||||
// AssertLog.info("交换机采集type={},索引返回:值={}",type,vb);
|
||||
indexes.add(ifIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新下一次请求的起始 OID(SNMP Walk)
|
||||
pdu.set(0, new VariableBinding(responsePdu.getVariableBindings().get(0).getOid()));
|
||||
}
|
||||
if(CollectionUtil.isEmpty(indexes)){
|
||||
System.err.println("返回获取索引列表为空");
|
||||
return "";
|
||||
}
|
||||
//去重
|
||||
indexes = indexes.stream().distinct().collect(Collectors.toList());
|
||||
// System.out.println("\n=== 接口数量: " + indexes.size() + " ===");
|
||||
// System.out.println("\n=== 接口列表: " + JSON.toJSONString(indexes));
|
||||
String[] ifOIDs = oidParams.keySet().toArray(new String[0]);
|
||||
String[] params = oidParams.values().toArray(new String[0]);
|
||||
if(!StringUtils.equals(type,"switchNet") && !StringUtils.equals(type,"switchModule")
|
||||
&& !StringUtils.equals(type,"switchMpu") && !StringUtils.equals(type,"switchPwr")
|
||||
&& !StringUtils.equals(type,"switchFan")){
|
||||
String os = "";
|
||||
String ps = "";
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if(type.toLowerCase().contains(params[i].toLowerCase())){
|
||||
if(StringUtils.isBlank(ps)){
|
||||
ps += params[i];
|
||||
os += ifOIDs[i];
|
||||
}else {
|
||||
ps += "," + params[i];
|
||||
os += "," + ifOIDs[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ifOIDs = os.split(",");
|
||||
params = ps.split(",");
|
||||
List<Integer> otherList = new ArrayList<>();
|
||||
otherList.add(indexes.get(0));
|
||||
indexes = otherList;
|
||||
}
|
||||
// 获取每个接口的信息
|
||||
// String[] ifOIDs = {
|
||||
// "1.3.6.1.2.1.2.2.1.2", // ifDescr
|
||||
// "1.3.6.1.2.1.2.2.1.3", // ifType
|
||||
// "1.3.6.1.2.1.2.2.1.5", // ifSpeed
|
||||
// "1.3.6.1.2.1.2.2.1.8", // ifOperStatus
|
||||
// "1.3.6.1.2.1.31.1.1.1.6", // ifInOctets
|
||||
// "1.3.6.1.2.1.31.1.1.1.10" // ifOutOctets
|
||||
//// "1.3.6.1.2.1.2.2.1.10", // ifInOctets
|
||||
//// "1.3.6.1.2.1.2.2.1.16" // ifOutOctets
|
||||
// };
|
||||
if(ifOIDs == null || ifOIDs.length == 0){
|
||||
return "";
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (int i = 0; i < indexes.size(); i++) {
|
||||
pdu = new PDU();
|
||||
for (String baseOID : ifOIDs) {
|
||||
pdu.add(new VariableBinding(new OID(baseOID + "." + indexes.get(i))));
|
||||
}
|
||||
pdu.setType(PDU.GET);
|
||||
ResponseEvent event = snmp.send(pdu, target);
|
||||
// System.out.printf("%-5s %-15s %-10s %-15s %-8s %-12s %-12s%n",
|
||||
// "Index", "Name", "Type", "Speed", "Status", "InBytes", "OutBytes");
|
||||
// if (event != null && event.getResponse() != null) {
|
||||
// VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]);
|
||||
// String ifName = vbs[0].getVariable().toString();
|
||||
// String ifType = getInterfaceType(vbs[1].getVariable().toInt());
|
||||
// String speed = formatSpeed(vbs[2].getVariable().toInt());
|
||||
// String status = getOperStatus(vbs[3].getVariable().toInt());
|
||||
// long inBytes = vbs[4].getVariable().toLong();
|
||||
// long outBytes = vbs[5].getVariable().toLong();
|
||||
// System.out.printf("%-15s %-10s %-15s %-8s %-12d %-12d%n",
|
||||
// ifName, ifType, speed, status, inBytes, outBytes);
|
||||
// }
|
||||
|
||||
if (event != null && event.getResponse() != null) {
|
||||
VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]);
|
||||
for (int m = 0; m < params.length; m++) {
|
||||
// AssertLog.info("参数名:{},值={}", params[m],vbs[m]);
|
||||
json.put(params[m],vbs[m].getVariable().toString());
|
||||
}
|
||||
}
|
||||
list.add(json.toString());
|
||||
}
|
||||
return JSON.toJSONString(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结果封装类
|
||||
*/
|
||||
public static class MapArrays {
|
||||
private final String[] keys;
|
||||
private final String[] values;
|
||||
|
||||
public MapArrays(String[] keys, String[] values) {
|
||||
this.keys = keys != null ? keys : new String[0];
|
||||
this.values = values != null ? values : new String[0];
|
||||
}
|
||||
|
||||
public String[] getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return keys.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Keys: " + Arrays.toString(keys) +
|
||||
"\nValues: " + Arrays.toString(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.tongran.agent.client.core.vo.SystemVO;
|
||||
import com.tongran.agent.client.service.SystemService;
|
||||
import com.tongran.agent.client.utils.AgentDataUtil;
|
||||
import com.tongran.agent.client.utils.AgentUtil;
|
||||
import com.tongran.agent.client.utils.AssertLog;
|
||||
import org.springframework.stereotype.Service;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
@@ -22,14 +23,16 @@ import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class SystemServiceImpl implements SystemService {
|
||||
// 改为单例,只初始化一次!
|
||||
private static final SystemInfo SI = new SystemInfo();
|
||||
private final HardwareAbstractionLayer hal = SI.getHardware();
|
||||
private final OperatingSystem os = SI.getOperatingSystem();
|
||||
@Override
|
||||
public SystemVO get() {
|
||||
SystemVO systemVO = SystemVO.builder().build();
|
||||
try {
|
||||
SystemInfo si = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
HardwareAbstractionLayer hal = SI.getHardware();
|
||||
CentralProcessor processor = hal.getProcessor();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
|
||||
System.out.println("=========================================================");
|
||||
// 1. 操作系统基本信息
|
||||
@@ -59,7 +62,7 @@ public class SystemServiceImpl implements SystemService {
|
||||
System.out.println("\n=== 磁盘信息 ===");
|
||||
systemVO.setDiskSizeTotal(diskSpace()); //硬盘:总可用空间
|
||||
systemVO.setBootTime(systemBootTime(hal.getProcessor())); //系统启动时间
|
||||
systemVO.setUname(systemDescription(si)); //系统描述
|
||||
systemVO.setUname(systemDescription(SI)); //系统描述
|
||||
systemVO.setLocalTime(localTime()); //系统本地时间
|
||||
systemVO.setUpTime(systemUptime(os)); //系统正常运行时间
|
||||
System.out.println("硬盘:总可用空间: " + systemVO.getDiskSizeTotal());
|
||||
@@ -159,11 +162,15 @@ public class SystemServiceImpl implements SystemService {
|
||||
memInfo.getOrDefault("Buffers", 0L) +
|
||||
memInfo.getOrDefault("Cached", 0L) +
|
||||
memInfo.getOrDefault("SReclaimable", 0L));
|
||||
AssertLog.info("memoryUtilizationCollect,总内存total:{}",total);
|
||||
AssertLog.info("memoryUtilizationCollect,可用内存available:{}",available);
|
||||
// 实际内存使用率
|
||||
long cached = memInfo.getOrDefault("Cached", 0L) +
|
||||
memInfo.getOrDefault("SReclaimable", 0L);
|
||||
long buffers = memInfo.getOrDefault("Buffers", 0L);
|
||||
double actualUsage = (double)(total - available - cached - buffers) / total * 100;
|
||||
// long cached = memInfo.getOrDefault("Cached", 0L) +
|
||||
// memInfo.getOrDefault("SReclaimable", 0L);
|
||||
// long buffers = memInfo.getOrDefault("Buffers", 0L);
|
||||
// double actualUsage = (double)(total - available - cached - buffers) / total * 100;
|
||||
double actualUsage = (double)(total - available) / total * 100;
|
||||
AssertLog.info("memoryUtilizationCollect,内存使用率:{}",actualUsage);
|
||||
System.out.printf("实际内存使用率: %.2f%%\n", actualUsage);
|
||||
return actualUsage;
|
||||
} catch (IOException e) {
|
||||
@@ -240,18 +247,17 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
private String handleSystemSwOs(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("操作系统: " + os.getFamily());
|
||||
return os.getFamily();
|
||||
// System.out.println("操作系统: " + os.getFamily());
|
||||
System.out.println("操作系统: " + os.toString());
|
||||
return os.toString();
|
||||
}
|
||||
|
||||
private String handleSystemSwArch(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
CentralProcessor processor = hal.getProcessor();
|
||||
System.out.println("=========================================================");
|
||||
String arch = os.getVersionInfo().getVersion() + " " + os.getBitness() + "位";
|
||||
// String arch = os.getVersionInfo().getVersion() + " " + os.getBitness() + "位";
|
||||
String arch = os.getBitness() + "位";
|
||||
System.out.println("操作系统架构: " + arch);
|
||||
return arch;
|
||||
}
|
||||
@@ -276,8 +282,6 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
private int handleUsersNum(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
System.out.println("=========================================================");
|
||||
int usersNum = os.getSessions().size();
|
||||
System.out.println("登录用户数: " + usersNum);
|
||||
@@ -292,8 +296,6 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
public long handleSystemBoottime(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
long boottime = systemBootTime(hal.getProcessor());
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("系统启动时间: " + boottime);
|
||||
@@ -301,8 +303,7 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
private String handleSystemUname(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
String uname = systemDescription(si);
|
||||
String uname = systemDescription(SI);
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("系统描述: " + uname);
|
||||
return uname;
|
||||
@@ -316,8 +317,6 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
private long handleSystemUptime(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
System.out.println("=========================================================");
|
||||
long uptime = systemUptime(os);
|
||||
System.out.println("系统正常运行时间: " + uptime);
|
||||
@@ -325,8 +324,6 @@ public class SystemServiceImpl implements SystemService {
|
||||
}
|
||||
|
||||
private long handleProcNum(){
|
||||
SystemInfo si = new SystemInfo();
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
System.out.println("=========================================================");
|
||||
long procNum = os.getProcessCount();
|
||||
System.out.println("进程数: " + procNum);
|
||||
@@ -381,8 +378,6 @@ public class SystemServiceImpl implements SystemService {
|
||||
|
||||
// 获取系统描述
|
||||
public String systemDescription(SystemInfo si) {
|
||||
OperatingSystem os = si.getOperatingSystem();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
return "操作系统: " + os.toString() + ",系统版本: " + os.getVersionInfo().toString() + "" +
|
||||
",处理器: " + hal.getProcessor().getProcessorIdentifier().getName() +
|
||||
",物理内存: " + hal.getMemory().getTotal() / (1024 * 1024 * 1024) + " GB";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
|
||||
import com.tongran.agent.client.netty.config.ConnectionConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClientExample {
|
||||
public static void main(String[] args) {
|
||||
EnhancedConnectionManager manager = new EnhancedConnectionManager();
|
||||
|
||||
try {
|
||||
// 配置多个连接
|
||||
List<ConnectionConfig> configs = Arrays.asList(
|
||||
// new ConnectionConfig("db_server", "192.168.1.101", 3306, 5),
|
||||
// new ConnectionConfig("redis_server", "192.168.1.102", 6379, 3),
|
||||
// new ConnectionConfig("api_server", "api.example.com", 8080, 10),
|
||||
new ConnectionConfig("server1", "127.0.0.1", 6610, 5)
|
||||
);
|
||||
|
||||
// 批量创建连接
|
||||
Map<String, Boolean> results = manager.createConnections(configs);
|
||||
System.out.println("连接创建结果: " + results);
|
||||
|
||||
// 等待连接建立
|
||||
Thread.sleep(3000);
|
||||
|
||||
// 根据消息类型路由
|
||||
manager.routeMessageByType("TYPE_A", "Database query");
|
||||
// manager.routeMessageByType("TYPE_B", "Cache operation");
|
||||
// manager.routeMessageByType("UNKNOWN", "Broadcast message");
|
||||
|
||||
// 检查连接状态
|
||||
Map<String, Boolean> status = manager.getConnectionStatus();
|
||||
System.out.println("连接状态: " + status);
|
||||
|
||||
Thread.sleep(5000);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
// manager.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
public class CpuUsageFromProcStat {
|
||||
|
||||
private static final String PROC_STAT = "/proc/stat";
|
||||
private static long[] prevCpuTime = null;
|
||||
|
||||
public static double getCpuUsage() throws Exception {
|
||||
List<String> lines = Files.readAllLines(Paths.get(PROC_STAT));
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("cpu ")) {
|
||||
long[] current = parseCpuLine(line);
|
||||
if (prevCpuTime == null) {
|
||||
prevCpuTime = current;
|
||||
Thread.sleep(500); // 初始等待
|
||||
return 0.0; // 第一次不返回数据
|
||||
}
|
||||
|
||||
double usage = calculateCpuUsage(prevCpuTime, current);
|
||||
prevCpuTime = current; // 更新
|
||||
return usage;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("无法读取 /proc/stat 中的 cpu 数据");
|
||||
}
|
||||
|
||||
private static long[] parseCpuLine(String line) {
|
||||
String[] parts = line.split("\\s+");
|
||||
// index: user, nice, system, idle, iowait, irq, softirq, steal
|
||||
long user = Long.parseLong(parts[1]);
|
||||
long nice = Long.parseLong(parts[2]);
|
||||
long system = Long.parseLong(parts[3]);
|
||||
long idle = Long.parseLong(parts[4]);
|
||||
long iowait = Long.parseLong(parts[5]);
|
||||
long irq = Long.parseLong(parts[6]);
|
||||
long softirq = Long.parseLong(parts[7]);
|
||||
|
||||
long idleTotal = idle + iowait;
|
||||
long systemTotal = user + nice + system + irq + softirq;
|
||||
long total = idleTotal + systemTotal;
|
||||
|
||||
return new long[]{idleTotal, total};
|
||||
}
|
||||
|
||||
private static double calculateCpuUsage(long[] prev, long[] curr) {
|
||||
long idleDiff = curr[0] - prev[0];
|
||||
long totalDiff = curr[1] - prev[1];
|
||||
|
||||
if (totalDiff <= 0) return 0.0;
|
||||
|
||||
return 1.0 - ((double) idleDiff / totalDiff);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 预热
|
||||
getCpuUsage();
|
||||
Thread.sleep(1000);
|
||||
|
||||
// 正式采集
|
||||
for (int i = 0; i < 5; i++) {
|
||||
double usage = getCpuUsage();
|
||||
System.out.printf("CPU 使用率: %.2f%%\n", usage * 100);
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CrontabManager {
|
||||
|
||||
// // 要添加的定时任务
|
||||
// private static final String CRON_JOB = "*/10 * * * * /usr/local/tongran/sbin/test2.sh >/dev/null 2>&1";
|
||||
// // 用于判断是否已存在的关键标识(可以是脚本路径)
|
||||
// private static final String JOB_IDENTIFIER = "/usr/local/tongran/sbin/test2.sh";
|
||||
|
||||
/**
|
||||
* 检查并添加定时任务
|
||||
*/
|
||||
public static boolean ensureCronJobExists(String CRON_JOB, String JOB_IDENTIFIER) {
|
||||
try {
|
||||
// 1. 读取当前用户的 crontab
|
||||
ProcessBuilder pb = new ProcessBuilder("crontab", "-l");
|
||||
pb.redirectErrorStream(true);
|
||||
Process process = pb.start();
|
||||
|
||||
// 设置超时(防止卡死)
|
||||
if (!process.waitFor(5, TimeUnit.SECONDS)) {
|
||||
process.destroy();
|
||||
throw new IOException("crontab -l timeout");
|
||||
}
|
||||
|
||||
StringBuilder crontabContent = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
crontabContent.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int exitCode = process.exitValue();
|
||||
if (exitCode != 0 && exitCode != 1) {
|
||||
// exit code 1 表示没有 crontab 文件(正常)
|
||||
throw new IOException("crontab -l failed with exit code: " + exitCode);
|
||||
}
|
||||
|
||||
String content = crontabContent.toString();
|
||||
|
||||
// 2. 检查是否已存在该任务
|
||||
if (content.contains(JOB_IDENTIFIER)) {
|
||||
System.out.println("Crontab 任务已存在,无需添加。");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. 如果不存在,追加任务
|
||||
String newCrontab;
|
||||
if (content.trim().isEmpty()) {
|
||||
// 原来没有 crontab
|
||||
newCrontab = CRON_JOB + "\n";
|
||||
} else {
|
||||
// 原来有 crontab,在末尾添加新任务
|
||||
newCrontab = content;
|
||||
if (!content.endsWith("\n")) {
|
||||
newCrontab += "\n";
|
||||
}
|
||||
newCrontab += CRON_JOB + "\n";
|
||||
}
|
||||
|
||||
|
||||
// 4. 写入新的 crontab
|
||||
ProcessBuilder pbWrite = new ProcessBuilder("crontab", "-");
|
||||
pbWrite.redirectErrorStream(true);
|
||||
Process writeProcess = pbWrite.start();
|
||||
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(writeProcess.getOutputStream())) {
|
||||
writer.write(newCrontab);
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
if (!writeProcess.waitFor(5, TimeUnit.SECONDS)) {
|
||||
writeProcess.destroy();
|
||||
throw new IOException("crontab - write timeout");
|
||||
}
|
||||
|
||||
int writeExitCode = writeProcess.exitValue();
|
||||
if (writeExitCode != 0) {
|
||||
throw new IOException("crontab - write failed with exit code: " + writeExitCode);
|
||||
}
|
||||
|
||||
System.out.println("Crontab 任务添加成功:\n" + CRON_JOB);
|
||||
return true;
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// === 使用示例 ===
|
||||
public static void main(String[] args) {
|
||||
// 要添加的定时任务
|
||||
String CRON_JOB = "*/10 * * * * /usr/local/tongran/sbin/test2.sh >/dev/null 2>&1";
|
||||
// 用于判断是否已存在的关键标识(可以是脚本路径)
|
||||
String JOB_IDENTIFIER = "/usr/local/tongran/sbin/test2.sh";
|
||||
// boolean success = ensureCronJobExists(CRON_JOB,JOB_IDENTIFIER);
|
||||
// if (success) {
|
||||
// System.out.println("✅ Crontab 状态正常。");
|
||||
// } else {
|
||||
// System.err.println("❌ 操作失败,请检查权限或路径。");
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前定时任务中是否包含目标脚本
|
||||
*/
|
||||
public static boolean isCronEntryExists(String TARGET_SCRIPT) {
|
||||
try {
|
||||
AssertLog.info("验证: 检查当前定时任务是否包含 {}",TARGET_SCRIPT);
|
||||
|
||||
Process process = Runtime.getRuntime().exec("crontab -l");
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream())
|
||||
);
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains(TARGET_SCRIPT)) {
|
||||
AssertLog.info("✅ 发现目标脚本在当前定时任务中: {}",line.trim());
|
||||
reader.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0 && exitCode != 1) { // exitCode 1 表示没有 crontab
|
||||
throw new IOException("获取当前 crontab 失败,退出码: " + exitCode);
|
||||
}
|
||||
AssertLog.info("当前定时任务中未找到目标脚本");
|
||||
return false;
|
||||
}catch (IOException | InterruptedException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 0. 清空 tr_live.cron 文件内容
|
||||
*/
|
||||
public static void clearCronFile(String cronDir, String CRON_FILE_PATH) {
|
||||
try {
|
||||
AssertLog.info("步骤0: 清空 {} 文件内容",CRON_FILE_PATH);
|
||||
|
||||
// 确保目录存在
|
||||
new File(cronDir).mkdirs();
|
||||
|
||||
// 创建空文件(如果不存在)或清空现有内容
|
||||
Files.write(Paths.get(CRON_FILE_PATH), new byte[0]);
|
||||
|
||||
AssertLog.info("✅ {} 文件已清空",CRON_FILE_PATH);
|
||||
}catch (IOException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 1. 备份当前定时任务到文件
|
||||
*/
|
||||
public static void backupCurrentCrontab(String CRON_FILE_PATH) {
|
||||
try {
|
||||
AssertLog.info("步骤1: 备份当前定时任务到 {}",CRON_FILE_PATH);
|
||||
Process process = Runtime.getRuntime().exec("crontab -l");
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream())
|
||||
);
|
||||
// 读取当前 crontab 内容
|
||||
StringBuilder currentCrontab = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
currentCrontab.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0 && exitCode != 1) { // exitCode 1 表示没有 crontab
|
||||
throw new IOException("获取当前 crontab 失败,退出码: " + exitCode);
|
||||
}
|
||||
|
||||
// 写入当前定时任务内容
|
||||
Files.write(Paths.get(CRON_FILE_PATH), currentCrontab.toString().getBytes());
|
||||
|
||||
AssertLog.info("✅ 当前定时任务已保存到: {}",CRON_FILE_PATH);
|
||||
}catch (IOException | InterruptedException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. 在文件末尾添加新的定时任务
|
||||
*/
|
||||
public static void appendNewCronEntry(String CRON_FILE_PATH, String NEW_CRON_ENTRY) {
|
||||
try {
|
||||
AssertLog.info("步骤2: 在文件末尾添加新定时任务");
|
||||
|
||||
// 检查是否已存在该定时任务
|
||||
String fileContent = new String(Files.readAllBytes(Paths.get(CRON_FILE_PATH)));
|
||||
if (fileContent.contains(NEW_CRON_ENTRY)) {
|
||||
AssertLog.info("⚠️ 定时任务已存在,跳过添加");
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加新定时任务到文件末尾
|
||||
Files.write(
|
||||
Paths.get(CRON_FILE_PATH),
|
||||
(NEW_CRON_ENTRY + "\n").getBytes(),
|
||||
StandardOpenOption.APPEND
|
||||
);
|
||||
AssertLog.info("✅ 新定时任务已添加到文件末尾");
|
||||
}catch (IOException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. 清除当前所有定时任务
|
||||
*/
|
||||
public static void clearCurrentCrontab() {
|
||||
try {
|
||||
AssertLog.info("步骤3: 清除当前所有定时任务");
|
||||
|
||||
// 创建一个空的临时文件
|
||||
File tempFile = File.createTempFile("empty_cron", ".tmp");
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
// 用空文件替换当前 crontab
|
||||
Process process = Runtime.getRuntime().exec("crontab " + tempFile.getAbsolutePath());
|
||||
int exitCode = process.waitFor();
|
||||
|
||||
if (exitCode != 0) {
|
||||
throw new IOException("清除当前 crontab 失败,退出码: " + exitCode);
|
||||
}
|
||||
|
||||
AssertLog.info("✅ 当前所有定时任务已清除");
|
||||
}catch (IOException | InterruptedException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. 重新加载定时任务文件
|
||||
*/
|
||||
public static void installNewCrontab(String CRON_FILE_PATH) {
|
||||
try {
|
||||
AssertLog.info("步骤4: 加载新的定时任务文件");
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!Files.exists(Paths.get(CRON_FILE_PATH))) {
|
||||
throw new IOException("定时任务文件不存在: " + CRON_FILE_PATH);
|
||||
}
|
||||
|
||||
// 执行 crontab 命令加载新文件
|
||||
Process process = Runtime.getRuntime().exec("crontab " + CRON_FILE_PATH);
|
||||
|
||||
// 读取输出
|
||||
BufferedReader stdout = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream())
|
||||
);
|
||||
BufferedReader stderr = new BufferedReader(
|
||||
new InputStreamReader(process.getErrorStream())
|
||||
);
|
||||
|
||||
String outputLine;
|
||||
while ((outputLine = stdout.readLine()) != null) {
|
||||
System.out.println("STDOUT: " + outputLine);
|
||||
}
|
||||
String errorLine;
|
||||
while ((errorLine = stderr.readLine()) != null) {
|
||||
System.err.println("STDERR: " + errorLine);
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
throw new IOException("加载新 crontab 失败,退出码: " + exitCode);
|
||||
}
|
||||
|
||||
AssertLog.info("✅ 新定时任务已成功加载");
|
||||
}catch (IOException | InterruptedException e) {
|
||||
System.err.println("操作 crontab 失败:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 验证最终结果
|
||||
*/
|
||||
public static void verifyFinalResult(String TARGET_SCRIPT) {
|
||||
try {
|
||||
AssertLog.info("\n--- 验证最终定时任务结果 ---");
|
||||
Process process = Runtime.getRuntime().exec("crontab -l");
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream())
|
||||
);
|
||||
|
||||
boolean found = false;
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
if (line.contains(TARGET_SCRIPT)) {
|
||||
AssertLog.info("✅ 确认 " + TARGET_SCRIPT + " 已成功添加到定时任务中");
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
if (!found) {
|
||||
AssertLog.info("❌ 未在当前定时任务中找到目标脚本");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
AssertLog.info("验证 crontab 时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
|
||||
import com.tongran.agent.client.netty.config.ConnectionConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 增强版连接管理器
|
||||
*/
|
||||
public class EnhancedConnectionManager {
|
||||
// private final MultiTargetNettyClient client;
|
||||
private final Map<String, ConnectionConfig> connectionConfigs;
|
||||
|
||||
public EnhancedConnectionManager() {
|
||||
// this.client = new MultiTargetNettyClient();
|
||||
this.connectionConfigs = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建连接
|
||||
*/
|
||||
public Map<String, Boolean> createConnections(List<ConnectionConfig> configs) {
|
||||
Map<String, Boolean> results = new HashMap<>();
|
||||
|
||||
// configs.forEach(config -> {
|
||||
// boolean success = client.createConnection(
|
||||
// config.getConnectionKey(),
|
||||
// config.getHost(),
|
||||
// config.getPort(),
|
||||
// config.getTimeout()
|
||||
// );
|
||||
// if (success) {
|
||||
// connectionConfigs.put(config.getConnectionKey(), config);
|
||||
// }
|
||||
// results.put(config.getConnectionKey(), success);
|
||||
// });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据消息类型路由到不同连接
|
||||
*/
|
||||
public void routeMessageByType(String messageType, String message) {
|
||||
// 这里可以根据业务逻辑决定发送到哪个连接
|
||||
// switch (messageType) {
|
||||
// case "TYPE_A":
|
||||
// client.sendMessage("server1", "[TYPE_A] " + message);
|
||||
// break;
|
||||
// case "TYPE_B":
|
||||
// client.sendMessage("server2", "[TYPE_B] " + message);
|
||||
// break;
|
||||
// case "TYPE_C":
|
||||
// client.sendMessage("server3", "[TYPE_C] " + message);
|
||||
// break;
|
||||
// default:
|
||||
// // 广播到所有连接
|
||||
// connectionConfigs.keySet().forEach(key ->
|
||||
// client.sendMessage(key, "[BROADCAST] " + message));
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有连接状态
|
||||
*/
|
||||
public Map<String, Boolean> getConnectionStatus() {
|
||||
Map<String, Boolean> status = new HashMap<>();
|
||||
// connectionConfigs.forEach((key, config) -> {
|
||||
// // 这里可以添加更详细的状态检查
|
||||
// status.put(key, client.sendMessage(key, "PING"));
|
||||
// });
|
||||
return status;
|
||||
}
|
||||
|
||||
// public void shutdown() {
|
||||
// client.shutdown();
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FileCleaner {
|
||||
|
||||
/**
|
||||
* 删除指定目录中 30 天前修改的文件
|
||||
*
|
||||
* @param directoryPath 目录路径,例如:"/tmp/logs"
|
||||
* @param days 保留天数,传入 30 表示删除 30 天以上的文件
|
||||
*/
|
||||
public static void cleanOldFiles(String directoryPath, int days) {
|
||||
Path dir = Paths.get(directoryPath);
|
||||
|
||||
// 检查目录是否存在且是目录
|
||||
if (!Files.exists(dir)) {
|
||||
System.err.println("目录不存在: " + directoryPath);
|
||||
return;
|
||||
}
|
||||
if (!Files.isDirectory(dir)) {
|
||||
System.err.println("路径不是目录: " + directoryPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算 30 天前的时间点(Instant)
|
||||
Instant cutoffTime = Instant.now().minus(days, ChronoUnit.DAYS);
|
||||
List<Path> deletedFiles = new ArrayList<>();
|
||||
List<Path> failedFiles = new ArrayList<>();
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
for (Path file : stream) {
|
||||
// 只处理普通文件(跳过子目录等)
|
||||
if (Files.isRegularFile(file)) {
|
||||
try {
|
||||
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
|
||||
Instant lastModifiedTime = attrs.lastModifiedTime().toInstant();
|
||||
|
||||
if (lastModifiedTime.isBefore(cutoffTime)) {
|
||||
Files.delete(file); // 删除文件
|
||||
deletedFiles.add(file);
|
||||
System.out.println("已删除: " + file + " (修改时间: " + lastModifiedTime + ")");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("无法读取或删除文件: " + file + " -> " + e.getMessage());
|
||||
failedFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("遍历目录失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
System.out.println("✅ 清理完成:");
|
||||
System.out.println(" 删除文件数: " + deletedFiles.size());
|
||||
System.out.println(" 删除失败数: " + failedFiles.size());
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public static void main(String[] args) {
|
||||
// String logDir = "/tmp/logs"; // 替换为你的实际目录
|
||||
// cleanOldFiles(logDir, 30); // 删除 30 天以上的文件
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class MachineFingerprint {
|
||||
|
||||
/**
|
||||
* 获取服务器硬件指纹:MD5( MAC + CPU信息 )
|
||||
*/
|
||||
public static String getHardwareFingerprint() {
|
||||
StringBuilder data = new StringBuilder();
|
||||
|
||||
// 1. 获取第一个非回环网卡的 MAC 地址
|
||||
String mac = getFirstValidMacAddress();
|
||||
if (mac != null) {
|
||||
data.append(mac);
|
||||
System.out.println("MAC 信息="+mac);
|
||||
} else {
|
||||
System.err.println("无法获取 MAC 地址");
|
||||
}
|
||||
|
||||
// 2. 获取 CPU 信息(如 CPU 型号、序列号)
|
||||
String cpuInfo = getCpuInfo();
|
||||
if (cpuInfo != null) {
|
||||
System.out.println("CPU 信息="+cpuInfo);
|
||||
data.append(cpuInfo);
|
||||
} else {
|
||||
System.err.println("无法获取 CPU 信息");
|
||||
}
|
||||
data.append(System.currentTimeMillis());
|
||||
if (data.length() == 0) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
// 3. 生成 MD5
|
||||
return md5(data.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第一个非回环、UP 状态网卡的 MAC 地址
|
||||
*/
|
||||
private static String getFirstValidMacAddress() {
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = interfaces.nextElement();
|
||||
if (ni.isLoopback() || !ni.isUp()) continue;
|
||||
byte[] mac = ni.getHardwareAddress();
|
||||
if (mac != null && mac.length > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < mac.length; i++) {
|
||||
sb.append(String.format("%02X", mac[i]));
|
||||
if (i < mac.length - 1) sb.append(":");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 CPU 信息(CPU 型号 + 序列号,如果存在)
|
||||
*/
|
||||
private static String getCpuInfo() {
|
||||
StringBuilder cpuInfo = new StringBuilder();
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
boolean found = false;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains("model name")) {
|
||||
cpuInfo.append(line.split(":")[1].trim()).append(";");
|
||||
found = true;
|
||||
}
|
||||
// 树莓派等设备有 cpu serial
|
||||
if (line.contains("Serial")) {
|
||||
cpuInfo.append("Serial=").append(line.split(":")[1].trim());
|
||||
found = true;
|
||||
}
|
||||
// 多数 x86 服务器没有 serial,可用 processor 数量做补充
|
||||
if (line.startsWith("processor")) {
|
||||
// 可选:记录核心数
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
return found ? cpuInfo.toString() : null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 MD5 哈希
|
||||
*/
|
||||
private static String md5(String input) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] digest = md.digest(input.getBytes());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : digest) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("MD5 算法不可用", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 测试 ==================
|
||||
public static void main(String[] args) {
|
||||
String fingerprint = getHardwareFingerprint();
|
||||
System.out.println("服务器硬件指纹 (MD5): " + fingerprint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,494 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import com.tongran.agent.client.core.vo.NetworkInterfaceInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 网络接口工具类(通过 ip 命令获取)
|
||||
*/
|
||||
public class NetworkInterfaceUtil {
|
||||
|
||||
public static boolean getmacVlanStatus(String interfaceName) {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"ping", "-I", interfaceName, "-c", "2", "-W", "2", "-4", "www.baidu.com"
|
||||
);
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
Process process = pb.start();
|
||||
boolean finished = process.waitFor(5, TimeUnit.SECONDS);
|
||||
|
||||
if (!finished) {
|
||||
process.destroy();
|
||||
AssertLog.warn("接口 {} ping超时", interfaceName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return process.exitValue() == 0;
|
||||
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("接口 {} ping失败: {}", interfaceName, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集所有 Ethernet 类型网卡信息
|
||||
*/
|
||||
public static List<NetworkInterfaceInfo> collectNetworkInfo() throws Exception {
|
||||
List<NetworkInterfaceInfo> result = new ArrayList<>();
|
||||
Map<String, NetworkInterfaceInfo> parentMap = new HashMap<>();
|
||||
List<NetworkInterfaceInfo> childTempList = new ArrayList<>();
|
||||
|
||||
// 获取所有网卡详细信息
|
||||
List<NetworkInterfaceDetails> allInterfaces = getNetworkInterfacesFromIpLink();
|
||||
AssertLog.info("从 ip link 获取到 {} 个网络接口", allInterfaces.size());
|
||||
|
||||
for (NetworkInterfaceDetails interfaceDetail : allInterfaces) {
|
||||
String name = interfaceDetail.name;
|
||||
String realName = interfaceDetail.realName;
|
||||
boolean isUp = "UP".equals(interfaceDetail.state);
|
||||
|
||||
// 调试信息
|
||||
AssertLog.info("开始处理网卡: {}", name);
|
||||
AssertLog.info(" 状态: {}, 是否虚拟: {}",
|
||||
isUp ? "UP" : "DOWN", interfaceDetail.isVirtual);
|
||||
|
||||
// 跳过回环、关闭的接口
|
||||
if (name.equals("lo") || !isUp) {
|
||||
AssertLog.info(" 跳过网卡 {}: 回环或未启用", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断是否为 Ethernet(通过名称约定)
|
||||
if (!isEthernetInterface(interfaceDetail)) {
|
||||
AssertLog.info(" 跳过网卡 {}: 非以太网接口", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取IP地址
|
||||
String ipv4 = getIPv4AddressFromIpAddr(name);
|
||||
String ipv6 = getIPv6AddressFromIpAddr(name);
|
||||
AssertLog.info(" IPv4: {}, IPv6: {}", ipv4, ipv6);
|
||||
|
||||
// 检查IP4信息
|
||||
// if (ipv4.equals("N/A")) {
|
||||
// AssertLog.info(" 跳过网卡 {}: 无IPv4地址", name);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// 检查物理链路状态
|
||||
boolean isConnected = isInterfaceConnected(name);
|
||||
if (!isConnected) {
|
||||
AssertLog.info(" 跳过网卡 {}: 物理链路未连接", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取网关
|
||||
String gateway = AgentUtil.getGateway(name, "www.baidu.com");
|
||||
if(gateway == null) {
|
||||
// 降级
|
||||
gateway = AgentUtil.getGatewayAddress(name);
|
||||
AssertLog.info("traceroute探测失败,改为ip route方式获取");
|
||||
}
|
||||
AssertLog.info("获取到网关为: {}", gateway);
|
||||
|
||||
// if ("N/A".equals(gateway)) {
|
||||
// AssertLog.info(" 跳过网卡 {}: 无法获取网关", name);
|
||||
// continue;
|
||||
// }
|
||||
boolean flag = false;
|
||||
if(!"N/A".equals(ipv4) && gateway != null){
|
||||
flag = true;
|
||||
}
|
||||
// 获取公网IP
|
||||
String publicIp = null;
|
||||
String ipInfo = null;
|
||||
if (flag) {
|
||||
List<String> publicIps = PublicIpFetcher.getPublicIps();
|
||||
|
||||
if (publicIps != null && !publicIps.isEmpty()) {
|
||||
AssertLog.info("网卡 {} nslookup获取到 {} 个公网IP: {}", name, publicIps.size(), publicIps);
|
||||
|
||||
for (String ip : publicIps) {
|
||||
if (ip != null && !ip.isEmpty()) {
|
||||
// 为每个公网IP添加临时路由
|
||||
boolean routeAdded = AgentUtil.addTempRoute(ip, name, gateway);
|
||||
}
|
||||
}
|
||||
ipInfo = PublicIpFetcher.getPublicIp();
|
||||
publicIp = PublicIpFetcher.extractIp(ipInfo);
|
||||
if (publicIp != null) {
|
||||
AssertLog.info(" 公网 IP: {}", publicIp);
|
||||
} else {
|
||||
AssertLog.info(" 未能提取公网 IP 地址");
|
||||
}
|
||||
// if (StringUtils.isBlank(publicIp)) {
|
||||
// AssertLog.info(" 跳过网卡 {}: 无公网IP", name);
|
||||
// continue;
|
||||
// }
|
||||
for (String ip : publicIps) {
|
||||
if (ip != null && !ip.isEmpty()) {
|
||||
// 删除临时路由
|
||||
boolean routeAdded = AgentUtil.delTempRoute(ip, name, gateway);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 创建网卡信息对象
|
||||
NetworkInterfaceInfo info = NetworkInterfaceInfo.builder()
|
||||
.name(name)
|
||||
.type("Ethernet")
|
||||
.mac(interfaceDetail.macAddress)
|
||||
.ipv4(ipv4)
|
||||
.ipv6(ipv6)
|
||||
.gateway(gateway)
|
||||
.publicIp(publicIp)
|
||||
.build();
|
||||
|
||||
// 如果有公网 IP,查询归属地
|
||||
if (publicIp != null && !publicIp.isEmpty() && ipInfo != null) {
|
||||
// 提取归属地信息
|
||||
PublicIpFetcher.IpLocationInfo location = PublicIpFetcher.extractLocation(ipInfo);
|
||||
info.setProvince(location.getProvince());
|
||||
info.setCity(location.getCity());
|
||||
info.setCarrier(location.getCarrier());
|
||||
}
|
||||
|
||||
// 判断父子关系
|
||||
if (interfaceDetail.parent != null && !interfaceDetail.parent.isEmpty()) {
|
||||
// 子接口
|
||||
AssertLog.info(" 识别为子接口: {}", name);
|
||||
String parentName = interfaceDetail.parent;
|
||||
|
||||
// 递归查找最终的父网卡
|
||||
parentName = findRootParentInterface(parentName, allInterfaces);
|
||||
if (!interfaceDetail.parent.equals(parentName)) {
|
||||
AssertLog.info(" {} 的最终父接口是: {} (原始父接口: {})",
|
||||
name, parentName, interfaceDetail.parent);
|
||||
}
|
||||
|
||||
info.setParentInterface(parentName);
|
||||
|
||||
// 查找父接口
|
||||
NetworkInterfaceInfo parent = parentMap.get(parentName);
|
||||
if (parent != null) {
|
||||
// 设置父子关系
|
||||
if (parent.getSubInterfaces() == null) {
|
||||
parent.setSubInterfaces(new ArrayList<>());
|
||||
}
|
||||
if(info.getIpv4() != null){
|
||||
info.setStatus(getmacVlanStatus(info.getName())?"1":"0");
|
||||
// 获取网卡创建时间戳
|
||||
long createTime = getInterfaceCreateTime(name);
|
||||
info.setNetCreateTime(createTime);
|
||||
}
|
||||
parent.getSubInterfaces().add(info);
|
||||
AssertLog.info(" 添加到父接口 {} 的子接口列表", parentName);
|
||||
} else {
|
||||
// 父接口还未处理,先放到临时列表
|
||||
childTempList.add(info);
|
||||
AssertLog.info(" 父接口 {} 还未处理,放入临时列表", parentName);
|
||||
}
|
||||
} else {
|
||||
// 父接口
|
||||
AssertLog.info(" 识别为父接口: {}", name);
|
||||
parentMap.put(name, info);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理父接口加入结果
|
||||
for (NetworkInterfaceInfo parent : parentMap.values()) {
|
||||
result.add(parent);
|
||||
AssertLog.info("父接口 {} 加入结果列表", parent.getName());
|
||||
}
|
||||
|
||||
// 处理子接口(父接口未出现的情况)
|
||||
for (NetworkInterfaceInfo child : childTempList) {
|
||||
String parentName = child.getParentInterface();
|
||||
NetworkInterfaceInfo parent = parentMap.get(parentName);
|
||||
|
||||
if (parent != null) {
|
||||
// 建立父子关系
|
||||
if (parent.getSubInterfaces() == null) {
|
||||
parent.setSubInterfaces(new ArrayList<>());
|
||||
}
|
||||
if(child.getIpv4() != null){
|
||||
child.setStatus(getmacVlanStatus(child.getName())?"1":"0");
|
||||
// 获取网卡创建时间戳
|
||||
long createTime = getInterfaceCreateTime(child.getName());
|
||||
child.setNetCreateTime(createTime);
|
||||
}
|
||||
parent.getSubInterfaces().add(child);
|
||||
AssertLog.info("子接口 {} 关联到父接口 {}", child.getName(), parentName);
|
||||
} else {
|
||||
// 确实没有父接口,作为独立接口
|
||||
result.add(child);
|
||||
AssertLog.info("子接口 {} 无父接口,作为独立接口", child.getName());
|
||||
}
|
||||
}
|
||||
|
||||
AssertLog.info("共收集到 {} 个有效网卡接口", result.size());
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 递归查找最终的父网卡
|
||||
*/
|
||||
private static String findRootParentInterface(String currentParent, List<NetworkInterfaceDetails> allInterfaces) {
|
||||
// 查找当前父网卡的详细信息
|
||||
for (NetworkInterfaceDetails iface : allInterfaces) {
|
||||
if (iface.name.equals(currentParent) && iface.parent != null && !iface.parent.isEmpty()) {
|
||||
// 如果当前父网卡还有父网卡,继续递归查找
|
||||
return findRootParentInterface(iface.parent, allInterfaces);
|
||||
}
|
||||
}
|
||||
// 返回最终的父网卡名称
|
||||
return currentParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 ip link 命令获取网卡详细信息
|
||||
*/
|
||||
private static List<NetworkInterfaceDetails> getNetworkInterfacesFromIpLink() throws Exception {
|
||||
List<NetworkInterfaceDetails> interfaces = new ArrayList<>();
|
||||
|
||||
Process process = Runtime.getRuntime().exec("ip -d link show");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
NetworkInterfaceDetails currentInterface = null;
|
||||
|
||||
Pattern interfacePattern = Pattern.compile("^(\\d+):\\s+(\\S+?)(?:@(\\S+))?\\s*:");
|
||||
Pattern linkPattern = Pattern.compile("\\s+link/(\\S+)\\s+([0-9a-f:]+)");
|
||||
Pattern statePattern = Pattern.compile("state\\s+(UP|DOWN|UNKNOWN)");
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
Matcher interfaceMatcher = interfacePattern.matcher(line);
|
||||
|
||||
if (interfaceMatcher.find()) {
|
||||
// 保存上一个接口
|
||||
if (currentInterface != null) {
|
||||
interfaces.add(currentInterface);
|
||||
}
|
||||
|
||||
// 创建新的接口
|
||||
currentInterface = new NetworkInterfaceDetails();
|
||||
currentInterface.index = Integer.parseInt(interfaceMatcher.group(1));
|
||||
currentInterface.name = interfaceMatcher.group(2).trim();
|
||||
|
||||
// 提取父接口(如果有@符号)
|
||||
if (interfaceMatcher.group(3) != null) {
|
||||
currentInterface.parent = interfaceMatcher.group(3).trim();
|
||||
currentInterface.realName = currentInterface.name + "@" + currentInterface.parent;
|
||||
}
|
||||
|
||||
// 检查是否是虚拟接口
|
||||
String ifaceName = currentInterface.name;
|
||||
if (ifaceName.contains(".") || ifaceName.startsWith("xdbvl") ||
|
||||
ifaceName.startsWith("docker") || ifaceName.startsWith("veth") ||
|
||||
ifaceName.startsWith("br-") || ifaceName.startsWith("virbr")) {
|
||||
currentInterface.isVirtual = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentInterface != null) {
|
||||
// 解析状态
|
||||
Matcher stateMatcher = statePattern.matcher(line);
|
||||
if (stateMatcher.find()) {
|
||||
currentInterface.state = stateMatcher.group(1);
|
||||
}
|
||||
|
||||
// 解析MAC地址
|
||||
Matcher linkMatcher = linkPattern.matcher(line);
|
||||
if (linkMatcher.find()) {
|
||||
currentInterface.macAddress = linkMatcher.group(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个接口
|
||||
if (currentInterface != null) {
|
||||
interfaces.add(currentInterface);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
process.waitFor();
|
||||
|
||||
// 第一步:收集所有实际存在的网卡名称
|
||||
Set<String> existingInterfaceNames = new HashSet<>();
|
||||
for (NetworkInterfaceDetails iface : interfaces) {
|
||||
existingInterfaceNames.add(iface.name);
|
||||
}
|
||||
|
||||
// 第二步:检查并清理父网卡设置
|
||||
for (NetworkInterfaceDetails iface : interfaces) {
|
||||
if (iface.parent != null) {
|
||||
// 如果父网卡不存在于实际网卡列表中,清空parent字段
|
||||
if (!existingInterfaceNames.contains(iface.parent)) {
|
||||
iface.parent = null;
|
||||
iface.realName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 ip addr 命令获取IPv4地址
|
||||
*/
|
||||
private static String getIPv4AddressFromIpAddr(String interfaceName) throws Exception {
|
||||
String command = String.format("ip -4 addr show %s 2>/dev/null || echo ''", interfaceName);
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains("inet ")) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
String ipWithMask = parts[1];
|
||||
reader.close();
|
||||
process.waitFor();
|
||||
return ipWithMask.split("/")[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
process.waitFor();
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 ip addr 命令获取IPv6地址
|
||||
*/
|
||||
private static String getIPv6AddressFromIpAddr(String interfaceName) throws Exception {
|
||||
String command = String.format("ip -6 addr show %s 2>/dev/null | grep -v scope.link || echo ''", interfaceName);
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains("inet6 ")) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
if (parts.length >= 2) {
|
||||
String ipWithMask = parts[1];
|
||||
reader.close();
|
||||
process.waitFor();
|
||||
return ipWithMask.split("/")[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
process.waitFor();
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为以太网接口
|
||||
*/
|
||||
private static boolean isEthernetInterface(NetworkInterfaceDetails interfaceDetail) {
|
||||
String name = interfaceDetail.name;
|
||||
// 判断是否为以太网接口
|
||||
if (name.startsWith("eth") || // Linux 传统
|
||||
name.startsWith("en") || // systemd 命名 (enp3s0)
|
||||
name.startsWith("em") // 有些主板网卡
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果有父接口,也可能是有效的子接口
|
||||
if (interfaceDetail.parent != null && !interfaceDetail.parent.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查物理链路状态
|
||||
*/
|
||||
private static boolean isInterfaceConnected(String interfaceName) {
|
||||
try {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
Process process;
|
||||
|
||||
if (os.contains("win")) {
|
||||
// Windows: 使用 wmic 检查网卡是否启用
|
||||
process = Runtime.getRuntime().exec(
|
||||
"wmic nic where \"NetEnabled=true\" get Name");
|
||||
} else {
|
||||
// Linux: 检查 operstate
|
||||
process = Runtime.getRuntime().exec(
|
||||
"cat /sys/class/net/" + interfaceName + "/operstate");
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (os.contains("win")) {
|
||||
if (line.trim().equalsIgnoreCase(interfaceName)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return "up".equals(line.trim());
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
System.err.println("无法检查接口状态: " + interfaceName);
|
||||
return false; // 保守处理
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取网卡创建时间戳(单位:秒)
|
||||
* 通过 stat 命令获取 /sys/class/net/<interface>/ 目录的创建时间
|
||||
* 返回Unix时间戳,失败返回-1
|
||||
*/
|
||||
public static long getInterfaceCreateTime(String interfaceName) {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder("stat", "-c", "%Y", "/sys/class/net/" + interfaceName);
|
||||
Process process = pb.start();
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()));
|
||||
String output = reader.readLine();
|
||||
reader.close();
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
|
||||
if (exitCode == 0 && output != null && !output.trim().isEmpty()) {
|
||||
return Long.parseLong(output.trim());
|
||||
}
|
||||
return -1;
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("获取网卡 {} 创建时间失败: {}", interfaceName, e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部类:网卡详情
|
||||
*/
|
||||
static class NetworkInterfaceDetails {
|
||||
int index;
|
||||
String name;
|
||||
|
||||
String realName;
|
||||
String state = "UNKNOWN";
|
||||
String macAddress = "";
|
||||
String parent = null;
|
||||
boolean isVirtual = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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);
|
||||
pb.redirectErrorStream(true);
|
||||
Process p = pb.start();
|
||||
|
||||
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) {
|
||||
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",
|
||||
ip + "/" + prefix, "via", gateway, "dev", dev);
|
||||
return pb.start().waitFor() == 0;
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("删除路由失败:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
package com.tongran.agent.client.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PublicIpFetcher {
|
||||
|
||||
// 主备IP查询服务
|
||||
private static final String PRIMARY_IP_SERVICE = "myip.ipip.net";
|
||||
private static final String FALLBACK_IP_SERVICE = "cip.cc";
|
||||
private static final int TIMEOUT_MS = 5_000; // 5秒超时
|
||||
|
||||
// 正则表达式匹配
|
||||
private static final String IPV4_PATTERN = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";
|
||||
private static final Pattern IP_PATTERN = Pattern.compile(IPV4_PATTERN);
|
||||
private static final Pattern CIPCC_PATTERN = Pattern.compile(
|
||||
"IP\\s*:\\s*" + IPV4_PATTERN + "\\s*地址\\s*:\\s*([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s*运营商\\s*:\\s*([^\\r\\n]+)"
|
||||
);
|
||||
private static final Pattern IPIP_PATTERN = Pattern.compile(
|
||||
"当前 IP:\\s*" + IPV4_PATTERN + "\\s*来自于:([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)"
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取公网 IP 列表(带降级策略)
|
||||
* 1. 首先尝试 nslookup myip.ipip.net
|
||||
* 2. 如果失败,尝试 nslookup cip.cc
|
||||
*/
|
||||
public static List<String> getPublicIps() {
|
||||
List<String> ipList = new ArrayList<>();
|
||||
|
||||
// 方法1: 尝试nslookup主服务
|
||||
ipList = parseNslookupResult(PRIMARY_IP_SERVICE);
|
||||
if (!ipList.isEmpty()) {
|
||||
AssertLog.info("nslookup {} 解析到IP: {}", PRIMARY_IP_SERVICE, ipList);
|
||||
return ipList;
|
||||
}
|
||||
|
||||
// 方法2: 尝试nslookup备用服务
|
||||
ipList = parseNslookupResult(FALLBACK_IP_SERVICE);
|
||||
if (!ipList.isEmpty()) {
|
||||
AssertLog.info("nslookup {} 解析到IP: {}", FALLBACK_IP_SERVICE, ipList);
|
||||
return ipList;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行nslookup并解析结果
|
||||
*/
|
||||
private static List<String> parseNslookupResult(String domain) {
|
||||
List<String> ipList = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 执行nslookup命令
|
||||
Process process = new ProcessBuilder("nslookup", domain)
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
|
||||
// 等待命令执行完成
|
||||
boolean finished = process.waitFor(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!finished) {
|
||||
process.destroyForcibly();
|
||||
return ipList;
|
||||
}
|
||||
|
||||
int exitCode = process.exitValue();
|
||||
if (exitCode != 0) {
|
||||
return ipList;
|
||||
}
|
||||
|
||||
// 读取并解析输出
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()))) {
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
|
||||
// 1. 查找"Address:"行
|
||||
if (line.contains("Address:") && !line.contains("#")) {
|
||||
Matcher matcher = IP_PATTERN.matcher(line);
|
||||
while (matcher.find()) {
|
||||
String ip = matcher.group(1);
|
||||
// 过滤DNS服务器IP和本地回环地址
|
||||
if (!isDnsServerIp(ip) && !isLocalIp(ip)) {
|
||||
ipList.add(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. 查找"Name:"行后的IP地址
|
||||
else if (line.trim().startsWith("Name:")) {
|
||||
// 读取下一行,通常是Address行
|
||||
String nextLine = reader.readLine();
|
||||
if (nextLine != null) {
|
||||
output.append(nextLine).append("\n");
|
||||
Matcher matcher = IP_PATTERN.matcher(nextLine);
|
||||
while (matcher.find()) {
|
||||
String ip = matcher.group(1);
|
||||
if (!isDnsServerIp(ip) && !isLocalIp(ip)) {
|
||||
ipList.add(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 如果以上方法都没找到,尝试在整个输出中匹配
|
||||
if (ipList.isEmpty()) {
|
||||
String allOutput = output.toString();
|
||||
Matcher matcher = IP_PATTERN.matcher(allOutput);
|
||||
while (matcher.find()) {
|
||||
String ip = matcher.group(1);
|
||||
// 跳过DNS服务器IP和本地地址
|
||||
if (!isDnsServerIp(ip) && !isLocalIp(ip)) {
|
||||
// 检查是否是DNS服务器IP(通常在输出开头)
|
||||
boolean isDnsLine = false;
|
||||
String[] lines = allOutput.split("\n");
|
||||
for (int i = 0; i < Math.min(3, lines.length); i++) {
|
||||
if (lines[i].contains("Server:") && lines[i].contains(ip)) {
|
||||
isDnsLine = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isDnsLine) {
|
||||
ipList.add(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
AssertLog.error("执行nslookup命令失败: {}", e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
AssertLog.error("nslookup命令被中断");
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("nslookup解析异常: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 去重
|
||||
return new ArrayList<>(new LinkedHashSet<>(ipList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为DNS服务器IP
|
||||
*/
|
||||
private static boolean isDnsServerIp(String ip) {
|
||||
// 常见的DNS服务器IP段
|
||||
return ip.startsWith("8.8.") || // Google DNS
|
||||
ip.startsWith("114.114.") || // 114DNS
|
||||
ip.startsWith("119.29.") || // 腾讯DNS
|
||||
ip.startsWith("223.5.5.") || // 阿里DNS
|
||||
ip.startsWith("180.76.76.76") || // 百度DNS
|
||||
ip.startsWith("1.2.4.8") || // CNNIC DNS
|
||||
ip.startsWith("208.67."); // OpenDNS
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为本地IP
|
||||
*/
|
||||
private static boolean isLocalIp(String ip) {
|
||||
return ip.startsWith("127.") ||
|
||||
ip.startsWith("192.168.") ||
|
||||
ip.startsWith("10.") ||
|
||||
ip.startsWith("172.16.") ||
|
||||
ip.startsWith("172.17.") ||
|
||||
ip.startsWith("172.18.") ||
|
||||
ip.startsWith("172.19.") ||
|
||||
ip.startsWith("172.20.") ||
|
||||
ip.startsWith("172.21.") ||
|
||||
ip.startsWith("172.22.") ||
|
||||
ip.startsWith("172.23.") ||
|
||||
ip.startsWith("172.24.") ||
|
||||
ip.startsWith("172.25.") ||
|
||||
ip.startsWith("172.26.") ||
|
||||
ip.startsWith("172.27.") ||
|
||||
ip.startsWith("172.28.") ||
|
||||
ip.startsWith("172.29.") ||
|
||||
ip.startsWith("172.30.") ||
|
||||
ip.startsWith("172.31.") ||
|
||||
ip.equals("0.0.0.0") ||
|
||||
ip.equals("255.255.255.255");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公网 IP 信息(保持原有逻辑,用于降级)
|
||||
*/
|
||||
public static String getPublicIp() {
|
||||
return getPublicIpInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 原有HTTP查询逻辑
|
||||
*/
|
||||
private static String getPublicIpInfo() {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
Future<String> future = executor.submit(() -> {
|
||||
try {
|
||||
String result = fetchFromUrl("https://" + PRIMARY_IP_SERVICE, "UTF-8");
|
||||
AssertLog.info("HTTP {}采集IP信息成功", PRIMARY_IP_SERVICE);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("HTTP主服务请求失败: {}", e.getMessage());
|
||||
throw e; // 不在这里降级,只抛异常
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
AssertLog.error("HTTP请求超时");
|
||||
future.cancel(true);
|
||||
return tryHttpFallback(); // 主线程执行降级
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("HTTP请求异常: {}", e.getMessage());
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP回退查询
|
||||
*/
|
||||
private static String tryHttpFallback() {
|
||||
try {
|
||||
Process process = new ProcessBuilder("curl", "-s", "--max-time", "5",
|
||||
"http://" + FALLBACK_IP_SERVICE)
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
|
||||
boolean finished = process.waitFor(5_000, TimeUnit.MILLISECONDS);
|
||||
if (finished && process.exitValue() == 0) {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()))) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
result.append(line).append("\n");
|
||||
}
|
||||
AssertLog.info("curl {} 采集IP信息成功", FALLBACK_IP_SERVICE);
|
||||
return parseCipCcResult(result.toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AssertLog.error("HTTP回退失败: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从HTTP URL获取IP信息
|
||||
*/
|
||||
private static String fetchFromUrl(String urlString, String charset) throws IOException {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(TIMEOUT_MS);
|
||||
connection.setReadTimeout(TIMEOUT_MS);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
throw new IOException("HTTP " + responseCode + " from " + urlString);
|
||||
}
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(connection.getInputStream(), charset))) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
return response.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析cip.cc的结果
|
||||
*/
|
||||
private static String parseCipCcResult(String result) {
|
||||
if (result == null || result.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Matcher matcher = CIPCC_PATTERN.matcher(result);
|
||||
if (matcher.find()) {
|
||||
String ip = matcher.group(1);
|
||||
String country = matcher.group(2);
|
||||
String province = matcher.group(3);
|
||||
String city = matcher.group(4);
|
||||
String carrier = matcher.group(5);
|
||||
|
||||
return String.format("当前 IP:%s 来自于:%s %s %s %s",
|
||||
ip, country, province, city, carrier);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地IP
|
||||
*/
|
||||
private static String getLocalIp() {
|
||||
try {
|
||||
String localIp = InetAddress.getLocalHost().getHostAddress();
|
||||
AssertLog.info("当前 IP:{} 来自于:本地网络", localIp);
|
||||
return localIp;
|
||||
} catch (UnknownHostException e) {
|
||||
AssertLog.error("当前IP获取失败:{}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getLocalIpInfo() {
|
||||
String ip = getLocalIp();
|
||||
return ip != null ? String.format("当前 IP:%s 来自于:本地网络", ip) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从IP信息中提取IP地址
|
||||
*/
|
||||
public static String extractIp(String ipInfo) {
|
||||
if (ipInfo == null || ipInfo.isEmpty()) return null;
|
||||
Matcher matcher = IP_PATTERN.matcher(ipInfo);
|
||||
return matcher.find() ? matcher.group(1) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从IP信息中提取归属地信息
|
||||
*/
|
||||
public static IpLocationInfo extractLocation(String ipInfo) {
|
||||
IpLocationInfo info = new IpLocationInfo();
|
||||
|
||||
if (ipInfo == null || ipInfo.isEmpty()) {
|
||||
return info;
|
||||
}
|
||||
|
||||
// 尝试匹配ipip.net格式
|
||||
Matcher ipipMatcher = IPIP_PATTERN.matcher(ipInfo);
|
||||
if (ipipMatcher.find()) {
|
||||
info.ip = ipipMatcher.group(1);
|
||||
info.country = ipipMatcher.group(2);
|
||||
info.province = ipipMatcher.group(3);
|
||||
info.city = ipipMatcher.group(4);
|
||||
info.carrier = ipipMatcher.group(5);
|
||||
return info;
|
||||
}
|
||||
|
||||
// 尝试匹配cip.cc格式
|
||||
Matcher cipMatcher = CIPCC_PATTERN.matcher(ipInfo);
|
||||
if (cipMatcher.find()) {
|
||||
info.ip = cipMatcher.group(1);
|
||||
info.country = cipMatcher.group(2);
|
||||
info.province = cipMatcher.group(3);
|
||||
info.city = cipMatcher.group(4);
|
||||
info.carrier = cipMatcher.group(5);
|
||||
return info;
|
||||
}
|
||||
|
||||
// 如果都不匹配,只提取IP
|
||||
String ip = extractIp(ipInfo);
|
||||
if (ip != null) {
|
||||
info.ip = ip;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整的归属地信息
|
||||
*/
|
||||
public static class IpLocationInfo {
|
||||
public String ip;
|
||||
public String country;
|
||||
public String province;
|
||||
public String city;
|
||||
public String carrier;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("IP: %s, 国家: %s, 省份: %s, 城市: %s, 运营商: %s",
|
||||
ip, country, province, city, carrier);
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
return province != null ? province.replaceAll("[。\\s]", "") : "";
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city != null ? city.replaceAll("[。\\s]", "") : "";
|
||||
}
|
||||
|
||||
public String getCarrier() {
|
||||
return carrier != null ? carrier.replaceAll("[。\\s]", "") : "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试方法
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
System.out.println("开始获取公网IP列表...");
|
||||
|
||||
// 测试nslookup解析
|
||||
System.out.println("测试解析myip.ipip.net:");
|
||||
List<String> ipList1 = parseNslookupResult(PRIMARY_IP_SERVICE);
|
||||
System.out.println("解析结果: " + ipList1);
|
||||
|
||||
System.out.println("\n测试解析cip.cc:");
|
||||
List<String> ipList2 = parseNslookupResult(FALLBACK_IP_SERVICE);
|
||||
System.out.println("解析结果: " + ipList2);
|
||||
|
||||
// 获取公网IP列表
|
||||
List<String> allIps = getPublicIps();
|
||||
System.out.println("\n最终获取到的公网IP列表: " + allIps);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
server:
|
||||
port: 7010
|
||||
port: -1
|
||||
servlet:
|
||||
context-path: /tr-agent-client
|
||||
|
||||
|
||||
# 接口文档配置
|
||||
knife4j:
|
||||
@@ -13,10 +14,16 @@ logging:
|
||||
file:
|
||||
path: /usr/local/tongran/logs
|
||||
|
||||
tcp:
|
||||
netty:
|
||||
charge:
|
||||
enable: true
|
||||
name: AGENT-CLIENT-服务
|
||||
port: 6610
|
||||
readerIdleTime: 300
|
||||
netty:
|
||||
server:
|
||||
host: 120.211.95.173
|
||||
#生产环境
|
||||
port: 6620
|
||||
#测试环境
|
||||
#port: 56620
|
||||
client:
|
||||
client-id: client-001
|
||||
reconnect-interval: 5
|
||||
maxReconnectAttempts: 10 # 最大重连次数
|
||||
initialReconnectDelay: 1000 # 初始重连延迟(毫秒)
|
||||
maxReconnectDelay: 30000 # 最大重连延迟(毫秒)
|
||||
@@ -6,7 +6,15 @@ spring:
|
||||
matching-strategy: ant_path_matcher
|
||||
application:
|
||||
name: tr-agent-client
|
||||
version: 1.0
|
||||
version: 1.1.19
|
||||
conf-path: /usr/local/tongran/conf
|
||||
script-path: /usr/local/tongran/sbin
|
||||
tmp-path: /usr/local/tongran/tmp
|
||||
temp-path: /usr/local/tongran/temp
|
||||
frp-path: /usr/local/tongran/frp
|
||||
poe-path: /usr/local/tongran/sbin/pppoe1
|
||||
temp-traffic-path: /usr/local/tongran/traffictemp
|
||||
temp-pcap-path: /usr/local/tongran/pcaptemp/
|
||||
web:
|
||||
resources:
|
||||
static-locations: classpath*:/META-INF/resources/
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<!-- 日志输出的样式 -->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
|
||||
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS,Asia/Shanghai}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user