diff --git a/README.md b/README.md index af873b6..c05d02f 100644 --- a/README.md +++ b/README.md @@ -681,11 +681,20 @@ Hier bleibt alles erhalten, was die Maschine ausmacht: SMBIOS-UUID, `vmgenid`, MAC-Adressen, bei Containern der Hostname. Für alles im Netz ist sie dieselbe Maschine — inklusive Lizenzbindungen, AD-Mitgliedschaft und DHCP-Reservierungen. -**Genau deshalb ist sie gefährlich.** `pvesnap-recovery` prüft vorher, ob das -Original läuft, und warnt unmissverständlich. Zwei Maschinen mit derselben MAC -und derselben IP im selben Netz geben Chaos. Das Original also **vorher -stoppen** — oder mit `--net down` starten und die Karte erst zuschalten, wenn -klar ist, dass die Luft rein ist. +**Genau deshalb ist sie gefährlich.** Zwei Maschinen mit derselben MAC und +derselben IP im selben Netz geben Chaos. `pvesnap-recovery` prüft deshalb +vorher, ob das Original läuft, und lässt sich in dem Fall nicht beiläufig +durchwinken: + +| Situation | Verhalten | +|---|---| +| ncurses-Oberfläche | Zusammenfassung nur mit `j`, danach eine zweite Rückfrage, die den Grund beim Namen nennt | +| `recover …` im Terminal | das Wort **`ja`** muss ausgeschrieben werden — `j` reicht nicht | +| `recover … -y` | **wird abgewiesen** (Exit-Code 2). `-y` überspringt Routinefragen, nicht diese | +| `recover … --force` | läuft durch — die bewusste Entscheidung | + +Das Original also **vorher stoppen** — oder mit `--net down` starten und die +Karte erst zuschalten, wenn klar ist, dass die Luft rein ist. ### Parameter @@ -705,7 +714,8 @@ klar ist, dass die Luft rein ist. | `--name ` | Name der neuen Maschine | | `--keep-binds` | durchgereichte Host-Verzeichnisse des Originals übernehmen | | `--no-start` | nur einrichten, nicht starten | -| `-y`, `--yes` | nicht nachfragen | +| `-y`, `--yes` | Routinefragen überspringen (deckt `--force` **nicht** ab) | +| `--force` | auch anlegen, wenn das Original noch läuft | | Befehl | Bedeutung | |---|---| diff --git a/pvesnap/recovery.py b/pvesnap/recovery.py index e42f566..ee941c1 100644 --- a/pvesnap/recovery.py +++ b/pvesnap/recovery.py @@ -307,6 +307,9 @@ class Plan: exchange_kind: str = "" # "disk" | "bind" | "" exchange_detail: str = "" warnings: list = field(default_factory=list) + # Was nicht nur unschoen, sondern gefaehrlich ist. Hierfuer genuegt ein + # beilaeufiges "ja" nicht - siehe cmd_create() und die Oberflaeche. + critical: list = field(default_factory=list) notes: list = field(default_factory=list) dropped: list = field(default_factory=list) @@ -559,7 +562,7 @@ def plan(proxmox, guest, snapname, spec): "MAC-Adressen wie das Original.") status = proxmox.guest_status(guest) if status.get("status") == "running": - result.warnings.append( + result.critical.append( "%s LAEUFT gerade. Zwei Maschinen mit gleicher MAC-Adresse, " "gleicher IP und gleicher Identitaet im selben Netz geben Chaos - " "erst das Original stoppen." % guest.label) diff --git a/pvesnap/recovery_ui.py b/pvesnap/recovery_ui.py index 79aacf1..8f8315b 100644 --- a/pvesnap/recovery_ui.py +++ b/pvesnap/recovery_ui.py @@ -338,11 +338,10 @@ def summary_lines(plan_): for note in plan_.notes: for line in _wrap("- " + note, 74): lines.append((line, curses.color_pair(C_DIM))) - if plan_.warnings: + for entry in plan_.warnings + plan_.critical: lines.append(("", 0)) - for warning in plan_.warnings: - for line in _wrap("!! " + warning, 74): - lines.append((line, curses.color_pair(C_WARN) | curses.A_BOLD)) + for line in _wrap("!! " + entry, 74): + lines.append((line, curses.color_pair(C_WARN) | curses.A_BOLD)) return lines @@ -396,7 +395,13 @@ def _new(stdscr, proxmox): "j = anlegen | beliebige andere Taste = zurueck") if key not in ("j", "J", "y", "Y"): continue - if plan_.warnings and not confirm( + if plan_.critical: + # Beim Namen nennen, worum es geht - "Warnungen gelesen?" + # klickt man weg, ohne hinzusehen. + if not confirm(stdscr, truncate(plan_.critical[0], 60) + + " Trotzdem?"): + continue + elif plan_.warnings and not confirm( stdscr, "Warnungen gelesen und trotzdem anlegen?"): continue @@ -694,6 +699,8 @@ def _print_summary(plan_): print(" Hinweis : %s" % note) for warning in plan_.warnings: print(" ACHTUNG : %s" % warning) + for reason in plan_.critical: + print(" GEFAHR : %s" % reason) def cmd_create(proxmox, args, mode): @@ -704,7 +711,26 @@ def cmd_create(proxmox, args, mode): print("Vorhaben:") _print_summary(plan_) - if not args.yes: + + # Gefaehrliches laesst sich nicht mit -y wegwinken. -y heisst "keine + # Routinefragen", nicht "frag mich auch dann nicht, wenn es weh tut". + if plan_.critical and not args.force: + # Sonst ueberholt die Fehlerausgabe die gepufferte Zusammenfassung, + # sobald jemand die Ausgabe umleitet - und die Begruendung stuende + # ueber dem, worauf sie sich bezieht. + sys.stdout.flush() + print("\nDas geht so nicht:", file=sys.stderr) + for reason in plan_.critical: + print(" - %s" % reason, file=sys.stderr) + if args.yes: + print("\n-y reicht dafuer nicht. Entweder das Original stoppen oder\n" + "bewusst --force setzen.", file=sys.stderr) + return 2 + print("\nWer das trotzdem will, tippt jetzt das Wort ja aus.") + if input("Trotzdem anlegen? [ja/nein] ").strip().lower() not in ("ja", "yes"): + print("Abgebrochen.") + return 1 + elif not args.yes: if plan_.warnings: print("\nEs gibt Warnungen - bitte oben lesen.") answer = input("\nAnlegen? [j/N] ").strip().lower() @@ -834,7 +860,11 @@ def _add_create_options(parser): "uebernehmen (Vorgabe: weglassen)") parser.add_argument("--no-start", action="store_true", help="nur einrichten, nicht starten") - parser.add_argument("-y", "--yes", action="store_true", help="nicht nachfragen") + parser.add_argument("-y", "--yes", action="store_true", + help="Routinefragen ueberspringen (deckt --force nicht ab)") + parser.add_argument("--force", action="store_true", + help="auch dann anlegen, wenn das Original noch laeuft - " + "gleiche MAC und IP zweimal im Netz") def build_parser():