package controller import ( "bytes" "context" "crypto/rand" "encoding/hex" "fmt" "os/exec" "time" "gorm.io/gorm" ) // randomCode returns a short unique code for records that require a unique // string identifier (e.g. playbooks). It combines a unix timestamp with 4 // random hex bytes to guarantee uniqueness even under rapid sequential inserts. func randomCode(prefix string) string { b := make([]byte, 4) _, _ = rand.Read(b) return fmt.Sprintf("%s-%d-%s", prefix, time.Now().Unix(), hex.EncodeToString(b)) } // runLocal executes a script or shell command locally on the AIOps host and // returns the combined stdout+stderr output, the process exit code (0 = success) // and the wall-clock duration in milliseconds. A timeout prevents runaway jobs. func runLocal(scriptType, content string, timeoutSec int) (output string, exitCode int, durationMs int) { 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": cmd = exec.CommandContext(ctx, "python3", "-c", content) default: // shell / command / "" → run via bash cmd = exec.CommandContext(ctx, "bash", "-c", content) } var out bytes.Buffer cmd.Stdout = &out cmd.Stderr = &out err := 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 } if err != nil { if exitError, ok := err.(*exec.ExitError); ok { exitCode = exitError.ExitCode() } else { exitCode = -1 output += "\n" + err.Error() } return output, exitCode, durationMs } return output, 0, durationMs } // AutomationHandler 自动化运维处理器 type AutomationHandler struct { db *gorm.DB } // NewAutomationHandler 创建自动化运维处理器 func NewAutomationHandler(db *gorm.DB) *AutomationHandler { return &AutomationHandler{db: db} }