Explorer: aus dem Dateibrowser zurueck in die Auswahl statt beenden

Bisher endete das Programm, sobald man den Dateibrowser verliess - wollte
man einen zweiten Snapshot ansehen, musste man es neu starten und sich
wieder durch Gast und Snapshot klicken.

Jetzt gibt es drei Ebenen, aus denen q oder F10 jeweils eine zurueckfuehrt:

  Gastauswahl -> Snapshot-Auswahl -> Dateibrowser

Beim Verlassen des Browsers wird der Snapshot ausgehaengt, bevor die
Snapshot-Liste wieder erscheint - dort steht dann auch, was in der
Zwischenzeit dazugekommen ist. Aus der Snapshot-Liste geht es zurueck zur
Gastauswahl, deren Inventar dabei ebenfalls neu gelesen wird.

Das rechte Fenster behaelt sein Verzeichnis ueber mehrere Snapshots
hinweg, damit man aus verschiedenen Snapshots ins selbe Ziel sammeln kann.

Auf pvetest01 gegen echte Snapshots geprueft: beim Wechsel wird der erste
Snapshot restlos abgebaut, bevor der zweite eingebunden wird (nie mehr als
eine Einbindung gleichzeitig), und der regulaere Ausstieg hinterlaesst
weder rbd-Maps noch Mountpunkte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-07-31 10:02:26 +02:00
co-authored by Claude Opus 5
parent 700492391a
commit 91bfdea76c
2 changed files with 84 additions and 37 deletions
+7 -1
View File
@@ -373,7 +373,13 @@ Links der Snapshot, rechts der lokale Rechner:
| `F7` / `n` | neues Verzeichnis (nur lokal) | | `F7` / `n` | neues Verzeichnis (nur lokal) |
| `F6` / `g` | Verzeichnis direkt eingeben | | `F6` / `g` | Verzeichnis direkt eingeben |
| `F2` / `m` | anderes Dateisystem des Snapshots wählen | | `F2` / `m` | anderes Dateisystem des Snapshots wählen |
| `F10` / `q` | beenden | | `F10` / `q` | eine Ebene zurück (Snapshot wird ausgehängt) |
Der Explorer hat drei Ebenen: **Gastauswahl → Snapshot-Auswahl → Dateien**. Mit
`q` geht es jeweils eine Ebene zurück; beim Verlassen des Dateibrowsers wird der
Snapshot wieder ausgehängt, sodass man gleich den nächsten öffnen kann. Das
rechte Fenster behält dabei sein Verzeichnis — praktisch, wenn man aus mehreren
Snapshots ins selbe Zielverzeichnis sammelt.
Kopiert wird immer vom aktiven ins andere Fenster — in den Snapshot hinein geht Kopiert wird immer vom aktiven ins andere Fenster — in den Snapshot hinein geht
nichts, er ist schreibgeschützt. Beim Kopieren gibt es einen Fortschrittsbalken, nichts, er ist schreibgeschützt. Beim Kopieren gibt es einen Fortschrittsbalken,
+77 -36
View File
@@ -3,6 +3,10 @@
Zwei Fenster wie im Midnight Commander: links der geoeffnete Snapshot Zwei Fenster wie im Midnight Commander: links der geoeffnete Snapshot
(schreibgeschuetzt), rechts der lokale Rechner. Markieren, F5 - fertig. (schreibgeschuetzt), rechts der lokale Rechner. Markieren, F5 - fertig.
Drei Ebenen, aus denen man mit q jeweils eine zurueckkommt:
Gastauswahl -> Snapshot-Auswahl -> Dateibrowser. Beim Verlassen des
Browsers wird der Snapshot wieder ausgehaengt.
pvesnap-explorer Gast und Snapshot auswaehlen pvesnap-explorer Gast und Snapshot auswaehlen
pvesnap-explorer 802 Snapshot von VM 802 auswaehlen pvesnap-explorer 802 Snapshot von VM 802 auswaehlen
pvesnap-explorer 802 <snapname> direkt oeffnen pvesnap-explorer 802 <snapname> direkt oeffnen
@@ -43,7 +47,7 @@ KEYS = [
("F3", "Ansehen", "Ansehen"), ("F3", "Ansehen", "Ansehen"),
("F7", "Neuer Ordner", "Ordner"), ("F7", "Neuer Ordner", "Ordner"),
("F2", "Laufwerk", "Laufw."), ("F2", "Laufwerk", "Laufw."),
("F10", "Ende", "Ende"), ("F10", "Zurueck", "Zurueck"),
] ]
COPY_BUFFER = 1024 * 1024 COPY_BUFFER = 1024 * 1024
@@ -542,7 +546,7 @@ class Explorer:
elif key in (curses.KEY_F10, "q", "Q"): elif key in (curses.KEY_F10, "q", "Q"):
return return
elif is_escape(key): elif is_escape(key):
if confirm(win, "Explorer beenden?"): if confirm(win, "Snapshot schliessen und zurueck zur Auswahl?"):
return return
# -- Aktionen --------------------------------------------------------- # -- Aktionen ---------------------------------------------------------
@@ -719,12 +723,17 @@ class Explorer:
"F6 / g Verzeichnis direkt eingeben", "F6 / g Verzeichnis direkt eingeben",
"F2 / m anderes Dateisystem des Snapshots waehlen", "F2 / m anderes Dateisystem des Snapshots waehlen",
"r neu einlesen", "r neu einlesen",
"F10 / q beenden", "F10 / q zurueck zur Snapshot-Auswahl",
"", "",
"Der Snapshot ist schreibgeschuetzt eingebunden - es kann nichts", "Der Snapshot ist schreibgeschuetzt eingebunden - es kann nichts",
"kaputtgehen. Kopiert wird immer vom aktiven ins andere Fenster.", "kaputtgehen. Kopiert wird immer vom aktiven ins andere Fenster.",
"Geraetedateien und Sockets werden uebersprungen, symbolische", "Geraetedateien und Sockets werden uebersprungen, symbolische",
"Verweise bleiben Verweise.", "Verweise bleiben Verweise.",
"",
"Es gibt drei Ebenen: Gastauswahl -> Snapshot-Auswahl -> Dateien.",
"F10 oder q geht jeweils eine Ebene zurueck; der Snapshot wird",
"dabei wieder ausgehaengt. Das rechte Fenster behaelt sein",
"Verzeichnis, auch wenn man den naechsten Snapshot oeffnet.",
] ]
height, width = win.getmaxyx() height, width = win.getmaxyx()
win.erase() win.erase()
@@ -755,7 +764,7 @@ def _pick_guest(win, guests):
% ("LXC" if g.type == "lxc" else "VM ", g.vmid, % ("LXC" if g.type == "lxc" else "VM ", g.vmid,
truncate(g.name or "-", 24), g.status, g.node)) truncate(g.name or "-", 24), g.status, g.node))
for g in guests] for g in guests]
return choose(win, "Welcher Gast?", entries) return choose(win, "Welcher Gast? (Esc beendet)", entries)
def _pick_snapshot(win, snapshots): def _pick_snapshot(win, snapshots):
@@ -766,7 +775,7 @@ def _pick_snapshot(win, snapshots):
entries.append((snap.name, "%-34s %s %s" entries.append((snap.name, "%-34s %s %s"
% (truncate(snap.name, 34), when, % (truncate(snap.name, 34), when,
truncate(snap.description.replace("\n", " "), 40)))) truncate(snap.description.replace("\n", " "), 40))))
return choose(win, "Welcher Snapshot?", entries) return choose(win, "Welcher Snapshot? (Esc = zurueck zur Gastauswahl)", entries)
def _wait_screen(win, text): def _wait_screen(win, text):
@@ -817,41 +826,71 @@ def _main(stdscr, args):
message(stdscr, "Keine VMs oder Container gefunden.", error=True) message(stdscr, "Keine VMs oder Container gefunden.", error=True)
return 1 return 1
if args.vmid: # Drei Ebenen, aus denen man jeweils eine zurueckkommt:
guest = next((g for g in guests if g.vmid == args.vmid), None) # Gastauswahl -> Snapshot-Auswahl -> Dateibrowser
if guest is None: # Der Snapshot wird beim Verlassen des Browsers wieder ausgehaengt, damit
message(stdscr, "Gast %d nicht gefunden." % args.vmid, error=True) # man gleich den naechsten oeffnen kann.
return 2 wanted_vmid, wanted_snapshot = args.vmid, args.snapshot
else:
guest = _pick_guest(stdscr, guests)
if guest is None:
return 0
_wait_screen(stdscr, "Lese Snapshots von %s ..." % guest.label) while True:
snapshots = list_snapshots(proxmox, guest) if wanted_vmid:
if not snapshots: guest = next((g for g in guests if g.vmid == wanted_vmid), None)
message(stdscr, "%s hat keine Snapshots." % guest.label, error=True) wanted_vmid = None
return 1 if guest is None:
message(stdscr, "Gast %d nicht gefunden." % args.vmid, error=True)
return 2
else:
guest = _pick_guest(stdscr, guests)
if guest is None:
return 0
if args.snapshot: while True:
name = next((s.name for s in snapshots if s.name == args.snapshot), None) _wait_screen(stdscr, "Lese Snapshots von %s ..." % guest.label)
if name is None: try:
message(stdscr, "Snapshot %r nicht gefunden." % args.snapshot, error=True) snapshots = list_snapshots(proxmox, guest)
return 2 except ProxmoxError as exc:
else: message(stdscr, str(exc), error=True)
name = _pick_snapshot(stdscr, snapshots) break
if name is None:
return 0
session = Session(proxmox, guest, name) if wanted_snapshot:
name = next((s.name for s in snapshots if s.name == wanted_snapshot), None)
if name is None:
message(stdscr, "Snapshot %r nicht gefunden."
% wanted_snapshot, error=True)
wanted_snapshot = None
if name is None:
continue
elif not snapshots:
message(stdscr, "%s hat keine Snapshots." % guest.label, error=True)
break
else:
name = _pick_snapshot(stdscr, snapshots)
if name is None:
break # zurueck zur Gastauswahl
_browse(stdscr, proxmox, guest, name, local)
# Beim Zurueckkommen kann sich das Inventar geaendert haben.
_wait_screen(stdscr, "Frage Proxmox nach VMs und Containern ...")
try:
guests = proxmox.inventory(refresh=True)
except ProxmoxError as exc:
message(stdscr, str(exc), error=True)
return 3
def _browse(stdscr, proxmox, guest, snapname, local):
"""Einen Snapshot einbinden, durchsehen und wieder aushaengen."""
session = Session(proxmox, guest, snapname)
try: try:
_wait_screen(stdscr, "Binde Snapshot '%s' ein - das kann etwas dauern ..." % name) _wait_screen(stdscr,
"Binde Snapshot '%s' ein - das kann etwas dauern ..." % snapname)
session.open() session.open()
session.prepare() session.prepare()
except (SnapfsError, ProxmoxError) as exc: except (SnapfsError, ProxmoxError) as exc:
session.close() session.close()
_show_notes(stdscr, str(exc).splitlines() + session.notes, "Fehlgeschlagen") _show_notes(stdscr, str(exc).splitlines() + session.notes, "Fehlgeschlagen")
return 3 return
try: try:
_show_notes(stdscr, session.notes) _show_notes(stdscr, session.notes)
@@ -860,19 +899,21 @@ def _main(stdscr, args):
_show_notes(stdscr, _show_notes(stdscr,
["Es wurde kein Dateisystem eingehaengt."] + session.notes, ["Es wurde kein Dateisystem eingehaengt."] + session.notes,
"Nichts zu zeigen") "Nichts zu zeigen")
return 3 return
start = entries[0][0] start = entries[0][0]
if len(entries) > 1: if len(entries) > 1:
start = choose(stdscr, "Welches Dateisystem zuerst?", entries) or start start = choose(stdscr, "Welches Dateisystem zuerst?", entries)
if start is None:
return # zurueck zur Snapshot-Auswahl
left = Pane(path=start, root=start, readonly=True, left = Pane(path=start, root=start, readonly=True,
title="%s @ %s" % (guest.label, name)) title="%s @ %s" % (guest.label, snapname))
Explorer(left, local, session=session, Explorer(left, local, session=session,
subtitle="%s - Snapshot %s" % (guest.label, name)).run(stdscr) subtitle="%s - Snapshot %s" % (guest.label, snapname)).run(stdscr)
finally: finally:
_wait_screen(stdscr, "Haenge Snapshot wieder aus ...") _wait_screen(stdscr, "Haenge Snapshot wieder aus ...")
session.close() session.close()
return 0
def build_parser(): def build_parser():