diff --git a/pom.xml b/pom.xml index 6ba7993..71dc27a 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,19 @@ commons-net 3.11.0 + + + commons-io + commons-io + 2.14.0 + + + + org.apache.commons + commons-lang3 + 3.18.0 + + org.mybatis.spring.boot mybatis-spring-boot-starter diff --git a/src/main/java/com/mini/capi/biz/controller/DiskMountController.java b/src/main/java/com/mini/capi/biz/controller/DiskMountController.java new file mode 100644 index 0000000..bfa4518 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/controller/DiskMountController.java @@ -0,0 +1,18 @@ +package com.mini.capi.biz.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@RestController +@RequestMapping("/biz/diskMount") +public class DiskMountController { + +} diff --git a/src/main/java/com/mini/capi/biz/controller/SysHostController.java b/src/main/java/com/mini/capi/biz/controller/SysHostController.java new file mode 100644 index 0000000..c2c520d --- /dev/null +++ b/src/main/java/com/mini/capi/biz/controller/SysHostController.java @@ -0,0 +1,18 @@ +package com.mini.capi.biz.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@RestController +@RequestMapping("/biz/sysHost") +public class SysHostController { + +} diff --git a/src/main/java/com/mini/capi/biz/domain/DiskMount.java b/src/main/java/com/mini/capi/biz/domain/DiskMount.java new file mode 100644 index 0000000..005f8e2 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/domain/DiskMount.java @@ -0,0 +1,108 @@ +package com.mini.capi.biz.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@Getter +@Setter +@TableName("biz_disk_mount") +public class DiskMount implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableField("create_time") + private LocalDateTime createTime; + + @TableField("sys_host_id") + private String sysHostId; + + /** + * 设备分区 + */ + @TableField("disk_fs") + private String diskFs; + + /** + * 总容量 + */ + @TableField("size_kb") + private BigDecimal sizeKb; + + /** + * 已用容量 + */ + @TableField("used_kb") + private BigDecimal usedKb; + + /** + * 可用容量 + */ + @TableField("avail_kb") + private BigDecimal availKb; + + /** + * 挂载路径 + */ + @TableField("mount_point") + private String mountPoint; + + @TableId(value = "disk_mount_id", type = IdType.AUTO) + private String diskMountId; + + /** + * 租户id + */ + @TableField("f_tenant_id") + private String fTenantId; + + /** + * 流程id + */ + @TableField("f_flow_id") + private String fFlowId; + + /** + * 流程任务主键 + */ + @TableField("f_flow_task_id") + private String fFlowTaskId; + + /** + * 流程任务状态 + */ + @TableField("f_flow_state") + private Integer fFlowState; + + + @Override + public String toString() { + return "DiskMount{" + + "sysHostId='" + sysHostId + '\'' + + ", diskFs='" + diskFs + '\'' + + ", sizeKb=" + sizeKb + + ", usedKb=" + usedKb + + ", availKb=" + availKb + + ", mountPoint='" + mountPoint + '\'' + + '}'; + } + +} diff --git a/src/main/java/com/mini/capi/biz/domain/SysHost.java b/src/main/java/com/mini/capi/biz/domain/SysHost.java new file mode 100644 index 0000000..4654d65 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/domain/SysHost.java @@ -0,0 +1,107 @@ +package com.mini.capi.biz.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@Getter +@Setter +@TableName("biz_sys_host") +public class SysHost implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableField("create_time") + private LocalDateTime createTime; + + @TableId(value = "sys_host_id", type = IdType.AUTO) + private String sysHostId; + + /** + * 人工/系统指定的主机唯一标识 + */ + @TableField("host_tag") + private String hostTag; + + @TableField("hostname") + private String hostname; + + /** + * CPU架构 + */ + @TableField("cpu_arch") + private String cpuArch; + + /** + * CPU型号 + */ + @TableField("cpu_model") + private String cpuModel; + + /** + * CP 核心数 + */ + @TableField("cpu_cores") + private Integer cpuCores; + + /** + * CPU使用率 + */ + @TableField("cpu_usage") + private String cpuUsage; + + /** + * 租户id + */ + @TableField("f_tenant_id") + private String fTenantId; + + /** + * 流程id + */ + @TableField("f_flow_id") + private String fFlowId; + + /** + * 流程任务主键 + */ + @TableField("f_flow_task_id") + private String fFlowTaskId; + + /** + * 流程任务状态 + */ + @TableField("f_flow_state") + private Integer fFlowState; + + + @Override + public String toString() { + return "SysHost{" + + "sysHostId='" + sysHostId + '\'' + + ", hostTag='" + hostTag + '\'' + + ", hostname='" + hostname + '\'' + + ", cpuArch='" + cpuArch + '\'' + + ", cpuModel='" + cpuModel + '\'' + + ", cpuCores=" + cpuCores + + ", cpuUsage='" + cpuUsage + '\'' + + '}'; + } +} diff --git a/src/main/java/com/mini/capi/biz/mapper/DiskMountMapper.java b/src/main/java/com/mini/capi/biz/mapper/DiskMountMapper.java new file mode 100644 index 0000000..01c1d68 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/mapper/DiskMountMapper.java @@ -0,0 +1,16 @@ +package com.mini.capi.biz.mapper; + +import com.mini.capi.biz.domain.DiskMount; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +public interface DiskMountMapper extends BaseMapper { + +} diff --git a/src/main/java/com/mini/capi/biz/mapper/SysHostMapper.java b/src/main/java/com/mini/capi/biz/mapper/SysHostMapper.java new file mode 100644 index 0000000..1690f53 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/mapper/SysHostMapper.java @@ -0,0 +1,16 @@ +package com.mini.capi.biz.mapper; + +import com.mini.capi.biz.domain.SysHost; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +public interface SysHostMapper extends BaseMapper { + +} diff --git a/src/main/java/com/mini/capi/biz/service/DiskMountService.java b/src/main/java/com/mini/capi/biz/service/DiskMountService.java new file mode 100644 index 0000000..64fdf5c --- /dev/null +++ b/src/main/java/com/mini/capi/biz/service/DiskMountService.java @@ -0,0 +1,16 @@ +package com.mini.capi.biz.service; + +import com.mini.capi.biz.domain.DiskMount; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +public interface DiskMountService extends IService { + +} diff --git a/src/main/java/com/mini/capi/biz/service/SysHostService.java b/src/main/java/com/mini/capi/biz/service/SysHostService.java new file mode 100644 index 0000000..7741552 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/service/SysHostService.java @@ -0,0 +1,16 @@ +package com.mini.capi.biz.service; + +import com.mini.capi.biz.domain.SysHost; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +public interface SysHostService extends IService { + +} diff --git a/src/main/java/com/mini/capi/biz/service/impl/DiskMountServiceImpl.java b/src/main/java/com/mini/capi/biz/service/impl/DiskMountServiceImpl.java new file mode 100644 index 0000000..fa91925 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/service/impl/DiskMountServiceImpl.java @@ -0,0 +1,20 @@ +package com.mini.capi.biz.service.impl; + +import com.mini.capi.biz.domain.DiskMount; +import com.mini.capi.biz.mapper.DiskMountMapper; +import com.mini.capi.biz.service.DiskMountService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@Service +public class DiskMountServiceImpl extends ServiceImpl implements DiskMountService { + +} diff --git a/src/main/java/com/mini/capi/biz/service/impl/SysHostServiceImpl.java b/src/main/java/com/mini/capi/biz/service/impl/SysHostServiceImpl.java new file mode 100644 index 0000000..6544f59 --- /dev/null +++ b/src/main/java/com/mini/capi/biz/service/impl/SysHostServiceImpl.java @@ -0,0 +1,20 @@ +package com.mini.capi.biz.service.impl; + +import com.mini.capi.biz.domain.SysHost; +import com.mini.capi.biz.mapper.SysHostMapper; +import com.mini.capi.biz.service.SysHostService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author gaoxq + * @since 2025-08-24 + */ +@Service +public class SysHostServiceImpl extends ServiceImpl implements SysHostService { + +} diff --git a/src/main/java/com/mini/capi/mybatis/demo.java b/src/main/java/com/mini/capi/mybatis/demo.java index 6454b73..53dcc71 100644 --- a/src/main/java/com/mini/capi/mybatis/demo.java +++ b/src/main/java/com/mini/capi/mybatis/demo.java @@ -29,7 +29,7 @@ public class demo { .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper")); }) .strategyConfig(builder -> { - builder.addInclude("biz_docker_host") + builder.addInclude("biz_sys_host,biz_disk_mount") .addTablePrefix("biz_") .entityBuilder() .enableLombok() diff --git a/src/main/java/com/mini/capi/utils/HostInfo.java b/src/main/java/com/mini/capi/utils/HostInfo.java new file mode 100644 index 0000000..0428f77 --- /dev/null +++ b/src/main/java/com/mini/capi/utils/HostInfo.java @@ -0,0 +1,126 @@ +package com.mini.capi.utils; + + +import com.jcraft.jsch.*; +import com.mini.capi.biz.domain.DiskMount; +import com.mini.capi.biz.domain.SysHost; + +import java.io.*; +import java.math.BigDecimal; +import java.util.*; + +public final class HostInfo { + + + private static final String HOST_TAG = "c-api"; + + public static final class Result { + public final SysHost host; + public final List disks; + + Result(SysHost h, List d) { + this.host = h; + this.disks = d; + } + } + + /* ========== 主入口 ========== */ + public static Result collect(String hostIp, int port, String username, String password) throws Exception { + + Session session = null; + try { + session = createSession(hostIp, port, username, password); + SysHost host = collectHost(session); + List disks = collectDisk(session); + disks.forEach(d -> d.setSysHostId(host.getSysHostId())); + return new Result(host, disks); + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + } + + /* ========== SSH 工具 ========== */ + private static Session createSession(String ip, int port, String user, String pwd) throws JSchException { + JSch jsch = new JSch(); + Session session = jsch.getSession(user, ip, port); + session.setPassword(pwd); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(10_000); + return session; + } + + private static String exec(Session session, String cmd) throws Exception { + ChannelExec ch = (ChannelExec) session.openChannel("exec"); + ch.setCommand(cmd); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream err = new ByteArrayOutputStream(); + ch.setOutputStream(out); + ch.setErrStream(err); + ch.connect(); + waitForExit(ch); + ch.disconnect(); + String errStr = err.toString().trim(); + if (!errStr.isEmpty()) { + throw new RuntimeException("Remote cmd error: " + errStr); + } + return out.toString().trim(); + } + + private static void waitForExit(ChannelExec ch) throws Exception { + long start = System.currentTimeMillis(); + while (!ch.isClosed()) { + if (System.currentTimeMillis() - start > 30_000) { + throw new RuntimeException("Command timeout"); + } + } + } + + /* ========== 采集主机信息 ========== */ + private static SysHost collectHost(Session session) throws Exception { + SysHost host = new SysHost(); + String id = vId.getUid(); + host.setSysHostId(id); + host.setHostTag(HOST_TAG); + host.setHostname(exec(session, "hostname -s")); + host.setCpuArch(exec(session, "uname -m")); + // CPU 型号:取 model name 第一行 + String cpuInfo = exec(session, "cat /proc/cpuinfo | grep 'model name' | head -1"); + host.setCpuModel(cpuInfo.contains(":") ? cpuInfo.split(":", 2)[1].trim() : null); + // 核心数 + String cores = exec(session, "nproc --all"); + host.setCpuCores(parseInt(cores)); + // CPU 使用率:top -bn1 取 idle 字段 + String top = exec(session, "top -bn1 | grep 'Cpu(s)'"); + host.setCpuUsage(top); + return host; + } + + /* ========== 采集磁盘信息 ========== */ + private static List collectDisk(Session session) throws Exception { + String df = exec(session, "df -k -P | tail -n +2"); // 去掉标题行 + List list = new ArrayList<>(); + for (String line : df.split("\n")) { + String[] arr = line.trim().split("\\s+"); + if (arr.length < 6) continue; // 跳过异常行 + DiskMount d = new DiskMount(); + d.setDiskFs(arr[0]); + d.setSizeKb(new BigDecimal(arr[1])); + d.setUsedKb(new BigDecimal(arr[2])); + d.setAvailKb(new BigDecimal(arr[3])); + d.setMountPoint(arr[5]); + list.add(d); + } + return list; + } + + /* ========== 工具方法 ========== */ + private static Integer parseInt(String s) { + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/mini/capi/utils/docker.java b/src/main/java/com/mini/capi/utils/docker.java index 3ba56ef..1c1f79c 100644 --- a/src/main/java/com/mini/capi/utils/docker.java +++ b/src/main/java/com/mini/capi/utils/docker.java @@ -54,7 +54,6 @@ public class docker { ChannelExec channel = (ChannelExec) session.openChannel("exec"); channel.setCommand(command); channel.setInputStream(null); - InputStream in = channel.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); diff --git a/src/main/java/com/mini/capi/utils/vId.java b/src/main/java/com/mini/capi/utils/vId.java new file mode 100644 index 0000000..1874b7f --- /dev/null +++ b/src/main/java/com/mini/capi/utils/vId.java @@ -0,0 +1,27 @@ +package com.mini.capi.utils; + +import java.security.SecureRandom; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class vId { + + private static final SecureRandom RAND = new SecureRandom(); + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); + + private vId() {} + + + /** + * + * 时间戳唯一编号 + */ + public static String getUid() { + // 17 位时间 + String tm = LocalDateTime.now().format(DF); + // 25 位随机数字(高位补零) + long rand = Math.abs(RAND.nextLong()) % (long) Math.pow(10, 15); + return tm + String.format("%015d", rand); + } + +} diff --git a/src/main/resources/mapper/DiskMountMapper.xml b/src/main/resources/mapper/DiskMountMapper.xml new file mode 100644 index 0000000..b7803ae --- /dev/null +++ b/src/main/resources/mapper/DiskMountMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + create_time, sys_host_id, disk_fs, size_kb, used_kb, avail_kb, mount_point, disk_mount_id, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state + + + diff --git a/src/main/resources/mapper/SysHostMapper.xml b/src/main/resources/mapper/SysHostMapper.xml new file mode 100644 index 0000000..ee298e5 --- /dev/null +++ b/src/main/resources/mapper/SysHostMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + create_time, sys_host_id, host_tag, hostname, cpu_arch, cpu_model, cpu_cores, cpu_usage, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state + + +