- pkg/discovery: TCP端口探测存活主机,ARP表解析MAC作为设备唯一标识 - pkg/sshclient: SSH连接(密码/密钥认证)与远程命令执行 - pkg/stress: stress-ng/stressapptest 压测(cpu/memory/disk/memnative/full)+温度与dmesg监控 - pkg/report: 检测报告生成(文本/JSON) - pkg/workflow: 永久循环工作流(间隔可配),MAC唯一标识设备,增量测试+状态变化打印 - pkg/config: YAML分类配置(scan/ssh/stress/report/workflow) - cmd: cobra入口,仅 --config 指定配置文件
169 lines
5.9 KiB
Go
169 lines
5.9 KiB
Go
package stress
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// truncate 截断过长输出
|
|
func truncate(s string, maxLen int) string {
|
|
s = strings.TrimSpace(s)
|
|
if len(s) > maxLen {
|
|
return s[:maxLen] + "\n ... (输出已截断)"
|
|
}
|
|
return s
|
|
}
|
|
|
|
// ============================
|
|
// stress-ng 压测
|
|
// ============================
|
|
|
|
// CPUStress stress-ng CPU 压力测试
|
|
func CPUStress(client *ssh.Client, ex Executor, cfg Config) Result {
|
|
start := time.Now()
|
|
fmt.Printf(" [CPU] stress-ng CPU 压力 (%d线程, %v)...\n", cfg.Threads, cfg.Duration)
|
|
|
|
info, err := EnsureTool(client, ex, "stress-ng", "apt install stress-ng / opkg install stress-ng")
|
|
if err != nil {
|
|
return Result{Type: TestCPU, Status: "skip", Error: err.Error(), Duration: time.Since(start)}
|
|
}
|
|
fmt.Printf(" [CPU] 使用 %s (%s)\n", info.Path, info.Version)
|
|
|
|
cmd := fmt.Sprintf("%s --cpu %d --cpu-method all --timeout %v --metrics-brief --temp-path /tmp 2>&1",
|
|
info.Path, cfg.Threads, cfg.Duration)
|
|
out, err := exec(client, ex, cmd)
|
|
duration := time.Since(start)
|
|
|
|
if err != nil {
|
|
return Result{Type: TestCPU, Status: "error", Output: truncate(out, 600), Error: err.Error(), Duration: duration}
|
|
}
|
|
return Result{Type: TestCPU, Status: "pass", Output: truncate(out, 600), Duration: duration}
|
|
}
|
|
|
|
// MemoryStress stress-ng 内存压力测试
|
|
func MemoryStress(client *ssh.Client, ex Executor, cfg Config) Result {
|
|
start := time.Now()
|
|
fmt.Printf(" [Memory] stress-ng 内存压力 (%d线程, %v)...\n", cfg.Threads, cfg.Duration)
|
|
|
|
info, err := EnsureTool(client, ex, "stress-ng", "apt install stress-ng / opkg install stress-ng")
|
|
if err != nil {
|
|
return Result{Type: TestMemory, Status: "skip", Error: err.Error(), Duration: time.Since(start)}
|
|
}
|
|
|
|
memWorkers := cfg.Threads
|
|
if memWorkers > 4 {
|
|
memWorkers = 4
|
|
}
|
|
cmd := fmt.Sprintf("%s --vm %d --vm-bytes 256M --vm-method all --timeout %v --metrics-brief 2>&1",
|
|
info.Path, memWorkers, cfg.Duration)
|
|
out, err := exec(client, ex, cmd)
|
|
duration := time.Since(start)
|
|
|
|
if err != nil {
|
|
return Result{Type: TestMemory, Status: "error", Output: truncate(out, 600), Error: err.Error(), Duration: duration}
|
|
}
|
|
return Result{Type: TestMemory, Status: "pass", Output: truncate(out, 600), Duration: duration}
|
|
}
|
|
|
|
// DiskIOStress stress-ng 磁盘IO压力测试
|
|
func DiskIOStress(client *ssh.Client, ex Executor, cfg Config) Result {
|
|
start := time.Now()
|
|
fmt.Printf(" [DiskIO] stress-ng 磁盘IO压力 (%v)...\n", cfg.Duration)
|
|
|
|
info, err := EnsureTool(client, ex, "stress-ng", "apt install stress-ng / opkg install stress-ng")
|
|
if err != nil {
|
|
return Result{Type: TestDiskIO, Status: "skip", Error: err.Error(), Duration: time.Since(start)}
|
|
}
|
|
|
|
testDir := cfg.DiskDir
|
|
if testDir == "" {
|
|
testDir = "/tmp/stress-disk-test"
|
|
}
|
|
_, _ = exec(client, ex, fmt.Sprintf("mkdir -p %s", testDir))
|
|
|
|
cmd := fmt.Sprintf("%s --iomix 2 --iomix-bytes %dM --timeout %v --metrics-brief 2>&1",
|
|
info.Path, cfg.DiskSizeMB, cfg.Duration)
|
|
out, err := exec(client, ex, cmd)
|
|
duration := time.Since(start)
|
|
|
|
_, _ = exec(client, ex, fmt.Sprintf("rm -rf %s", testDir))
|
|
|
|
if err != nil {
|
|
return Result{Type: TestDiskIO, Status: "error", Output: truncate(out, 600), Error: err.Error(), Duration: duration}
|
|
}
|
|
return Result{Type: TestDiskIO, Status: "pass", Output: truncate(out, 600), Duration: duration}
|
|
}
|
|
|
|
// ============================
|
|
// stressapptest 内存精压
|
|
// ============================
|
|
|
|
// MemNativeStress stressapptest 内存稳定性精压
|
|
func MemNativeStress(client *ssh.Client, ex Executor, cfg Config) Result {
|
|
start := time.Now()
|
|
fmt.Printf(" [MemNative] stressapptest 内存精压 (%v)...\n", cfg.Duration)
|
|
|
|
info, err := EnsureTool(client, ex, "stressapptest", "apt install stressapptest / opkg install stressapptest")
|
|
if err != nil {
|
|
return Result{Type: TestMemNative, Status: "skip", Error: err.Error(), Duration: time.Since(start)}
|
|
}
|
|
fmt.Printf(" [MemNative] 使用 %s\n", info.Path)
|
|
|
|
memSize := cfg.MemSizeMB
|
|
if memSize <= 0 {
|
|
out, _ := exec(client, ex, "free -m 2>/dev/null | awk '/Mem:/{print int($7*0.6)}'")
|
|
out = strings.TrimSpace(out)
|
|
if out != "" {
|
|
fmt.Sscanf(out, "%d", &memSize)
|
|
}
|
|
if memSize <= 0 {
|
|
memSize = 512
|
|
}
|
|
}
|
|
|
|
cmd := fmt.Sprintf("%s -s %d -M %d -f 0 -v 2>&1",
|
|
info.Path, int(cfg.Duration.Seconds()), memSize)
|
|
out, err := exec(client, ex, cmd)
|
|
duration := time.Since(start)
|
|
|
|
output := truncate(out, 800)
|
|
if strings.Contains(strings.ToUpper(output), "PASS") {
|
|
return Result{Type: TestMemNative, Status: "pass", Output: output, Duration: duration}
|
|
}
|
|
if strings.Contains(strings.ToUpper(output), "FAIL") {
|
|
return Result{Type: TestMemNative, Status: "fail", Output: output, Duration: duration}
|
|
}
|
|
if err != nil {
|
|
return Result{Type: TestMemNative, Status: "error", Output: output, Error: err.Error(), Duration: duration}
|
|
}
|
|
return Result{Type: TestMemNative, Status: "pass", Output: output, Duration: duration}
|
|
}
|
|
|
|
// ============================
|
|
// 综合压测
|
|
// ============================
|
|
|
|
// FullStress 三合一综合压测 (CPU + 内存 + 磁盘IO 同时进行)
|
|
func FullStress(client *ssh.Client, ex Executor, cfg Config) Result {
|
|
start := time.Now()
|
|
fmt.Printf(" [Full] 三合一综合压测 (CPU %d线程 + 内存 + 磁盘IO, %v)...\n", cfg.Threads, cfg.Duration)
|
|
|
|
info, err := EnsureTool(client, ex, "stress-ng", "apt install stress-ng / opkg install stress-ng")
|
|
if err != nil {
|
|
return Result{Type: TestFull, Status: "skip", Error: err.Error(), Duration: time.Since(start)}
|
|
}
|
|
|
|
cmd := fmt.Sprintf("%s --cpu %d --vm 2 --vm-bytes 128M --iomix 1 --iomix-bytes 256M --timeout %v --metrics-brief 2>&1",
|
|
info.Path, cfg.Threads, cfg.Duration)
|
|
out, err := exec(client, ex, cmd)
|
|
duration := time.Since(start)
|
|
|
|
if err != nil {
|
|
return Result{Type: TestFull, Status: "error", Output: truncate(out, 600), Error: err.Error(), Duration: duration}
|
|
}
|
|
return Result{Type: TestFull, Status: "pass", Output: truncate(out, 600), Duration: duration}
|
|
}
|