Transfer-Laufwerke: nur noch exFAT, Oberflaeche und Anbindung
Ein einziges Dateisystem, mit Absicht: exFAT koennen Windows und Linux beide
von Haus aus, es kennt keine Besitzrechte auf dem Datentraeger und hat anders
als FAT32 keine 4-GB-Grenze je Datei. Die Auswahl faellt damit weg - eine
Entscheidung weniger, wenn es schnell gehen muss.
Neu in der Oberflaeche:
Taste v Uebersicht der Laufwerke mit Zustand
Enter darauf haengt am Host ein und startet den Commander - links das
Laufwerk, rechts der eigene Rechner
n / u / l anlegen, aushaengen, loeschen
Optionsmaske Mehrfachauswahl statt Groessenangabe
--transfer NAME dasselbe auf der Kommandozeile, mehrfach moeglich
Am laufenden System durchgespielt: Laufwerk angelegt, vom Host ein Skript
hineingelegt, Wiederherstellung damit gestartet, im Gast gelesen, ausgefuehrt
und geschrieben, Wiederherstellung verworfen - das Laufwerk lebte samt
Ergebnis weiter.
Zwei Rechtefallen dabei gefunden und geschlossen:
Der Gast konnte lesen, aber nicht schreiben. exFAT hat keine Besitzrechte
auf der Platte, die entstehen beim Einhaengen - mit der Vorgabe gehoert
alles root, und der root eines unprivilegierten Containers ist auf dem Host
uid 100000. Wird jetzt mit umask=0000 eingehaengt.
Ein durchgereichtes Laufwerk galt als "am Host eingehaengt", weil beim
Container genau das der Fall ist. Wer das sah, haette es aushaengen und dem
laufenden Gast den Boden wegziehen koennen. Die Belegung wird jetzt auch aus
Container-Konfigurationen gelesen, der Gast hat Vorrang in der Anzeige, und
Aushaengen wird abgelehnt, solange jemand darauf arbeitet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6279a20d4a
commit
2b895e6f4f
+42
-20
@@ -26,11 +26,12 @@ 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, MKFS, RecoveryError, Spec, create,
|
||||
from .recovery import (EXCHANGE_GUEST_PATH, RecoveryError, Spec, create,
|
||||
destroy, find_orphans, flatten, flatten_cost, flatten_hint,
|
||||
human_bytes,
|
||||
linked_volumes, list_instances, plan, pull, release,
|
||||
remove_orphans, start)
|
||||
from . import transfer, transfer_ui
|
||||
from .snapfs import list_snapshots
|
||||
from .util import truncate
|
||||
|
||||
@@ -43,6 +44,7 @@ KEYS = [
|
||||
("h", "Herunterfahren", "Stop"),
|
||||
("a", "Austausch", "Austau."),
|
||||
("x", "Verwerfen", "Verwerf."),
|
||||
("v", "Laufwerke", "Laufw."),
|
||||
("r", "Aktualisieren", "Aktual."),
|
||||
("q", "Beenden", "Ende"),
|
||||
]
|
||||
@@ -163,14 +165,7 @@ def _fields(guest):
|
||||
]
|
||||
if guest.type == "qemu":
|
||||
rows.append(("resume", "Arbeitsspeicher", "choice", RESUME_CHOICES))
|
||||
rows.append(("exchange", "Austauschlaufwerk", "text", ()))
|
||||
rows.append(("exchange_fs", " Dateisystem", "choice",
|
||||
tuple((f, f) for f in sorted(MKFS))))
|
||||
else:
|
||||
rows.append(("exchange", "Austauschverzeichnis", "choice",
|
||||
(("", "keins"), ("bind", "ja, unter %s im Gast"
|
||||
% EXCHANGE_GUEST_PATH))))
|
||||
rows.append(("exchange_dir", " eigenes Host-Verzeichnis", "text", ()))
|
||||
rows.append(("transfers", "Transfer-Laufwerke", "transfers", ()))
|
||||
rows += [
|
||||
("newid", "Neue VMID", "text", ()),
|
||||
("node", "Node", "text", ()),
|
||||
@@ -188,6 +183,8 @@ def _field_text(spec, key, kind, choices):
|
||||
return str(value)
|
||||
if key == "newid":
|
||||
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":
|
||||
@@ -220,12 +217,8 @@ def _options(win, guest, spec):
|
||||
"das Austauschlaufwerk da.",
|
||||
"resume": "Snapshots mit Arbeitsspeicher laufen genau dort weiter, wo "
|
||||
"sie standen - ohne Crash-Recovery.",
|
||||
"exchange": ("Groesse wie 20G. Leer = keins."
|
||||
if guest.type == "qemu" else
|
||||
"Ein Host-Verzeichnis, das im Container auftaucht."),
|
||||
"exchange_fs": "Dateisystem des Austauschlaufwerks.",
|
||||
"exchange_dir": "Leer = pvesnap legt selbst eines an. Hier passt auch "
|
||||
"ein eingehaengtes Netzlaufwerk.",
|
||||
"transfers": "Vorbereitete Austauschmedien. Anlegen und befuellen "
|
||||
"in der Uebersicht mit v .",
|
||||
"newid": "VMID der neuen Maschine.",
|
||||
"node": "Auf welchem Node sie laufen soll.",
|
||||
"start": "Sofort hochfahren.",
|
||||
@@ -252,8 +245,11 @@ def _options(win, guest, spec):
|
||||
elif is_escape(key) or key == "q":
|
||||
return False
|
||||
elif is_enter(key) or key == " " or key in (curses.KEY_RIGHT, curses.KEY_LEFT):
|
||||
_edit(win, spec, current_key, kind, choices,
|
||||
back=key == curses.KEY_LEFT)
|
||||
if kind == "transfers":
|
||||
spec.transfers = transfer_ui.choose(win, spec.transfers)
|
||||
else:
|
||||
_edit(win, spec, current_key, kind, choices,
|
||||
back=key == curses.KEY_LEFT)
|
||||
|
||||
|
||||
def _wrap(text, width):
|
||||
@@ -334,6 +330,10 @@ def summary_lines(plan_):
|
||||
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),
|
||||
curses.color_pair(C_OK)))
|
||||
|
||||
if plan_.notes:
|
||||
lines.append(("", 0))
|
||||
@@ -649,6 +649,9 @@ def _overview(stdscr, proxmox):
|
||||
if key in ("n", "N"):
|
||||
_new(stdscr, proxmox)
|
||||
reload_ = True
|
||||
elif key in ("v", "V"):
|
||||
transfer_ui.screen(stdscr, browse=_browse_transfer)
|
||||
reload_ = True
|
||||
elif key in ("r", "R"):
|
||||
reload_ = True
|
||||
elif key == curses.KEY_UP and instances:
|
||||
@@ -675,6 +678,23 @@ def _overview(stdscr, proxmox):
|
||||
reload_ = True
|
||||
|
||||
|
||||
def _browse_transfer(win, path, volume):
|
||||
"""Den Zwei-Fenster-Commander auf ein Transfer-Laufwerk loslassen.
|
||||
|
||||
Links das Laufwerk, rechts der lokale Rechner - also genau der Weg, um
|
||||
Werkzeuge hineinzukopieren, bevor die Maschine startet, und den Dump
|
||||
hinterher wieder herauszuholen.
|
||||
"""
|
||||
from .explorer import Explorer, Pane
|
||||
links = Pane(path=path, root=path, readonly=False,
|
||||
title="Transfer: %s (%s)" % (volume.name, volume.label))
|
||||
rechts = Pane(path=os.getcwd(), root="/", readonly=False,
|
||||
title="Lokaler Rechner")
|
||||
Explorer(links, rechts,
|
||||
subtitle="Transfer-Laufwerk %s - beide Seiten beschreibbar"
|
||||
% volume.name).run(win)
|
||||
|
||||
|
||||
def _main(stdscr, _args):
|
||||
curses.curs_set(0)
|
||||
if hasattr(curses, "set_escdelay"):
|
||||
@@ -718,7 +738,8 @@ def _spec_from_args(args, mode):
|
||||
spec.resume = args.resume
|
||||
spec.exchange = args.exchange or ""
|
||||
spec.exchange_storage = args.exchange_storage or ""
|
||||
spec.exchange_fs = args.exchange_fs
|
||||
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
|
||||
@@ -982,8 +1003,9 @@ def _add_create_options(parser):
|
||||
"(bei Containern genuegt --exchange ja)")
|
||||
parser.add_argument("--exchange-storage", metavar="STORAGE",
|
||||
help="Storage dafuer (Vorgabe: wie die erste Platte)")
|
||||
parser.add_argument("--exchange-fs", default="ext4", choices=sorted(MKFS),
|
||||
help="Dateisystem des Austauschlaufwerks (Vorgabe: ext4)")
|
||||
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)")
|
||||
|
||||
Reference in New Issue
Block a user