From 23c8f2cd6671c3483b18ea143e41baf646154cf6 Mon Sep 17 00:00:00 2001 From: cynfo3000 Date: Sat, 28 Feb 2026 09:35:33 +0100 Subject: [PATCH] Major-Update Ablauf korrigiert und getestet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent: - major_update: opnsense-update -r -bkp + pkg-static update -f + pkg-static upgrade -y - Reboot ist bei Major-Updates default true (immer noetig) - update_check: reboot_required Flag hinzugefuegt - update: reboot_required korrekt aus opnsense-update -c Exit-Code - Alle Steps liefern strukturierte Ergebnisse fuer Frontend-Workflow Getestet auf 192.168.85.19: - Normales Update: 25.7 → 25.7.11_9 (93 Pakete) erfolgreich - Major-Upgrade: 25.7.11_9 → 26.1.2_5 erfolgreich (Base+Kernel+Packages+Reboot) README: Update-Ablauf dokumentiert (Normal vs Major) --- README.md | 25 +++++++++-- agent/ws/handler.go | 106 ++++++++++++++++++++++++++++---------------- 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index d258b72..3ad387c 100644 --- a/README.md +++ b/README.md @@ -234,20 +234,37 @@ curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \ https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/reboot ``` +### Update-Ablauf + +**Normales Update** (Patches innerhalb der gleichen Version): +1. `opnsense-update` — Core-Update herunterladen +2. `pkg upgrade -y` — Paket-Updates installieren +3. Nachpruefung: `opnsense-update -c` — Exit 1 = Reboot noetig + +Response enthaelt `reboot_required: true/false` fuer das Frontend. + +**Major-Upgrade** (z.B. 25.7 → 26.1): +1. `opnsense-update -r -bkp` — Repository wechseln + Base + Kernel + Packages herunterladen +2. `pkg-static update -f` — Repository-Katalog forciert aktualisieren +3. `pkg-static upgrade -y` — Alle Pakete auf neue Version installieren +4. **Reboot erforderlich** (immer bei Major-Upgrade) + +Major-Upgrades setzen `reboot_required: true` und `reboot` ist standardmaessig aktiviert. + ### WebSocket Commands (Backend → Agent) ```json // Remote-Befehl ausfuehren {"type":"command","id":"cmd1","command":"exec","params":{"command":"uptime","timeout":30}} -// Update-Check +// Update-Check (liefert core_update_available, pending_packages, reboot_required) {"type":"command","id":"cmd2","command":"update_check"} -// Normales Update +// Normales Update (reboot optional) {"type":"command","id":"cmd3","command":"update","params":{"reboot":false}} -// Major-Update -{"type":"command","id":"cmd4","command":"major_update","params":{"version":"26.7","reboot":true}} +// Major-Update (reboot default true) +{"type":"command","id":"cmd4","command":"major_update","params":{"version":"26.1","reboot":true}} // Reboot {"type":"command","id":"cmd5","command":"reboot","params":{"delay":5}} diff --git a/agent/ws/handler.go b/agent/ws/handler.go index a5dc6f9..bb83ed1 100644 --- a/agent/ws/handler.go +++ b/agent/ws/handler.go @@ -102,14 +102,42 @@ func (h *Handler) handleUpdateCheck(msg Message) Message { // 1. OPNsense Core Update pruefen coreOutput, coreErr := h.runCommand("/usr/local/sbin/opnsense-update -c", 60) if coreErr != nil { - // Exit 1 = Update verfuegbar + // Exit 1 = Update verfuegbar ODER Reboot noetig + info := strings.TrimSpace(coreOutput) result["core_update_available"] = true - result["core_update_info"] = strings.TrimSpace(coreOutput) + result["core_update_info"] = info } else { result["core_update_available"] = false result["core_update_info"] = "Kein Core-Update verfuegbar" } + // Reboot-Check: Base/Kernel Version != installierte Version? + baseVer, _ := h.runCommand("cat /usr/local/opnsense/version/base", 5) + kernelVer, _ := h.runCommand("cat /usr/local/opnsense/version/kernel", 5) + coreJson, _ := h.runCommand("cat /usr/local/opnsense/version/core", 5) + baseVer = strings.TrimSpace(baseVer) + kernelVer = strings.TrimSpace(kernelVer) + + rebootRequired := false + // Pruefen ob CORE_SERIES sich von Base/Kernel unterscheidet + if strings.Contains(coreJson, "CORE_NEXT") && baseVer != "" && kernelVer != "" { + // Einfacher Check: wenn base/kernel neuer als core → reboot noetig + coreVer := "" + for _, line := range strings.Split(coreJson, "\n") { + if strings.Contains(line, "CORE_PKGVERSION") { + parts := strings.SplitN(line, ":", 2) + if len(parts) == 2 { + coreVer = strings.Trim(strings.TrimSpace(parts[1]), "\",") + } + } + } + if coreVer != "" && baseVer != "" && !strings.HasPrefix(baseVer, strings.Split(coreVer, ".")[0]+"."+strings.Split(coreVer, ".")[1]) { + // Base-Version Major.Minor unterscheidet sich von Core → Reboot + rebootRequired = true + } + } + result["reboot_required"] = rebootRequired + // 2. Paket-Updates pruefen pkgOutput, _ := h.runCommand("/usr/sbin/pkg upgrade -n", 60) pendingPackages := h.parsePkgUpgrade(pkgOutput) @@ -168,23 +196,28 @@ func (h *Handler) handleUpdate(msg Message) Message { // Schritt 3: opnsense-update -c (Nachpruefung) log.Println("Update Schritt 3: Nachpruefung") checkOutput, checkErr := h.runCommand("/usr/local/sbin/opnsense-update -c", 60) - stillPending := checkErr != nil + rebootRequired := checkErr != nil // Exit 1 nach Update = Reboot noetig step3 := map[string]interface{}{ "step": "verify", "output": checkOutput, - "updates_pending": stillPending, + "reboot_required": rebootRequired, } steps = append(steps, step3) result["steps"] = steps + result["reboot_required"] = rebootRequired result["reboot_requested"] = reboot // Gesamtstatus if coreErr == nil && pkgErr == nil { response.Status = "ok" - result["message"] = "Update erfolgreich abgeschlossen" + if rebootRequired { + result["message"] = "Update erfolgreich — Reboot erforderlich" + } else { + result["message"] = "Update erfolgreich abgeschlossen" + } } else { - response.Status = "ok" // Nicht "error" — teilweise Updates sind OK + response.Status = "ok" result["message"] = "Update mit Warnungen abgeschlossen" } @@ -210,17 +243,18 @@ func (h *Handler) handleUpdate(msg Message) Message { } // handleMajorUpdate fuehrt ein Major-Upgrade durch +// Ablauf: opnsense-update -r -bkp → pkg-static update -f → pkg-static upgrade -y → Reboot func (h *Handler) handleMajorUpdate(msg Message) Message { response := Message{Type: "response", ID: msg.ID} targetVersion, ok := msg.Params["version"].(string) if !ok || targetVersion == "" { response.Status = "error" - response.Error = "Parameter 'version' erforderlich (z.B. '26.7')" + response.Error = "Parameter 'version' erforderlich (z.B. '26.1')" return response } - reboot := false + reboot := true // Major-Update braucht immer Reboot, default an if r, ok := msg.Params["reboot"].(bool); ok { reboot = r } @@ -231,63 +265,59 @@ func (h *Handler) handleMajorUpdate(msg Message) Message { "target_version": targetVersion, } var steps []map[string]interface{} + hasError := false - // Schritt 1: opnsense-update -r (Release wechseln) - log.Printf("Major-Update Schritt 1: opnsense-update -r %s", targetVersion) - releaseCmd := fmt.Sprintf("/usr/local/sbin/opnsense-update -r %s", targetVersion) - releaseOutput, releaseErr := h.runCommand(releaseCmd, 300) + // Schritt 1: opnsense-update -r -bkp (Release wechseln + Base + Kernel + Packages) + log.Printf("Schritt 1: opnsense-update -r %s -bkp", targetVersion) + releaseCmd := fmt.Sprintf("/usr/local/sbin/opnsense-update -r %s -bkp", targetVersion) + releaseOutput, releaseErr := h.runCommand(releaseCmd, 900) step1 := map[string]interface{}{ - "step": fmt.Sprintf("opnsense-update -r %s", targetVersion), + "step": fmt.Sprintf("opnsense-update -r %s -bkp", targetVersion), "output": releaseOutput, "ok": releaseErr == nil, } if releaseErr != nil { step1["error"] = releaseErr.Error() - steps = append(steps, step1) - - // Bei Fehler im Release-Wechsel abbrechen - result["steps"] = steps - result["message"] = "Release-Wechsel fehlgeschlagen" - response.Status = "error" - response.Error = "Release-Wechsel fehlgeschlagen" - response.Data = result - return response + hasError = true } steps = append(steps, step1) - // Schritt 2: opnsense-update (eigentliches Update laden) - log.Println("Major-Update Schritt 2: opnsense-update") - updateOutput, updateErr := h.runCommand("/usr/local/sbin/opnsense-update", 900) + // Schritt 2: pkg-static update -f (Repository-Katalog auf neuen Release aktualisieren) + log.Println("Schritt 2: pkg-static update -f") + catalogOutput, catalogErr := h.runCommand("/usr/local/sbin/pkg-static update -f", 120) step2 := map[string]interface{}{ - "step": "opnsense-update", - "output": updateOutput, - "ok": updateErr == nil, + "step": "pkg-static update -f", + "output": catalogOutput, + "ok": catalogErr == nil, } - if updateErr != nil { - step2["error"] = updateErr.Error() + if catalogErr != nil { + step2["error"] = catalogErr.Error() + hasError = true } steps = append(steps, step2) - // Schritt 3: pkg upgrade -y - log.Println("Major-Update Schritt 3: pkg upgrade -y") - pkgOutput, pkgErr := h.runCommand("/usr/sbin/pkg upgrade -y", 600) + // Schritt 3: pkg-static upgrade -y (alle Pakete auf neue Version) + log.Println("Schritt 3: pkg-static upgrade -y") + pkgOutput, pkgErr := h.runCommand("/usr/local/sbin/pkg-static upgrade -y", 900) step3 := map[string]interface{}{ - "step": "pkg upgrade", + "step": "pkg-static upgrade -y", "output": pkgOutput, "ok": pkgErr == nil, } if pkgErr != nil { step3["error"] = pkgErr.Error() + hasError = true } steps = append(steps, step3) result["steps"] = steps + result["reboot_required"] = true result["reboot_requested"] = reboot - if updateErr == nil { - result["message"] = fmt.Sprintf("Major-Update auf %s abgeschlossen", targetVersion) + if !hasError { + result["message"] = fmt.Sprintf("Major-Update auf %s abgeschlossen — Reboot erforderlich", targetVersion) } else { - result["message"] = fmt.Sprintf("Major-Update auf %s mit Warnungen", targetVersion) + result["message"] = fmt.Sprintf("Major-Update auf %s mit Warnungen — Reboot erforderlich", targetVersion) } response.Status = "ok" @@ -307,7 +337,7 @@ func (h *Handler) handleMajorUpdate(msg Message) Message { } response.Data = result - log.Printf("Major-Update auf %s abgeschlossen", targetVersion) + log.Printf("Major-Update auf %s abgeschlossen (Reboot noch ausstehend)", targetVersion) return response }