Explorer: Transfer-Laufwerke auf F2; cleanup findet verwaiste Snapshot-Sperren
Zwei Luecken, die seit dem Umbau offenstanden.
1. Transfer-Laufwerke waren aus dem Explorer nicht erreichbar
F2 zeigte nur die Dateisysteme des Snapshots. Jetzt stehen die
Transfer-Laufwerke in derselben Liste - aus Sicht des Bedieners ist es
dieselbe Frage ("wo soll ich hinschauen?"), und der Weg ueber
pvesnap-recovery entfaellt.
Das gewaehlte Laufwerk landet im rechten Fenster, links bleibt der
Snapshot. Damit laesst sich direkt aus einem Snapshot auf das
Austauschmedium kopieren, das anschliessend in die wiederhergestellte
Maschine wandert - ohne Zwischenlager auf dem Host.
Ausgehaengt wird beim Verlassen, und zwar nur, was wir selbst eingehaengt
haben: ein Laufwerk, das schon vorher am Host hing, gehoert jemand
anderem. Das finally faengt auch Absturz und Strg-C ab - bliebe es
eingehaengt, gaelte es spaeter als belegt.
2. cleanup fand geschuetzte Snapshots ohne Klon nicht
Loescht jemand eine Wiederherstellung in der Proxmox-Oberflaeche, raeumt
Proxmox den Klon durchaus mit ab - aber nicht den Schutz seines
Quell-Snapshots (rbd snap protect, den setzt es beim Klonen selbst).
Zurueck bleibt ein geschuetzter Snapshot ohne Klon: die Vorhaltezeit
scheitert an ihm jede Nacht mit "snapshot is protected", und
find_orphans() findet prinzipiell nichts, weil es den Datentraeger, nach
dem es sucht, wirklich nicht mehr gibt. Dasselbe entsteht nach einem von
Hand ausgefuehrten rbd flatten.
find_stale_protections() sucht deshalb direkt nach der Ursache: jeder
geschuetzte Snapshot, an dem kein Klon haengt. Ein solcher Schutz hat
keinen Zweck - er existiert einzig dafuer, dass ein Klon seine Grundlage
behaelt. Geloest wird nur der Schutz, geloescht wird nichts. Vor jedem
Loesen wird ein zweites Mal nachgesehen, ob inzwischen doch ein Klon
daran haengt.
cleanup laeuft damit in zwei Durchgaengen und liegt neu auch in der
Oberflaeche auf Taste c - man sucht sonst lange nach einem Befehl, den
man nur im Notfall braucht.
Getestet gegen die Kulisse der Handbuch-Werkstatt: die rbd-Attrappe
kennt jetzt snap ls und children und enthaelt beide Zustaende
nebeneinander - ein geschuetzter Snapshot mit Klon (muss in Ruhe
gelassen werden) und einer ohne (muss gefunden werden).
Handbuch und README nachgezogen, zwei Bildschirmfotos dazu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
59e7224297
commit
d9d9345d03
+97
-8
@@ -29,6 +29,7 @@ from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
|
||||
from . import __version__
|
||||
from . import transfer
|
||||
from .curses_util import (C_DIM, C_DIR, C_FOOTER, C_HEADER, C_MARK, C_OK, C_SEL,
|
||||
C_WARN, ask, choose, confirm, fill, init_colors, is_enter,
|
||||
is_escape, keybar, message, prompt, put, read_key)
|
||||
@@ -378,6 +379,10 @@ class Explorer:
|
||||
self.session = session
|
||||
self.subtitle = subtitle
|
||||
self.status = ""
|
||||
# Transfer-Laufwerke, die *wir* eingehaengt haben. Nur die haengen wir
|
||||
# beim Verlassen wieder aus - ein Laufwerk, das schon vorher am Host
|
||||
# hing, gehoert jemand anderem.
|
||||
self.mounted_transfers = set()
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
@@ -494,7 +499,15 @@ class Explorer:
|
||||
def run(self, win):
|
||||
for pane in self.panes:
|
||||
pane.reload()
|
||||
try:
|
||||
self._loop(win)
|
||||
finally:
|
||||
# Auch bei Absturz oder Strg-C: ein Transfer-Laufwerk, das am Host
|
||||
# eingehaengt bleibt, gilt spaeter als belegt und laesst sich dann
|
||||
# nicht in eine Maschine haengen.
|
||||
self._release_transfers()
|
||||
|
||||
def _loop(self, win):
|
||||
while True:
|
||||
self.draw(win)
|
||||
win.refresh()
|
||||
@@ -661,14 +674,85 @@ class Explorer:
|
||||
message(win, "Kein Verzeichnis: %s" % path, error=True)
|
||||
|
||||
def _switch_mount(self, win):
|
||||
if not self.session or len(self.session.mounts) < 2:
|
||||
message(win, "Es ist nur ein Dateisystem eingebunden.")
|
||||
"""Laufwerk waehlen - Dateisystem des Snapshots oder Transfer-Laufwerk.
|
||||
|
||||
Beides steht in derselben Liste, weil es aus Sicht des Bedieners
|
||||
dieselbe Frage ist: "wo soll ich hinschauen?". Ein Transfer-Laufwerk
|
||||
hier zu erreichen spart den Umweg ueber pvesnap-recovery - man kann
|
||||
aus dem Snapshot direkt auf das Austauschmedium kopieren, das
|
||||
anschliessend in die wiederhergestellte Maschine wandert.
|
||||
"""
|
||||
entries = []
|
||||
|
||||
filesystems = self.session.mount_entries() if self.session else []
|
||||
if len(filesystems) > 1:
|
||||
for path, text in filesystems:
|
||||
entries.append((("fs", path), "Snapshot: %s" % text))
|
||||
|
||||
for volume in self._transfer_volumes():
|
||||
state = "" if volume.free or volume.mountpoint \
|
||||
else " [%s]" % volume.state_text
|
||||
entries.append((("transfer", volume.name),
|
||||
"Transfer: %-14s %8s %s%s"
|
||||
% (volume.name, human_bytes(volume.size),
|
||||
truncate(volume.note, 26), state)))
|
||||
|
||||
if not entries:
|
||||
message(win, "Es ist nur ein Dateisystem eingebunden, und "
|
||||
"Transfer-Laufwerke gibt es noch keine.")
|
||||
return
|
||||
entries = self.session.mount_entries()
|
||||
chosen = choose(win, "Welches Dateisystem?", entries)
|
||||
if chosen:
|
||||
self.panes[0].root = chosen
|
||||
self.panes[0].go(chosen)
|
||||
|
||||
chosen = choose(win, "Welches Laufwerk?", entries)
|
||||
if not chosen:
|
||||
return
|
||||
kind, value = chosen
|
||||
if kind == "fs":
|
||||
self.panes[0].root = value
|
||||
self.panes[0].go(value)
|
||||
else:
|
||||
self._open_transfer(win, value)
|
||||
|
||||
def _transfer_volumes(self):
|
||||
try:
|
||||
return transfer.load()
|
||||
except transfer.TransferError as exc:
|
||||
self.status = str(exc)
|
||||
return []
|
||||
|
||||
def _open_transfer(self, win, name):
|
||||
"""Ein Transfer-Laufwerk einhaengen und ins Arbeitsfenster holen.
|
||||
|
||||
Ins Arbeitsfenster, nicht ins Snapshot-Fenster: links steht der
|
||||
Snapshot, aus dem geholt wird, rechts das Ziel. Genau dorthin gehoert
|
||||
ein Austauschmedium. Gibt es keinen Snapshot (Aufruf aus der
|
||||
Laufwerksverwaltung heraus), landet es im gerade aktiven Fenster.
|
||||
"""
|
||||
target = self.panes[1] if self.session else self.source
|
||||
try:
|
||||
already = bool(transfer.get(name).mountpoint)
|
||||
path = transfer.host_mount(name)
|
||||
except transfer.TransferError as exc:
|
||||
message(win, str(exc), error=True)
|
||||
return
|
||||
if not already:
|
||||
self.mounted_transfers.add(name)
|
||||
|
||||
target.root = "/"
|
||||
target.marks.clear()
|
||||
if not target.go(path):
|
||||
message(win, "Eingehaengt unter %s, aber nicht lesbar." % path, error=True)
|
||||
return
|
||||
target.title = "Transfer: %s" % name
|
||||
self.status = "%s eingehaengt unter %s" % (name, path)
|
||||
|
||||
def _release_transfers(self):
|
||||
"""Was wir eingehaengt haben, geben wir auch wieder frei."""
|
||||
for name in sorted(self.mounted_transfers):
|
||||
try:
|
||||
transfer.host_umount(name)
|
||||
except transfer.TransferError as exc:
|
||||
log.warning("Transfer-Laufwerk %s blieb eingehaengt: %s", name, exc)
|
||||
self.mounted_transfers.clear()
|
||||
|
||||
def _view(self, win, entry):
|
||||
if not entry.name or entry.is_dir or entry.is_special:
|
||||
@@ -732,7 +816,8 @@ class Explorer:
|
||||
"F3 / v Datei ansehen (Text oder Hex)",
|
||||
"F7 / n neues Verzeichnis (nur im lokalen Fenster)",
|
||||
"F6 / g Verzeichnis direkt eingeben",
|
||||
"F2 / m anderes Dateisystem des Snapshots waehlen",
|
||||
"F2 / m Laufwerk waehlen: anderes Dateisystem des Snapshots",
|
||||
" oder ein Transfer-Laufwerk ins rechte Fenster",
|
||||
"r neu einlesen",
|
||||
"F10 / q zurueck zur Snapshot-Auswahl",
|
||||
"",
|
||||
@@ -741,6 +826,10 @@ class Explorer:
|
||||
"Geraetedateien und Sockets werden uebersprungen, symbolische",
|
||||
"Verweise bleiben Verweise.",
|
||||
"",
|
||||
"Transfer-Laufwerke (F2) sind Austauschmedien fuer Maschinen ohne",
|
||||
"Netz: hier befuellen, spaeter in die Wiederherstellung haengen.",
|
||||
"Beim Verlassen werden sie wieder ausgehaengt.",
|
||||
"",
|
||||
"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",
|
||||
|
||||
Reference in New Issue
Block a user