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
+129
-3
@@ -29,10 +29,11 @@ from .curses_util import (C_DIM, C_FOOTER, C_HEADER, C_MARK, C_OK, C_SEL, C_WARN
|
||||
from .proxmox import Proxmox, ProxmoxError, pvesh_available
|
||||
from . import transfer, transfer_ui
|
||||
from .recovery import (RecoveryError, Spec, create, destroy, eject_transfer,
|
||||
find_orphans, flatten, flatten_cost, flatten_hint,
|
||||
find_orphans, find_stale_protections, flatten,
|
||||
flatten_cost, flatten_hint,
|
||||
human_bytes, insert_transfer, linked_volumes,
|
||||
display_state, list_instances, node_address, plan,
|
||||
remove_orphans, set_display,
|
||||
release_protections, remove_orphans, set_display,
|
||||
spice_file, start, transfer_state)
|
||||
from .snapfs import list_snapshots
|
||||
from .util import truncate
|
||||
@@ -46,6 +47,7 @@ KEYS = [
|
||||
("h", "Herunterfahren", "Stop"),
|
||||
("x", "Verwerfen", "Verwerf."),
|
||||
("v", "Laufwerke", "Laufw."),
|
||||
("c", "Aufraeumen", "Aufr."),
|
||||
("r", "Aktualisieren", "Aktual."),
|
||||
("q", "Beenden", "Ende"),
|
||||
]
|
||||
@@ -878,6 +880,9 @@ def _overview(stdscr, proxmox):
|
||||
elif key in ("v", "V"):
|
||||
transfer_ui.screen(stdscr, browse=_browse_transfer)
|
||||
reload_ = True
|
||||
elif key in ("c", "C"):
|
||||
_cleanup(stdscr, proxmox)
|
||||
reload_ = True
|
||||
elif key in ("r", "R"):
|
||||
reload_ = True
|
||||
elif key == curses.KEY_UP and instances:
|
||||
@@ -901,6 +906,80 @@ def _overview(stdscr, proxmox):
|
||||
reload_ = True
|
||||
|
||||
|
||||
def _cleanup(stdscr, proxmox):
|
||||
"""Reste aufraeumen, die entstehen, wenn jemand an pvesnap vorbei loescht.
|
||||
|
||||
Zwei verschiedene Dinge, deshalb zwei Durchgaenge: liegengebliebene
|
||||
Datentraeger - und geschuetzte Snapshots, deren Klon es nicht mehr gibt.
|
||||
Den zweiten Fall findet die Suche nach Datentraegern prinzipiell nicht.
|
||||
"""
|
||||
_wait_screen(stdscr, "Suche liegengebliebene Datentraeger ...")
|
||||
try:
|
||||
orphans = find_orphans(proxmox)
|
||||
except (RecoveryError, ProxmoxError) as exc:
|
||||
message(stdscr, str(exc), error=True)
|
||||
return
|
||||
|
||||
fortschritt = _Progress(stdscr, "Suche geschuetzte Snapshots ohne Klon")
|
||||
try:
|
||||
stale = find_stale_protections(proxmox, progress=fortschritt)
|
||||
except (RecoveryError, ProxmoxError) as exc:
|
||||
message(stdscr, str(exc), error=True)
|
||||
return
|
||||
|
||||
klone = [e for e in orphans if e["parent"]]
|
||||
if not klone and not stale:
|
||||
message(stdscr, "Nichts liegengeblieben - alles sauber.")
|
||||
return
|
||||
|
||||
zeilen = []
|
||||
if klone:
|
||||
zeilen.append(("Datentraeger, zu denen es keinen Gast mehr gibt:", curses.A_BOLD))
|
||||
for entry in klone:
|
||||
zeilen.append((" %-26s %8s Klon von %s"
|
||||
% (entry["volid"], human_bytes(entry["bytes"]),
|
||||
entry["parent"]), curses.color_pair(C_MARK)))
|
||||
zeilen.append(("", 0))
|
||||
if stale:
|
||||
zeilen.append(("Geschuetzte Snapshots, an denen kein Klon mehr haengt:",
|
||||
curses.A_BOLD))
|
||||
for entry in stale:
|
||||
zeilen.append((" %s@%s" % (entry["volid"], entry["snapshot"]),
|
||||
curses.color_pair(C_MARK)))
|
||||
zeilen.append(("", 0))
|
||||
for text in _wrap("An diesen scheitert die Vorhaltezeit dauerhaft mit "
|
||||
"\"snapshot is protected\". Der Schutz wird geloest - "
|
||||
"geloescht wird dabei nichts.", 74):
|
||||
zeilen.append((text, curses.color_pair(C_DIM)))
|
||||
zeilen.append(("", 0))
|
||||
|
||||
ohne_eltern = [e for e in orphans if not e["parent"]]
|
||||
if ohne_eltern:
|
||||
for text in _wrap("Ausserdem liegen %d Datentraeger ohne Elternteil herum. "
|
||||
"Die werden hier nicht angefasst - dafuer gibt es "
|
||||
"'pvesnap-recovery cleanup --all'." % len(ohne_eltern), 74):
|
||||
zeilen.append((text, curses.color_pair(C_DIM)))
|
||||
|
||||
taste = _pages(stdscr, zeilen, "Aufraeumen",
|
||||
"j = aufraeumen | andere Taste = nichts tun")
|
||||
if taste not in ("j", "J", "y", "Y"):
|
||||
return
|
||||
|
||||
fortschritt = _Progress(stdscr, "Raeume auf")
|
||||
bericht = []
|
||||
if klone:
|
||||
entfernt, gescheitert = remove_orphans(proxmox, klone, progress=fortschritt)
|
||||
bericht.append("%d Datentraeger entfernt." % len(entfernt))
|
||||
bericht += [" fehlgeschlagen: %s" % p for p in gescheitert]
|
||||
if stale:
|
||||
geloest, gescheitert = release_protections(proxmox, stale,
|
||||
progress=fortschritt)
|
||||
bericht.append("%s geloest." % _anzahl(len(geloest), "Schutzmarke",
|
||||
"Schutzmarken"))
|
||||
bericht += [" fehlgeschlagen: %s" % p for p in gescheitert]
|
||||
_pages(stdscr, _plain(bericht), "Fertig")
|
||||
|
||||
|
||||
def _browse_transfer(win, path, volume):
|
||||
"""Den Zwei-Fenster-Commander auf ein Transfer-Laufwerk loslassen.
|
||||
|
||||
@@ -1119,9 +1198,23 @@ def cmd_flatten(proxmox, args):
|
||||
|
||||
|
||||
def cmd_cleanup(proxmox, args):
|
||||
"""Zwei Durchgaenge - sie finden verschiedene Dinge.
|
||||
|
||||
Der erste sucht Datentraeger ohne Gast. Der zweite geschuetzte Snapshots
|
||||
ohne Klon: die findet der erste prinzipiell nicht, weil es dazu keinen
|
||||
Datentraeger mehr gibt, nach dem er suchen koennte.
|
||||
"""
|
||||
schlecht = _cleanup_volumes(proxmox, args)
|
||||
print()
|
||||
schlecht += _cleanup_protections(proxmox, args)
|
||||
return 1 if schlecht else 0
|
||||
|
||||
|
||||
def _cleanup_volumes(proxmox, args):
|
||||
"""Liegengebliebene Datentraeger. Liefert die Zahl der Fehlschlaege."""
|
||||
orphans = find_orphans(proxmox)
|
||||
if not orphans:
|
||||
print("Nichts liegengeblieben.")
|
||||
print("Keine liegengebliebenen Datentraeger.")
|
||||
return 0
|
||||
|
||||
clones = [e for e in orphans if e["parent"]]
|
||||
@@ -1158,6 +1251,39 @@ def cmd_cleanup(proxmox, args):
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
def _anzahl(zahl, einzahl, mehrzahl):
|
||||
return "%d %s" % (zahl, einzahl if zahl == 1 else mehrzahl)
|
||||
|
||||
|
||||
def _cleanup_protections(proxmox, args):
|
||||
"""Geschuetzte Snapshots ohne Klon. Liefert die Zahl der Fehlschlaege."""
|
||||
print("Suche geschuetzte Snapshots ohne Klon ...")
|
||||
stale = find_stale_protections(proxmox,
|
||||
progress=lambda text: print(" " + text))
|
||||
if not stale:
|
||||
print("Keine gefunden.")
|
||||
return 0
|
||||
|
||||
print("\nGeschuetzte Snapshots, an denen kein Klon mehr haengt:\n")
|
||||
for entry in stale:
|
||||
print(" %s@%s" % (entry["volid"], entry["snapshot"]))
|
||||
|
||||
print("\nAn diesen scheitert die Vorhaltezeit dauerhaft mit\n"
|
||||
"\"snapshot is protected\". Der Schutz wird geloest - geloescht wird\n"
|
||||
"dabei nichts, die Snapshots bleiben stehen.")
|
||||
if not args.yes:
|
||||
if _ask("\nSchutz loesen? [j/N] ") not in ("j", "y", "ja", "yes"):
|
||||
print("Abgebrochen.")
|
||||
return 0
|
||||
|
||||
released, failed = release_protections(
|
||||
proxmox, stale, progress=lambda text: print(" " + text))
|
||||
print("\n%s geloest." % _anzahl(len(released), "Schutzmarke", "Schutzmarken"))
|
||||
for problem in failed:
|
||||
print(" fehlgeschlagen: %s" % problem, file=sys.stderr)
|
||||
return 1 if failed else 0
|
||||
|
||||
|
||||
def cmd_spice(proxmox, args):
|
||||
instance = _find_instance(proxmox, args.vmid)
|
||||
text = spice_file(proxmox, instance)
|
||||
|
||||
Reference in New Issue
Block a user