- discovery: ping 探测存活 + ARP 解析 MAC,包级 Devices map,串行扫描 - stress: 脚本生成模式(BuildScript/ParseResults),ToolSet 工具检测,包级 Results map - report: 包级 Printed map,SaveAndPrintDeviceReport 保存+打印 - sshclient: 包级 RunCommand - workflow: 仅持有 config,Start() 固定循环,直接调用各包方法 - config: 移除 ScanConfig.Concurrency(串行扫描无需并发数)
157 lines
5.1 KiB
Go
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 '未知测试类型'"
|
|
}
|
|
}
|