Das eingebaute Austauschlaufwerk faellt weg
Es tat dasselbe wie die Transfer-Laufwerke, nur schlechter: namenlos, nicht vorher befuellbar, nicht wiederverwendbar, und es starb mit der Maschine. Zwei Mechanismen fuer dieselbe Sache sind schlechter als einer. Entfernt: --exchange, --exchange-storage, --exchange-fs, --exchange-dir, die Unterbefehle pull und release, das Formatieren und Einhaengen auf dem Host samt Mount-Merkliste. Was davon gebraucht wurde, steckt jetzt in transfer.py - dort aber benannt, dauerhaft und im laufenden Betrieb wechselbar. Aeltere Eintraege in der Merkliste stoeren nicht: ihr Austauschlaufwerk ist ein gewoehnliches Volume der Maschine und wird beim Verwerfen ohnehin von Proxmox mitgeloescht. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0a92ec0984
commit
f2de07ecfd
+20
-175
@@ -5,10 +5,10 @@ Ohne Argumente kommt die ncurses-Oberflaeche, alles geht aber auch direkt:
|
||||
pvesnap-recovery Uebersicht und Assistent
|
||||
pvesnap-recovery list was gerade laeuft
|
||||
pvesnap-recovery live 802 neuesten Snapshot ohne Netz starten
|
||||
pvesnap-recovery live 802 <snapshot> --exchange 20G
|
||||
pvesnap-recovery live 802 <snapshot> --transfer dumps
|
||||
pvesnap-recovery recover 802 <snapshot> mit Netz, gleiche Identitaet
|
||||
pvesnap-recovery console 9802 noVNC-Adresse anzeigen
|
||||
pvesnap-recovery pull 9802 Austauschlaufwerk auf den Host holen
|
||||
pvesnap-recovery Taste v: Transfer-Laufwerke
|
||||
pvesnap-recovery destroy 9802 restlos verwerfen
|
||||
"""
|
||||
|
||||
@@ -26,11 +26,10 @@ from .curses_util import (C_DIM, C_FOOTER, C_HEADER, C_MARK, C_OK, C_SEL, C_WARN
|
||||
choose, confirm, fill, init_colors, is_enter, is_escape,
|
||||
keybar, message, prompt, put, read_key)
|
||||
from .proxmox import Proxmox, ProxmoxError, pvesh_available
|
||||
from .recovery import (EXCHANGE_GUEST_PATH, RecoveryError, Spec, create,
|
||||
from .recovery import (RecoveryError, Spec, create,
|
||||
eject_transfer, insert_transfer, transfer_state,
|
||||
destroy, find_orphans, flatten, flatten_cost, flatten_hint,
|
||||
human_bytes,
|
||||
linked_volumes, list_instances, plan, pull, release,
|
||||
human_bytes, linked_volumes, list_instances, plan,
|
||||
remove_orphans, start)
|
||||
from . import transfer, transfer_ui
|
||||
from .snapfs import list_snapshots
|
||||
@@ -43,7 +42,6 @@ KEYS = [
|
||||
("n", "Neu", "Neu"),
|
||||
("s", "Starten", "Start"),
|
||||
("h", "Herunterfahren", "Stop"),
|
||||
("a", "Austausch", "Austau."),
|
||||
("x", "Verwerfen", "Verwerf."),
|
||||
("v", "Laufwerke", "Laufw."),
|
||||
("r", "Aktualisieren", "Aktual."),
|
||||
@@ -186,12 +184,8 @@ def _field_text(spec, key, kind, choices):
|
||||
return str(value) if value else "automatisch (naechste freie)"
|
||||
if key == "transfers":
|
||||
return transfer_ui.summary(value)
|
||||
if key == "exchange":
|
||||
return value or "keins"
|
||||
if key == "node":
|
||||
return value or "wie das Original"
|
||||
if key == "exchange_dir":
|
||||
return value or "(eigenes anlegen)"
|
||||
return str(value or "")
|
||||
|
||||
|
||||
@@ -280,10 +274,8 @@ def _edit(win, spec, key, kind, choices, back=False):
|
||||
spec.net = ""
|
||||
return
|
||||
|
||||
label = {"exchange": "Groesse (z.B. 20G, leer = keins)",
|
||||
"newid": "Neue VMID (leer = automatisch)",
|
||||
"node": "Node (leer = wie das Original)",
|
||||
"exchange_dir": "Host-Verzeichnis (leer = selbst anlegen)"}.get(key, key)
|
||||
label = {"newid": "Neue VMID (leer = automatisch)",
|
||||
"node": "Node (leer = wie das Original)"}.get(key, key)
|
||||
answer = prompt(win, label, str(getattr(spec, key) or ""))
|
||||
if answer is None:
|
||||
return
|
||||
@@ -329,8 +321,6 @@ def summary_lines(plan_):
|
||||
% (human_bytes(plan_.vmstate_bytes) or "?")
|
||||
if plan_.resume else "vorhanden, wird aber nicht geladen"),
|
||||
curses.color_pair(C_OK) if plan_.resume else 0))
|
||||
if plan_.exchange_kind:
|
||||
lines.append(("Austausch: %s" % plan_.exchange_detail, 0))
|
||||
for volume in plan_.transfers:
|
||||
lines.append(("Transfer: %-14s %8s im Gast als %s"
|
||||
% (volume.name, human_bytes(volume.size), volume.label),
|
||||
@@ -380,7 +370,7 @@ def _new(stdscr, proxmox):
|
||||
if snapname is None:
|
||||
continue
|
||||
|
||||
spec = Spec(exchange_dir="")
|
||||
spec = Spec()
|
||||
while True:
|
||||
if not _options(stdscr, guest, spec):
|
||||
break
|
||||
@@ -460,17 +450,6 @@ def _details(stdscr, proxmox, instance):
|
||||
lines.append((" oder auf dem Host: pct enter %d" % instance.vmid,
|
||||
curses.color_pair(C_MARK)))
|
||||
|
||||
exchange = instance.exchange or {}
|
||||
if exchange:
|
||||
lines.append(("", 0))
|
||||
if exchange.get("kind") == "bind":
|
||||
lines.append(("Austausch: %s" % exchange.get("path"), 0))
|
||||
lines.append((" im Gast unter %s" % EXCHANGE_GUEST_PATH, 0))
|
||||
else:
|
||||
lines.append(("Austausch: %s (%s), im Gast als weitere Platte"
|
||||
% (exchange.get("volid"), exchange.get("fs")), 0))
|
||||
lines.append((" nach dem Herunterfahren mit 'a' "
|
||||
"auf den Host holen", curses.color_pair(C_DIM)))
|
||||
if instance.resumed:
|
||||
lines.append(("", 0))
|
||||
lines.append(("Der Arbeitsspeicher des Snapshots wurde geladen - beim "
|
||||
@@ -512,7 +491,7 @@ def _details(stdscr, proxmox, instance):
|
||||
lines.append(("Datentraeger: eigenstaendig - haengt an keinem Snapshot",
|
||||
curses.color_pair(C_OK)))
|
||||
|
||||
footer = ("s Start | h Stop | e Auswerfen | a Austausch | f Loesen | "
|
||||
footer = ("s Start | h Stop | e Auswerfen | f Loesen | "
|
||||
"x Verwerfen | q zurueck")
|
||||
key = _pages(stdscr, lines, "Wiederherstellung %s" % instance.label, footer)
|
||||
|
||||
@@ -524,8 +503,6 @@ def _details(stdscr, proxmox, instance):
|
||||
elif key in ("h", "H"):
|
||||
_do(stdscr, "Fahre %s herunter ..." % instance.label,
|
||||
lambda: proxmox.shutdown_guest(instance.guest))
|
||||
elif key in ("a", "A"):
|
||||
_pull(stdscr, proxmox, instance)
|
||||
elif key in ("e", "E"):
|
||||
_swap(stdscr, proxmox, instance, steckplaetze)
|
||||
elif key in ("f", "F"):
|
||||
@@ -546,55 +523,6 @@ def _do(stdscr, text, action):
|
||||
message(stdscr, hint, error=True)
|
||||
|
||||
|
||||
def _pull(stdscr, proxmox, instance):
|
||||
_wait_screen(stdscr, "Hole das Austauschlaufwerk auf den Host ...")
|
||||
try:
|
||||
path = pull(proxmox, instance)
|
||||
except (RecoveryError, ProxmoxError, OSError) as exc:
|
||||
message(stdscr, str(exc), error=True)
|
||||
return
|
||||
_pages(stdscr, _plain([
|
||||
"Das Austauschlaufwerk liegt jetzt hier:",
|
||||
"",
|
||||
" " + path,
|
||||
"",
|
||||
"Von dort kopieren, zum Beispiel mit:",
|
||||
" pvesnap-explorer --path %s" % path,
|
||||
" scp -r %s/. ziel:/pfad/" % path,
|
||||
"",
|
||||
"Wieder loesen mit: pvesnap-recovery release %d" % instance.vmid,
|
||||
]), "Austauschlaufwerk")
|
||||
|
||||
|
||||
def _swap(stdscr, proxmox, instance, steckplaetze):
|
||||
"""Transfer-Laufwerk im laufenden Betrieb abziehen oder wieder einklinken."""
|
||||
wechselbar = [(e, drin) for e, drin in steckplaetze if e.get("kind") != "bind"]
|
||||
if not wechselbar:
|
||||
message(stdscr, "Hier gibt es nichts zu wechseln.", error=True)
|
||||
return
|
||||
|
||||
if len(wechselbar) == 1:
|
||||
entry, drin = wechselbar[0]
|
||||
else:
|
||||
gewaehlt = choose(stdscr, "Welches Laufwerk?",
|
||||
[((e, d), "%-14s %s" % (e["name"],
|
||||
"eingesteckt" if d else "ausgeworfen"))
|
||||
for e, d in wechselbar])
|
||||
if gewaehlt is None:
|
||||
return
|
||||
entry, drin = gewaehlt
|
||||
|
||||
name = entry["name"]
|
||||
if drin:
|
||||
if not confirm(stdscr, "%r auswerfen? Im Gast vorher aushaengen!" % name):
|
||||
return
|
||||
_do(stdscr, "Werfe %r aus ..." % name,
|
||||
lambda: eject_transfer(proxmox, instance, name))
|
||||
else:
|
||||
_do(stdscr, "Klinke %r ein ..." % name,
|
||||
lambda: insert_transfer(proxmox, instance, name))
|
||||
|
||||
|
||||
def _flatten(stdscr, proxmox, instance, linked):
|
||||
if not linked:
|
||||
message(stdscr, "%s haengt an keinem Snapshot mehr." % instance.label)
|
||||
@@ -721,8 +649,6 @@ def _overview(stdscr, proxmox):
|
||||
_do(stdscr, "Fahre %s herunter ..." % current.label,
|
||||
lambda: proxmox.shutdown_guest(current.guest))
|
||||
reload_ = True
|
||||
elif key in ("a", "A"):
|
||||
_pull(stdscr, proxmox, current)
|
||||
elif key in ("x", "X"):
|
||||
_destroy(stdscr, proxmox, current)
|
||||
reload_ = True
|
||||
@@ -786,18 +712,12 @@ def _spec_from_args(args, mode):
|
||||
spec.node = args.node or ""
|
||||
spec.net = args.net or ""
|
||||
spec.resume = args.resume
|
||||
spec.exchange = args.exchange or ""
|
||||
spec.exchange_storage = args.exchange_storage or ""
|
||||
spec.exchange_fs = "exfat"
|
||||
spec.transfers = list(args.transfer or [])
|
||||
spec.exchange_dir = args.exchange_dir or ""
|
||||
spec.memory = args.memory or 0
|
||||
spec.cores = args.cores or 0
|
||||
spec.name = args.name or ""
|
||||
spec.keep_binds = args.keep_binds
|
||||
spec.start = not args.no_start
|
||||
if spec.exchange_dir and not spec.exchange:
|
||||
spec.exchange = "bind"
|
||||
return spec
|
||||
|
||||
|
||||
@@ -814,8 +734,9 @@ def _print_summary(plan_):
|
||||
if plan_.resume:
|
||||
print(" Arbeitsspei.: wird geladen (%s)"
|
||||
% (human_bytes(plan_.vmstate_bytes) or "?"))
|
||||
if plan_.exchange_kind:
|
||||
print(" Austausch : %s" % plan_.exchange_detail)
|
||||
for volume in plan_.transfers:
|
||||
print(" Transfer : %-14s %8s im Gast als %s"
|
||||
% (volume.name, human_bytes(volume.size), volume.label))
|
||||
for note in plan_.notes:
|
||||
print(" Hinweis : %s" % note)
|
||||
for warning in plan_.warnings:
|
||||
@@ -887,15 +808,6 @@ def _print_access(instance):
|
||||
print(" %s" % instance.console_url())
|
||||
if instance.type == "lxc":
|
||||
print(" oder auf dem Host: pct enter %d" % instance.vmid)
|
||||
exchange = instance.exchange or {}
|
||||
if exchange.get("kind") == "bind":
|
||||
print("\n Austausch: %s (im Gast unter %s)"
|
||||
% (exchange.get("path"), EXCHANGE_GUEST_PATH))
|
||||
elif exchange.get("volid"):
|
||||
print("\n Austausch: als weitere Platte im Gast (%s, Bezeichnung PVESNAP)"
|
||||
% exchange.get("fs"))
|
||||
print(" im Gast einhaengen, befuellen, Gast herunterfahren, dann:")
|
||||
print(" pvesnap-recovery pull %d" % instance.vmid)
|
||||
print("\n Wieder verwerfen: pvesnap-recovery destroy %d" % instance.vmid)
|
||||
|
||||
|
||||
@@ -925,50 +837,6 @@ def cmd_destroy(proxmox, args):
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_pull(proxmox, args):
|
||||
instance = _find_instance(proxmox, args.vmid)
|
||||
path = pull(proxmox, instance)
|
||||
print(path)
|
||||
if not args.quiet:
|
||||
print("\nDas Austauschlaufwerk liegt jetzt auf dem Host.", file=sys.stderr)
|
||||
print(" pvesnap-explorer --path %s" % path, file=sys.stderr)
|
||||
print(" Wieder loesen: pvesnap-recovery release %d" % instance.vmid,
|
||||
file=sys.stderr)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_flatten(proxmox, args):
|
||||
instance = _find_instance(proxmox, args.vmid)
|
||||
linked = linked_volumes(proxmox, instance)
|
||||
if not linked:
|
||||
print("%s haengt an keinem Snapshot mehr - nichts zu tun."
|
||||
% instance.label)
|
||||
return 0
|
||||
|
||||
print("%s loest sich von %s." % (instance.label, instance.origin))
|
||||
for volid in linked:
|
||||
print(" %s" % volid)
|
||||
cost = flatten_cost(proxmox, instance)
|
||||
print("\nDabei werden die Daten wirklich kopiert%s."
|
||||
% ((" - rund %s" % human_bytes(cost)) if cost else ""))
|
||||
if not args.yes:
|
||||
if _ask("Jetzt loesen? [j/N] ") not in ("j", "y", "ja", "yes"):
|
||||
print("Abgebrochen.")
|
||||
return 1
|
||||
|
||||
done, already, other = flatten(proxmox, instance, stream=True, force=args.force,
|
||||
progress=lambda text: print(" " + text))
|
||||
print("\n%d Datentraeger geloest." % len(done))
|
||||
for volid in already:
|
||||
print(" war schon eigenstaendig: %s" % volid)
|
||||
for volid in other:
|
||||
print(" %s: %s" % (volid, flatten_hint(proxmox, volid)))
|
||||
if not linked_volumes(proxmox, instance):
|
||||
print("\n%s ist jetzt eigenstaendig. Die Snapshots des Originals lassen "
|
||||
"sich wieder loeschen." % instance.label)
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_cleanup(proxmox, args):
|
||||
orphans = find_orphans(proxmox)
|
||||
if not orphans:
|
||||
@@ -1009,11 +877,6 @@ def cmd_cleanup(proxmox, args):
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
def cmd_release(_proxmox, args):
|
||||
print("geloest." if release(args.vmid) else "War nicht eingehaengt.")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_console(proxmox, args):
|
||||
instance = _find_instance(proxmox, args.vmid)
|
||||
print(instance.console_url())
|
||||
@@ -1048,17 +911,9 @@ def _add_create_options(parser):
|
||||
help="Arbeitsspeicher des Snapshots laden")
|
||||
parser.add_argument("--no-resume", dest="resume", action="store_false",
|
||||
help="kalt starten, auch wenn der Snapshot RAM enthaelt")
|
||||
parser.add_argument("--exchange", metavar="GROESSE",
|
||||
help="Austauschlaufwerk anlegen, z.B. 20G "
|
||||
"(bei Containern genuegt --exchange ja)")
|
||||
parser.add_argument("--exchange-storage", metavar="STORAGE",
|
||||
help="Storage dafuer (Vorgabe: wie die erste Platte)")
|
||||
parser.add_argument("--transfer", action="append", metavar="NAME", default=[],
|
||||
help="vorbereitetes Transfer-Laufwerk anhaengen "
|
||||
"(mehrfach moeglich)")
|
||||
parser.add_argument("--exchange-dir", metavar="PFAD",
|
||||
help="nur Container: dieses Host-Verzeichnis durchreichen "
|
||||
"(auch ein eingehaengtes Netzlaufwerk)")
|
||||
parser.add_argument("--memory", type=int, help="Arbeitsspeicher in MB abweichend")
|
||||
parser.add_argument("--cores", type=int, help="Kerne abweichend")
|
||||
parser.add_argument("--name", help="Name der neuen Maschine")
|
||||
@@ -1083,16 +938,16 @@ def build_parser():
|
||||
pvesnap-recovery
|
||||
ncurses-Oberflaeche mit Uebersicht und Assistent
|
||||
|
||||
pvesnap-recovery live 802 --exchange 20G
|
||||
neuesten Snapshot von VM 802 ohne Netzwerk starten, mit einem 20-GB-
|
||||
Austauschlaufwerk fuer den Datenbank-Dump
|
||||
pvesnap-recovery live 802 --transfer dumps
|
||||
neuesten Snapshot von VM 802 ohne Netzwerk starten, mit dem
|
||||
vorbereiteten Transfer-Laufwerk "dumps"
|
||||
|
||||
pvesnap-recovery live 100 --exchange ja
|
||||
Container 100 abgeschottet starten; ein Host-Verzeichnis erscheint
|
||||
im Container unter %s
|
||||
pvesnap-recovery live 100 --transfer dumps --transfer werkzeuge
|
||||
Container 100 abgeschottet starten, mit zwei Laufwerken
|
||||
|
||||
pvesnap-recovery live 100 --exchange-dir /mnt/nfs/dumps
|
||||
dasselbe, aber mit einem eigenen (auch eingehaengten) Verzeichnis
|
||||
pvesnap-recovery
|
||||
Taste v: Transfer-Laufwerke anlegen und befuellen (Commander)
|
||||
Taste e: im laufenden Betrieb auswerfen und wieder einklinken
|
||||
|
||||
pvesnap-recovery recover 802 autosnap-... --newid 9802
|
||||
mit Netzwerk und gleicher Identitaet - nur wenn das Original steht!
|
||||
@@ -1102,7 +957,7 @@ def build_parser():
|
||||
|
||||
pvesnap-recovery destroy 9802
|
||||
restlos verwerfen: Maschine weg, Klone weg, Schutz der Snapshots geloest
|
||||
""" % EXCHANGE_GUEST_PATH)
|
||||
""")
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
parser.add_argument("-V", "--version", action="version",
|
||||
version="pvesnap-recovery %s" % __version__)
|
||||
@@ -1123,14 +978,6 @@ def build_parser():
|
||||
sub = subs.add_parser(name, help=helptext)
|
||||
sub.add_argument("vmid", type=int)
|
||||
|
||||
pull_ = subs.add_parser("pull", help="Austauschlaufwerk auf den Host holen")
|
||||
pull_.add_argument("vmid", type=int)
|
||||
pull_.add_argument("-q", "--quiet", action="store_true",
|
||||
help="nur den Pfad ausgeben")
|
||||
|
||||
release_ = subs.add_parser("release", help="Austauschlaufwerk wieder loesen")
|
||||
release_.add_argument("vmid", type=int)
|
||||
|
||||
flatten_ = subs.add_parser(
|
||||
"flatten", help="vom Quell-Snapshot loesen - fuer den Dauerbetrieb")
|
||||
flatten_.add_argument("vmid", type=int)
|
||||
@@ -1170,8 +1017,6 @@ def main(argv=None):
|
||||
"recover": lambda: cmd_create(proxmox, args, "recovery"),
|
||||
"list": lambda: cmd_list(proxmox, args),
|
||||
"destroy": lambda: cmd_destroy(proxmox, args),
|
||||
"pull": lambda: cmd_pull(proxmox, args),
|
||||
"release": lambda: cmd_release(proxmox, args),
|
||||
"flatten": lambda: cmd_flatten(proxmox, args),
|
||||
"cleanup": lambda: cmd_cleanup(proxmox, args),
|
||||
"console": lambda: cmd_console(proxmox, args),
|
||||
|
||||
Reference in New Issue
Block a user