Files
membank/auto-check/pkg/stress/script.go
张威33321 906bb54aaa 重构 auto-check:状态数据下沉各包,workflow 精简为纯调度
- discovery: ping 探测存活 + ARP 解析 MAC,包级 Devices map,串行扫描
- stress: 脚本生成模式(BuildScript/ParseResults),ToolSet 工具检测,包级 Results map
- report: 包级 Printed map,SaveAndPrintDeviceReport 保存+打印
- sshclient: 包级 RunCommand
- workflow: 仅持有 config,Start() 固定循环,直接调用各包方法
- config: 移除 ScanConfig.Concurrency(串行扫描无需并发数)
2026-08-12 17:11:59 +08:00

157 lines
5.1 KiB
Go

package stress
import (
"fmt"
"strings"
)
// ============================
// 脚本生成(根据指标生成远程执行的 shell 脚本)
// ============================
// BuildScript 根据指标列表和配置生成 self-contained shell 脚本
// 脚本输出结构化文本,便于 ParseResults 解析
func BuildScript(metrics []Metric, cfg Config) string {
var sb strings.Builder
// 脚本头部
sb.WriteString("#!/bin/bash\n")
sb.WriteString("set -e\n\n")
// 系统信息探测
sb.WriteString("echo '===SYSTEM_INFO==='\n")
sb.WriteString("echo hostname:$(hostname)\n")
sb.WriteString("echo cpu:$(lscpu 2>/dev/null | grep 'Model name' | sed 's/Model name:\\s*//' || echo unknown)\n")
sb.WriteString("echo cores:$(nproc 2>/dev/null || echo 0)\n")
sb.WriteString("echo memory:$(free -h 2>/dev/null | awk '/Mem:/{print $2}' || echo unknown)\n")
sb.WriteString("echo kernel:$(uname -r 2>/dev/null || echo unknown)\n")
sb.WriteString("echo disk:$(lsblk -d -o NAME,SIZE 2>/dev/null | head -3 | tr '\\n' ' ')\n")
sb.WriteString("echo ''\n\n")
// 启动温度监控(后台)
hasTemp := false
for _, m := range metrics {
if m.Name == "temp" && m.Enabled {
hasTemp = true
break
}
}
if hasTemp {
tempInterval := int(cfg.TempLogInt.Seconds())
if tempInterval <= 0 {
tempInterval = 10
}
sb.WriteString(fmt.Sprintf("# 温度监控(后台)\n"))
sb.WriteString("TEMP_LOG=/tmp/auto-check-temp.log\n")
sb.WriteString("> $TEMP_LOG\n")
sb.WriteString(fmt.Sprintf("(while true; do sensors 2>/dev/null | grep -i 'temp\\|core\\|cpu' | head -5 >> $TEMP_LOG; sleep %d; done) &\n", tempInterval))
sb.WriteString("TEMP_PID=$!\n\n")
}
// 逐项执行测试
for _, m := range metrics {
if m.IsMonitor || !m.Enabled {
continue
}
sb.WriteString(fmt.Sprintf("echo '===TEST:%s==='\n", m.Name))
sb.WriteString("START_TIME=$(date +%%s%%N)\n")
sb.WriteString("set +e\n")
cmd := buildTestCommand(m, cfg)
sb.WriteString(fmt.Sprintf("OUTPUT=$(%s 2>&1)\n", cmd))
sb.WriteString("EXIT_CODE=$?\n")
sb.WriteString("set -e\n")
sb.WriteString("END_TIME=$(date +%%s%%N)\n")
sb.WriteString("DURATION=$(( (END_TIME - START_TIME) / 1000000 ))\n")
// 判定结果
sb.WriteString("if [ $EXIT_CODE -eq 0 ]; then\n")
sb.WriteString(" echo 'status:pass'\n")
sb.WriteString("else\n")
sb.WriteString(" echo 'status:fail'\n")
sb.WriteString("fi\n")
sb.WriteString("echo \"duration:${DURATION}ms\"\n")
sb.WriteString("echo \"output:${OUTPUT}\"\n")
sb.WriteString("echo ''\n\n")
}
// 收集温度监控结果
if hasTemp {
sb.WriteString("echo '===MONITOR:temp==='\n")
sb.WriteString("if [ -f $TEMP_LOG ] && [ -s $TEMP_LOG ]; then\n")
sb.WriteString(" echo 'status:pass'\n")
sb.WriteString(" TEMP_MAX=$(grep -oP '\\d+\\.?\\d*°C' $TEMP_LOG | sort -t. -k1 -n | tail -1 || echo 'unknown')\n")
sb.WriteString(" echo \"samples:$(wc -l < $TEMP_LOG)\"\n")
sb.WriteString(" echo \"max:$TEMP_MAX\"\n")
sb.WriteString(" echo \"output:$(tail -10 $TEMP_LOG)\"\n")
sb.WriteString("else\n")
sb.WriteString(" echo 'status:skip'\n")
sb.WriteString("fi\n")
sb.WriteString("echo ''\n")
sb.WriteString("kill $TEMP_PID 2>/dev/null || true\n\n")
}
// 收集 dmesg 结果
sb.WriteString("echo '===MONITOR:dmesg==='\n")
sb.WriteString("DMESG_ERR=$(dmesg --level=err,crit,alert,emerg 2>/dev/null | tail -30 || true)\n")
sb.WriteString("if [ -n \"$DMESG_ERR\" ]; then\n")
sb.WriteString(" echo 'status:fail'\n")
sb.WriteString(" echo \"output:$DMESG_ERR\"\n")
sb.WriteString("else\n")
sb.WriteString(" echo 'status:pass'\n")
sb.WriteString(" echo 'output:无硬件相关内核报错'\n")
sb.WriteString("fi\n")
sb.WriteString("echo ''\n")
sb.WriteString("echo '===END==='\n")
return sb.String()
}
// buildTestCommand 根据指标生成测试命令
func buildTestCommand(m Metric, cfg Config) string {
sec := int(cfg.Duration.Seconds())
threads := cfg.Threads
switch m.Name {
case "cpu":
return fmt.Sprintf("stress-ng --cpu %d --cpu-method all --timeout %ds --metrics-brief --temp-path /tmp", threads, sec)
case "memory":
workers := threads
if workers > 4 {
workers = 4
}
return fmt.Sprintf("stress-ng --vm %d --vm-bytes 256M --vm-method all --timeout %ds --metrics-brief", workers, sec)
case "disk":
testDir := cfg.DiskDir
if testDir == "" {
testDir = "/tmp/stress-disk-test"
}
diskMB := cfg.DiskSizeMB
if diskMB <= 0 {
diskMB = 1024
}
return fmt.Sprintf("mkdir -p %s && stress-ng --iomix 2 --iomix-bytes %dM --timeout %ds --metrics-brief && rm -rf %s",
testDir, diskMB, sec, testDir)
case "memnative":
memMB := cfg.MemSizeMB
if memMB <= 0 {
memMB = 0 // 脚本内自动计算
}
if memMB > 0 {
return fmt.Sprintf("stressapptest -s %d -M %d -f 0 -v", sec, memMB)
}
// 自动计算可用内存的 60%
return fmt.Sprintf("M=$(free -m 2>/dev/null | awk '/Mem:/{print int($7*0.6)}' || echo 512) && stressapptest -s %d -M $M -f 0 -v", sec)
case "full":
return fmt.Sprintf("stress-ng --cpu %d --vm 2 --vm-bytes 128M --iomix 1 --iomix-bytes 256M --timeout %ds --metrics-brief",
threads, sec)
default:
return "echo '未知测试类型'"
}
}