diff --git a/README.md b/README.md index a070e8e..561f1f0 100644 --- a/README.md +++ b/README.md @@ -702,10 +702,52 @@ Maschine als verwaist gelten. Ohne Elternteil wird nichts von selbst entfernt, und der Schutz eines Snapshots wird nur gelöst, wenn wirklich kein Klon mehr daran hängt. -Bei anderen Storages sieht es anders aus: **LVM-thin**-Klone sind ohnehin -unabhängig (der Thin-Pool zählt die Blöcke, das Original darf weg), bei **ZFS** -hängt der Klon am Snapshot und ließe sich nur per `zfs send | zfs recv` lösen — -das macht `flatten` nicht und meldet die Datenträger als nicht behandelt. +### Und wenn die Maschine nicht auf Ceph liegt? + +Das ganze Thema „Abhängigkeit vom Quell-Snapshot" ist eine **Ceph-Eigenheit**. +Andere Storages verhalten sich anders: + +| Storage | Klon aus Snapshot | Hängt am Quell-Snapshot? | `flatten` | +|---|---|---|---| +| `rbd` (Ceph) | `rbd clone`, sofort | **ja** — Snapshot wird geschützt und unlöschbar | nötig für den Dauerbetrieb | +| `lvmthin` | `lvcreate -s`, sofort | **nein** | nicht nötig | +| `zfspool` | `zfs clone`, sofort | ja | nicht möglich (nur `zfs send \| zfs recv`) | +| `lvm` (dick) | — | — | PVE kann dort gar keine Snapshots | +| `dir`, `nfs`, `cifs` | — | — | kein Klon aus qcow2-Snapshots möglich | + +**LVM-thin ist in diesem Punkt sogar angenehmer als Ceph.** Ein Thin-Snapshot +ist ein eigenständiges Volume, das sich mit anderen nur die Blöcke im Pool +teilt — nachgemessen: + +``` +Belegung im Thin-Pool nach dem Klonen (256 MB Nutzdaten): + data <19.84g 1.26 % ← Pool hält die Daten nur EINMAL + snap_..._wdhtest 1.00g + vm-9998-disk-0 1.00g 25.00 % Klon + vm-9999-disk-0 1.00g 25.00 % Original + +Quell-Snapshot löschen, während der Klon existiert → erlaubt +Original löschen → erlaubt +Prüfsumme des Klons danach → unverändert +``` + +Also: platzsparend geteilt **und** trotzdem frei löschbar. Es gibt dort weder +das Problem mit geschützten Snapshots noch etwas zu flatten — `flatten` meldet +solche Datenträger entsprechend als „nicht nötig". + +Der Preis ist ein anderer: `lvmthin` ist **lokaler** Speicher. Die +Wiederherstellung muss auf demselben Node laufen wie das Original; +`pvesnap-recovery` prüft das und lehnt einen anderen Node ab. Mit Ceph ist sie +dagegen auf jedem Node des Clusters startbar. + +Und ein Warnhinweis, der für Thin-Pools genauso gilt wie für Ceph: Läuft der +**Thin-Pool** voll, stehen alle Volumes darin. Überprovisionierung im Blick +behalten. + +Bei **ZFS** hängt der Klon wie bei Ceph am Snapshot, aber `zfs promote` würde +die Abhängigkeit nur umdrehen statt auflösen. Echte Unabhängigkeit ginge dort +nur über eine Vollkopie per `zfs send | zfs recv` — das macht `flatten` nicht +und sagt es auch so. ### Der Arbeitsspeicher kommt mit diff --git a/pvesnap/recovery.py b/pvesnap/recovery.py index a867e61..6217e3f 100644 --- a/pvesnap/recovery.py +++ b/pvesnap/recovery.py @@ -256,6 +256,26 @@ def linked_volumes(proxmox, instance): return linked +def flatten_hint(proxmox, volid): + """Warum ein Datentraeger nicht abgekoppelt wird - je nach Storage. + + Nur Ceph kennt das Problem ueberhaupt: dort haelt ein Klon seinen + Quell-Snapshot fest. Andere Storage-Typen loesen das anders oder gar nicht. + """ + storage = volid.split(":", 1)[0] if ":" in volid else "" + try: + kind = (proxmox._json(["get", "/storage/%s" % storage]) or {}).get("type", "") + except ProxmoxError: + kind = "" + if kind == "lvmthin": + return ("nicht noetig - Thin-Snapshots sind von sich aus unabhaengig; " + "Quell-Snapshot und Original lassen sich jederzeit loeschen") + if kind == "zfspool": + return ("nicht moeglich - ein ZFS-Klon haengt am Snapshot. Loesen ginge " + "nur ueber 'zfs send | zfs recv' in einen neuen Datentraeger") + return "kein Ceph-Datentraeger" + + def flatten_cost(proxmox, instance): """Wieviele Bytes ein flatten kopieren muesste. @@ -785,10 +805,19 @@ def plan(proxmox, guest, snapname, spec): "vergroessern oder verkleinern - entweder --memory " "weglassen oder --no-resume verwenden.") if spec.mode == "recovery": - result.notes.append( - "Die Datentraeger sind Linked Clones - sie haengen am Quell-Snapshot, " - "der dadurch unloeschbar wird. Soll die Maschine dauerhaft laufen, " - "spaeter mit 'pvesnap-recovery flatten %d' loesen." % result.newid) + kinds = {_storage_info(proxmox, volid.split(":", 1)[0]).get("type") + for _key, volid, _size in result.disks} + if "rbd" in kinds: + result.notes.append( + "Die Datentraeger sind Linked Clones - auf Ceph haelt ein Klon " + "seinen Quell-Snapshot fest, der dadurch unloeschbar wird. Soll " + "die Maschine dauerhaft laufen, spaeter mit " + "'pvesnap-recovery flatten %d' loesen." % result.newid) + elif kinds <= {"lvmthin"}: + result.notes.append( + "Die Datentraeger sind Thin-Snapshots und damit von sich aus " + "unabhaengig - Quell-Snapshot und Original lassen sich jederzeit " + "loeschen. Ein Abkoppeln ist nicht noetig.") if result.resume: result.notes.append("Der Arbeitsspeicher (%s) wird kopiert; die Maschine " "laeuft danach genau dort weiter, wo sie beim Snapshot " diff --git a/pvesnap/recovery_ui.py b/pvesnap/recovery_ui.py index cbeb554..3f72d82 100644 --- a/pvesnap/recovery_ui.py +++ b/pvesnap/recovery_ui.py @@ -27,7 +27,8 @@ from .curses_util import (C_DIM, C_FOOTER, C_HEADER, C_MARK, C_OK, C_SEL, C_WARN keybar, message, prompt, put, read_key) from .proxmox import Proxmox, ProxmoxError, pvesh_available from .recovery import (EXCHANGE_GUEST_PATH, MKFS, RecoveryError, Spec, create, - destroy, find_orphans, flatten, flatten_cost, human_bytes, + destroy, find_orphans, flatten, flatten_cost, flatten_hint, + human_bytes, linked_volumes, list_instances, plan, pull, release, remove_orphans, start) from .snapfs import list_snapshots @@ -565,7 +566,8 @@ def _flatten(stdscr, proxmox, instance, linked): + _plain(progress.lines), "Fehlgeschlagen") return lines = ["%d Datentraeger geloest." % len(done), ""] - lines += ["nicht behandelt (kein Ceph): %s" % volid for volid in other] + for volid in other: + lines += _wrap("%s: %s" % (volid, flatten_hint(proxmox, volid)), 74) if not linked_volumes(proxmox, instance): lines += ["", "%s ist jetzt eigenstaendig. Die Snapshots des Originals " "lassen sich wieder loeschen." % instance.label] @@ -889,7 +891,7 @@ def cmd_flatten(proxmox, args): for volid in already: print(" war schon eigenstaendig: %s" % volid) for volid in other: - print(" nicht behandelt (kein Ceph-Datentraeger): %s" % volid) + 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)