Files
aiops/backend/pkg/scriptrunner/runtime.go
T
LoganandClaude 471353e3d7 feat: 监控告警体系统一重构 + 自动化任务调度引擎 + 监控大屏拆分
告警中心
- 新增统一告警(alert_unified)、监控告警规则(monitor_alert_rule)、指标目录(alert_metric_catalog)
- 通知策略与消息分发(alert_notify_policy/dispatch/message)、静默规则
- 告警引擎按阈值/SNMP 拆分(alert_engine_monitor/snmp)

监控采集
- SNMP 常量/模板与历史指标(snmp templates/constants、snmp_metrics_history、snmp_collect)
- 中间件监控 Redis 采集与种子(middleware_monitor_redis/seed)
- 站点探测 pkg(siteprobe) + 站点视图(site_view)

自动化任务
- cron 调度器(cronscheduler)、脚本运行器(scriptrunner)、系统任务(systask)及运行记录(cron_job_run、system_task)

审计与凭证
- 审计设置(audit_setting) + 历史清理(auditcleaner)

前端
- 监控页面(host/middleware/site/snmp)拆分为子模块并大屏化
- 告警中心多 Tab(规则/指标目录/通知策略/静默)、任务管理组件重构、Agent CLI Markdown 渲染
- 移除旧脚本卡片/详情/日志组件,统一为 ScriptTable/ScriptCodeEditor/TerminalOutput

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-01 23:44:45 +08:00

159 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 脚本运行时探测与本地执行 — 适配 ./aiops 单二进制 / Docker 容器环境 // Logan.Li
package scriptrunner
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"runtime"
"time"
)
// RuntimeInfo 当前主机可用的脚本解释器
type RuntimeInfo struct {
Shell bool `json:"shell"`
ShellPath string `json:"shell_path,omitempty"`
Python bool `json:"python"`
PythonPath string `json:"python_path,omitempty"`
PowerShell bool `json:"powershell"`
PowerShellPath string `json:"powershell_path,omitempty"`
OS string `json:"os"`
Arch string `json:"arch"`
EmbedMode bool `json:"embed_mode"` // 单二进制 embed 部署
}
// DetectRuntime 探测 bash/sh、python3、powershell 是否可用 // Logan.Li
func DetectRuntime() RuntimeInfo {
info := RuntimeInfo{
OS: runtime.GOOS,
Arch: runtime.GOARCH,
EmbedMode: true,
}
for _, p := range []string{"/bin/bash", "/usr/bin/bash", "bash"} {
if path, ok := lookExecutable(p); ok {
info.Shell = true
info.ShellPath = path
break
}
}
if !info.Shell {
for _, p := range []string{"/bin/sh", "/usr/bin/sh", "sh"} {
if path, ok := lookExecutable(p); ok {
info.Shell = true
info.ShellPath = path
break
}
}
}
for _, p := range []string{"/usr/bin/python3", "/usr/local/bin/python3", "python3"} {
if path, ok := lookExecutable(p); ok {
info.Python = true
info.PythonPath = path
break
}
}
for _, p := range []string{"/usr/bin/pwsh", "/usr/local/bin/pwsh", "pwsh", "powershell"} {
if path, ok := lookExecutable(p); ok {
info.PowerShell = true
info.PowerShellPath = path
break
}
}
return info
}
func lookExecutable(name string) (string, bool) {
path, err := exec.LookPath(name)
if err != nil {
return "", false
}
return path, true
}
// CanRun 判断指定脚本类型是否可在本机执行 // Logan.Li
func CanRun(rt RuntimeInfo, scriptType string) (bool, string) {
switch scriptType {
case "shell", "bash", "command", "":
if rt.Shell {
return true, ""
}
return false, "当前环境未找到 bash/sh,无法执行 Shell 脚本(Docker 镜像需安装 bash)"
case "python":
if rt.Python {
return true, ""
}
return false, "当前环境未找到 python3,无法执行 Python 脚本(请在镜像中安装 python3)"
case "powershell":
if rt.PowerShell {
return true, ""
}
return false, "当前环境未找到 PowerShell (pwsh),无法执行 PowerShell 脚本"
default:
return false, fmt.Sprintf("不支持的脚本类型: %s", scriptType)
}
}
// Run 在 AIOps 本机执行脚本,返回合并输出、退出码、耗时(ms) // Logan.Li
func Run(scriptType, content string, timeoutSec int) (output string, exitCode int, durationMs int, err error) {
rt := DetectRuntime()
ok, reason := CanRun(rt, scriptType)
if !ok {
return reason + "\n", -1, 0, fmt.Errorf("%s", reason)
}
start := time.Now()
timeout := time.Duration(timeoutSec) * time.Second
if timeout <= 0 {
timeout = 300 * time.Second
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
var cmd *exec.Cmd
switch scriptType {
case "python":
py := rt.PythonPath
if py == "" {
py = "python3"
}
cmd = exec.CommandContext(ctx, py, "-c", content)
case "powershell":
pwsh := rt.PowerShellPath
if pwsh == "" {
pwsh = "pwsh"
}
cmd = exec.CommandContext(ctx, pwsh, "-NoProfile", "-NonInteractive", "-Command", content)
default:
sh := rt.ShellPath
if sh == "" {
sh = "bash"
}
cmd = exec.CommandContext(ctx, sh, "-c", content)
}
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
cmd.Env = os.Environ()
runErr := cmd.Run()
durationMs = int(time.Since(start).Milliseconds())
output = out.String()
if ctx.Err() == context.DeadlineExceeded {
output += fmt.Sprintf("\n[超时] 执行超过 %d 秒被终止", timeoutSec)
return output, -1, durationMs, ctx.Err()
}
if runErr != nil {
if exitError, ok := runErr.(*exec.ExitError); ok {
exitCode = exitError.ExitCode()
} else {
exitCode = -1
output += "\n" + runErr.Error()
}
return output, exitCode, durationMs, runErr
}
return output, 0, durationMs, nil
}