feat(verlauf): Filter, Wiederholen und Aufräumen
Verlauf filterbar (URL, API, proxdeploy history); Wiederholen füllt den Assistenten aus einem alten Auftrag; Aufräumen löscht nach Fehlschlag oder Abbruch den halb angelegten Gast – nur, wenn dessen Beschreibung auf den Auftrag verweist. Deshalb setzt das Klonen die Beschreibung jetzt sofort. Auf pve-demo geprüft (CT 109). Closes #014 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
package befehle
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"git.wilde-server.de/florian.wilde/proxdeploy/cli/internal/api"
|
||||
)
|
||||
@@ -172,3 +175,146 @@ func (u *umgebung) cancelBefehl() *cobra.Command {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Statustexte wie in der Web-GUI.
|
||||
var statusText = map[api.AuftragStatus]string{
|
||||
api.AuftragStatusWARTEND: "wartet", api.AuftragStatusLAEUFT: "läuft", api.AuftragStatusERFOLGREICH: "erfolgreich",
|
||||
api.AuftragStatusFEHLGESCHLAGEN: "fehlgeschlagen", api.AuftragStatusABGEBROCHEN: "abgebrochen",
|
||||
api.AuftragStatusAUFGERAEUMT: "aufgeräumt",
|
||||
}
|
||||
|
||||
// statusAusText nimmt „fehlgeschlagen“, „läuft“, „aufgeräumt“ oder den API-Wert.
|
||||
func statusAusText(text string) string {
|
||||
for wert, t := range statusText {
|
||||
if strings.EqualFold(t, text) || strings.EqualFold(string(wert), text) {
|
||||
return string(wert)
|
||||
}
|
||||
}
|
||||
return strings.ToUpper(text)
|
||||
}
|
||||
|
||||
func (u *umgebung) historyBefehl() *cobra.Command {
|
||||
var server, typ, distro, status string
|
||||
var seite int
|
||||
befehl := &cobra.Command{
|
||||
Use: "history",
|
||||
Aliases: []string{"verlauf"},
|
||||
Short: "Verlauf der Aufträge, neueste zuerst",
|
||||
Example: " proxdeploy history --type lxc --status FEHLGESCHLAGEN\n proxdeploy history --server rz1 --page 2",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
c, _, err := u.client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := map[string]string{"server": server, "typ": typ, "distribution": distro, "status": ""}
|
||||
if status != "" {
|
||||
filter["status"] = statusAusText(status)
|
||||
}
|
||||
if seite > 1 {
|
||||
filter["seite"] = fmt.Sprint(seite)
|
||||
}
|
||||
v, err := c.Verlauf(cmd.Context(), filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.opt.json {
|
||||
return u.jsonAusgeben(v)
|
||||
}
|
||||
s := u.stil()
|
||||
if len(v.Eintraege) == 0 {
|
||||
fmt.Fprintln(u.aus, s.Muted("Keine Aufträge."))
|
||||
return nil
|
||||
}
|
||||
symbole := map[api.AuftragStatus]string{
|
||||
api.AuftragStatusERFOLGREICH: "✓", api.AuftragStatusFEHLGESCHLAGEN: "✗", api.AuftragStatusABGEBROCHEN: "✗",
|
||||
api.AuftragStatusLAEUFT: "◐", api.AuftragStatusWARTEND: "○", api.AuftragStatusAUFGERAEUMT: "○",
|
||||
}
|
||||
zeilen := [][]string{}
|
||||
for _, a := range v.Eintraege {
|
||||
art := map[api.AuftragArt]string{api.AUSROLLEN: "VM", api.TEMPLATEBAUEN: "Template", api.AUFRAEUMEN: "Aufräumen"}[a.Art]
|
||||
if a.Art == api.AUSROLLEN && a.GastTyp != nil && *a.GastTyp == api.AuftragGastTypLXC {
|
||||
art = "LXC"
|
||||
}
|
||||
id := "–"
|
||||
if a.Vmid != nil {
|
||||
id = fmt.Sprint(*a.Vmid)
|
||||
}
|
||||
zustand := symbole[a.Status] + " " + statusText[a.Status]
|
||||
if s.ASCII {
|
||||
zustand = strings.NewReplacer("✓", "[OK]", "✗", "[!!]", "◐", "[..]", "○", "[--]").Replace(zustand)
|
||||
}
|
||||
zeit := a.ErstelltAm
|
||||
if t, ok := zeitAus(&a.ErstelltAm); ok {
|
||||
zeit = t.Local().Format("02.01. 15:04")
|
||||
}
|
||||
name := wert(a.Name)
|
||||
if name == "" {
|
||||
name = "–"
|
||||
}
|
||||
zeilen = append(zeilen, []string{a.KurzId, name, art, a.Server.Name, wert(a.DistributionId), id, zustand, zeit})
|
||||
}
|
||||
u.tabelle(s, []string{"AUFTRAG", "NAME", "TYP", "SERVER", "DISTRIBUTION", "ID", "STATUS", "ZEIT"}, zeilen)
|
||||
if v.Seiten > 1 {
|
||||
fmt.Fprintln(u.aus, s.Muted(fmt.Sprintf("Seite %d von %d · %d Aufträge · weiter mit --page %d", v.Seite, v.Seiten, v.Gesamt, min(v.Seite+1, v.Seiten))))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
f := befehl.Flags()
|
||||
f.StringVar(&server, "server", "", "nur dieser Server")
|
||||
f.StringVar(&typ, "type", "", "vm, lxc, template oder aufraeumen")
|
||||
f.StringVar(&distro, "distro", "", "nur diese Distribution")
|
||||
f.StringVar(&status, "status", "", "z. B. fehlgeschlagen, erfolgreich, laeuft")
|
||||
f.IntVar(&seite, "page", 1, "Seite (25 je Seite)")
|
||||
return befehl
|
||||
}
|
||||
|
||||
func (u *umgebung) cleanupBefehl() *cobra.Command {
|
||||
var ja, nichtWarten bool
|
||||
befehl := &cobra.Command{
|
||||
Use: "cleanup <job-id>",
|
||||
Short: "Halb angelegten Gast eines fehlgeschlagenen Auftrags löschen",
|
||||
Long: "Stoppt und löscht die VM bzw. den Container, den ein fehlgeschlagener oder abgebrochener\n" +
|
||||
"Auftrag angelegt hat. Gelöscht wird nur, was laut Beschreibung zu diesem Auftrag gehört.",
|
||||
Args: eineID("cleanup"),
|
||||
RunE: func(cmd *cobra.Command, argumente []string) error {
|
||||
c, _, err := u.client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a, err := c.Auftrag(cmd.Context(), argumente[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.Aufraeumbar == nil {
|
||||
return bedienfehler("bei %s gibt es nichts aufzuräumen (Status %s)", a.KurzId, statusText[a.Status])
|
||||
}
|
||||
z := a.Aufraeumbar
|
||||
wort := map[api.AuftragAufraeumbarTyp]string{api.AuftragAufraeumbarTypVM: "VM", api.AuftragAufraeumbarTypLXC: "Container"}[z.Typ]
|
||||
ziel := fmt.Sprintf("%s %d", wort, z.Vmid)
|
||||
if z.Knoten != nil {
|
||||
ziel += " auf " + *z.Knoten
|
||||
}
|
||||
if !ja {
|
||||
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
return bedienfehler("%s löschen? Ohne Terminal mit --yes bestätigen", ziel)
|
||||
}
|
||||
fmt.Fprintf(u.fehler, "%s stoppen und mit allen Disks löschen? [j/N] ", ziel)
|
||||
antwort, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if a := strings.ToLower(strings.TrimSpace(antwort)); a != "j" && a != "ja" && a != "y" && a != "yes" {
|
||||
fmt.Fprintln(u.fehler, u.stil().Muted("Nichts gelöscht."))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
angenommen, err := c.Aufraeumen(cmd.Context(), a.KurzId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.angenommen(cmd.Context(), c, angenommen, nichtWarten)
|
||||
},
|
||||
}
|
||||
befehl.Flags().BoolVarP(&ja, "yes", "y", false, "ohne Rückfrage")
|
||||
befehl.Flags().BoolVar(&nichtWarten, "no-wait", false, "nicht verfolgen, nur die Auftrags-ID ausgeben")
|
||||
return befehl
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user