Handbuch auf MkDocs-Basis, mit Bildschirmfotos aus dem Programm

26 Kapitel in vier Teilen, in der Reihenfolge, in der man sie braucht:
erst sichern, dann Dateien holen, dann ganze Maschinen wiederherstellen,
dahinter der Nachschlagteil.

Gebaut wird mit MkDocs + Material. Bewusst ohne Netzabhaengigkeiten:
keine Schriften vom CDN (font: false), Volltextsuche mit deutschem
Stemming liegt neben den Seiten. Im Notfall steht vielleicht das halbe
Netz - dann nuetzt eine Doku im Internet nichts.

  handbuch/bauen.sh              baut handbuch/site/
  handbuch/bauen.sh ansehen      Vorschau auf 127.0.0.1:8000

install.sh nimmt das gebaute Handbuch mit nach
/usr/share/doc/pvesnap/handbuch/ - falls es vorliegt. Auf dem Host selbst
wird nichts gebaut, mkdocs gehoert nicht auf einen Hypervisor.

Die 28 Bildschirmfotos sind nicht abfotografiert, sondern erzeugt: Der
echte Programmcode laeuft in einem Pseudo-Terminal gegen einen erfundenen
Proxmox-Host (Attrappen fuer pvesh, perl und rbd), pyte baut den Bildschirm
nach, heraus faellt ein SVG. Damit stimmen sie garantiert mit dem Programm
ueberein, sind reproduzierbar und enthalten keine echten Daten. Die
Werkstatt dafuer liegt unter handbuch/werkstatt/ samt LIESMICH.md.

Nebenbei: die Schlussmeldung von install.sh warb noch mit --exchange,
das mit dem eingebauten Austauschlaufwerk weggefallen ist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-08-09 12:39:30 +02:00
co-authored by Claude Opus 5
parent c484596702
commit 59e7224297
72 changed files with 7355 additions and 2 deletions
+5
View File
@@ -10,3 +10,8 @@ __pycache__/
*.lock *.lock
state.json state.json
pvesnap.conf pvesnap.conf
# Handbuch
handbuch/site/
handbuch/.venv/
handbuch/werkstatt/demo/
+19
View File
@@ -21,6 +21,25 @@ Dazu drei Werkzeuge, um wieder **an die Daten im Snapshot** zu kommen:
--- ---
## Handbuch
Diese Datei ist die Kurzfassung. Ausführlich, mit Bildschirmfotos und
Volltextsuche, steht alles im **Handbuch** unter [`handbuch/`](handbuch/):
```bash
handbuch/bauen.sh # baut handbuch/site/ (reines HTML, ohne Netz nutzbar)
handbuch/bauen.sh ansehen # Vorschau auf http://127.0.0.1:8000/
```
`install.sh` nimmt das gebaute Handbuch mit auf den Host, nach
`/usr/share/doc/pvesnap/handbuch/index.html` — genau dort, wo man es im Notfall
braucht, ohne Netz und ohne Suchmaschine.
Die Bildschirmfotos werden aus dem echten Programmcode erzeugt, gegen einen
erfundenen Proxmox-Host; siehe [`handbuch/werkstatt/`](handbuch/werkstatt/).
---
## Installation ## Installation
Auf dem Proxmox-Host als root: Auf dem Proxmox-Host als root:
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# ---------------------------------------------------------------------
# Das Handbuch bauen.
#
# Ergebnis ist reines HTML in handbuch/site/ - ohne Netzzugang benutzbar,
# mit Volltextsuche. install.sh kopiert es von dort nach
# /usr/share/doc/pvesnap/handbuch, wenn es vorliegt.
#
# Gebaut wird auf dem Arbeitsplatz, nicht auf dem Proxmox-Host: mkdocs
# gehoert nicht auf einen Hypervisor.
# ---------------------------------------------------------------------
set -euo pipefail
HIER="$(cd "$(dirname "$0")" && pwd)"
VENV="${HIER}/.venv"
rot() { printf '\033[31m%s\033[0m\n' "$*" >&2; }
info() { printf '\033[36m%s\033[0m\n' "$*"; }
BEDARF=(mkdocs mkdocs-material)
if ! command -v python3 >/dev/null 2>&1; then
rot "python3 wird gebraucht."
exit 1
fi
# Eigene Umgebung, damit nichts am System haengenbleibt.
if [ ! -x "${VENV}/bin/mkdocs" ]; then
info "Richte Bau-Umgebung in ${VENV} ein ..."
python3 -m venv "${VENV}"
"${VENV}/bin/pip" install --quiet --upgrade pip
"${VENV}/bin/pip" install --quiet "${BEDARF[@]}"
fi
case "${1:-bauen}" in
bauen)
info "Baue Handbuch ..."
"${VENV}/bin/mkdocs" build --strict --config-file "${HIER}/mkdocs.yml"
echo
info "Fertig: ${HIER}/site/index.html"
echo " Auf den Host bringen: ./install.sh (kopiert es mit)"
;;
ansehen|serve)
info "Starte Vorschau auf http://127.0.0.1:8000/ (Strg-C beendet)"
"${VENV}/bin/mkdocs" serve --config-file "${HIER}/mkdocs.yml"
;;
bilder)
# Die Bildschirmfotos neu aufnehmen - siehe werkstatt/LIESMICH.md
exec "${HIER}/werkstatt/aufnehmen.sh"
;;
sauber)
rm -rf "${HIER}/site" "${VENV}"
info "site/ und .venv/ entfernt."
;;
*)
cat <<EOF
Aufruf: $0 [befehl]
bauen HTML nach handbuch/site/ bauen (Vorgabe)
ansehen Vorschau im Browser, baut bei jeder Aenderung neu
bilder Bildschirmfotos neu aufnehmen (siehe werkstatt/LIESMICH.md)
sauber site/ und die Bau-Umgebung entfernen
EOF
exit 1
;;
esac
+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap config → Globales</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap - Globale Einstellungen </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> /etc/pvesnap.conf </text>
<rect x="31.2" y="116.0" width="989.4" height="18.4" fill="#06989a"/>
<text x="31.2" y="129.3" fill="#2e3436" textLength="989.0" lengthAdjust="spacing" xml:space="preserve">Praefix im Snapshot-Namen auto </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Pruefintervall des Dienstes 1m </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Zustandsdatei /var/lib/pvesnap/state.json </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Protokollstufe INFO </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Zusaetzliche Logdatei - </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Zeitlimit je Snapshot-Task 15m </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Wiederholungen bei belegter Spe… 2 </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Wartezeit vor Wiederholung 1m </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Pause zwischen zwei VMs keine </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Beim Start sofort ausfuehren nein </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Testlauf (nichts wirklich tun) nein </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Standard-Beschreibung pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time… </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Platzhalter der Beschreibung: {group} {vmid} {name} {node} {type} {datetime} {date} {time} {keep_time} {keep_count} </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Enter Aendern | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+40
View File
@@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap config → Gruppe taeglich</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap - Gruppe: taeglich </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Kurzname im Snapshot: auto-taeglich-JJJJMMTT-HHMMSS </text>
<text x="31.2" y="111.3" fill="#d3d7cf" font-weight="bold" textLength="980.4" lengthAdjust="spacing" xml:space="preserve">-- Allgemein -----------------------------------------------------------------------------------------------------</text>
<rect x="31.2" y="116.0" width="989.4" height="18.4" fill="#06989a"/>
<text x="31.2" y="129.3" fill="#2e3436" textLength="989.0" lengthAdjust="spacing" xml:space="preserve">Name taeglich </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Aktiv ja </text>
<text x="31.2" y="165.3" fill="#d3d7cf" font-weight="bold" textLength="980.4" lengthAdjust="spacing" xml:space="preserve">-- Zeitplan ------------------------------------------------------------------------------------------------------</text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Art des Zeitplans taeglich </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Uhrzeit (HH:MM) 02:30 </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Naechster Termin (Vorschau) Mo 10.08.2026 02:30 (taeglich um 02:30) </text>
<text x="31.2" y="237.3" fill="#d3d7cf" font-weight="bold" textLength="980.4" lengthAdjust="spacing" xml:space="preserve">-- Vorhaltezeit --------------------------------------------------------------------------------------------------</text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Anzahl behalten (je VM) 14 </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Maximales Alter 3w </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Mindestens behalten 0 (keine Untergrenze) </text>
<text x="31.2" y="309.3" fill="#d3d7cf" font-weight="bold" textLength="980.4" lengthAdjust="spacing" xml:space="preserve">-- Welche VMs? ---------------------------------------------------------------------------------------------------</text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Alle VMs/Container nein </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> VMs aus Liste waehlen ... - </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Namensmuster (z.B. web-*) - </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Tags produktion </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Pools - </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Gasttypen alle (VMs und Container) </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Ausgeschlossene VMIDs - </text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Ausgeschlossene Namensmuster - </text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Ausgeschlossene Tags nosnap </text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Trifft aktuell zu auf 6 Gast/Gaeste (100, 101, 102, 105, 110, 130) </text>
<text x="31.2" y="507.3" fill="#d3d7cf" font-weight="bold" textLength="980.4" lengthAdjust="spacing" xml:space="preserve">-- Snapshot-Optionen ---------------------------------------------------------------------------------------------</text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> RAM mitsichern (nur laufende … nein </text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Gestoppte ueberspringen nein </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Beschreibung (Vorlage) pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time} … </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Enter Aendern | Leer Umschalten | v VM-Auswahl | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap config</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap - Gruppen </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Datei: /etc/pvesnap.conf [Dienst: laeuft] </text>
<text x="31.2" y="111.3" fill="#d3d7cf" font-weight="bold" textLength="705.2" lengthAdjust="spacing" xml:space="preserve">Gruppe Aktiv Zeitplan Behalte Auswahl</text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> ------------------------------------------------------------------------------------------------------------------ </text>
<rect x="31.2" y="134.0" width="989.4" height="18.4" fill="#06989a"/>
<text x="31.2" y="147.3" fill="#2e3436" textLength="989.0" lengthAdjust="spacing" xml:space="preserve">stuendlich ja alle 1h (an der Uhr ausgeric… 24 / 2d Tags: stuendlich </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> taeglich ja taeglich um 02:30 14 / 3w Tags: produktion </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> monatlich ja monatlich am 1. um 04:00 6 / 57w1d alle VMs </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Enter Bearbeiten | n Neu | c Kopieren | d Loeschen | Leer An/Aus </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> g Global | v VMs | s Speichern | r Dienst neu laden | q Ende </text>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap config → Uebersicht</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap - Uebersicht: welche VM in welcher Gruppe? </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 10 Gaeste </text>
<text x="31.2" y="111.3" fill="#d3d7cf" font-weight="bold" textLength="438.6" lengthAdjust="spacing" xml:space="preserve">VMID Name Status Gruppen</text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 100 web01 running stuendlich, taeglich, monatlich </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 101 db01 running stuendlich, taeglich, monatlich </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 102 fileserver running taeglich, monatlich </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 105 warenwirtschaft running taeglich, monatlich </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 110 mailgw running taeglich, monatlich </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 111 gitlab running monatlich </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 120 build-test stopped (keine Gruppe) </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 130 dc01 running taeglich, monatlich </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 9101 db01-live running (keine Gruppe) </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 9102 warenwirtschaft-w stopped (keine Gruppe) </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Pfeiltasten Blaettern | r Neu laden | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+25
View File
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap config → VMs waehlen</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap - VMs fuer Gruppe 'taeglich' </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 0 ausgewaehlt </text>
<text x="31.2" y="111.3" fill="#d3d7cf" font-weight="bold" textLength="636.4" lengthAdjust="spacing" xml:space="preserve"> VMID Typ Name Node Status Tags</text>
<rect x="31.2" y="116.0" width="989.4" height="18.4" fill="#06989a"/>
<text x="31.2" y="129.3" fill="#2e3436" textLength="989.0" lengthAdjust="spacing" xml:space="preserve">[ ] 100 VM web01 pve1 running produktion,stuendli… </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 101 VM db01 pve1 running produktion,datenban… </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 102 VM fileserver pve2 running produktion </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 105 VM warenwirtschaft pve2 running produktion,dongle </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 110 LXC mailgw pve1 running produktion </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 111 LXC gitlab pve2 running entwicklung </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 120 VM build-test pve2 stopped nosnap </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 130 VM dc01 pve1 running produktion </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 9101 VM db01-live pve1 running pvesnap-recovery </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [ ] 9102 VM warenwirtschaft-w pve2 stopped pvesnap-recovery </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Leer Auswaehlen | a Alle | n Keine | i Umkehren | / Filter | Enter Uebernehmen | q Abbruch </text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve2 — pvesnap-recovery → einrichten</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Vorhaben pruefen </text>
<text x="14.0" y="93.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Quelle: VM 105 (warenwirtschaft) auf pve2 </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Snapshot: auto-taeglich-20260809-023000 </text>
<text x="31.2" y="129.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve">Neu: VM 9104 auf pve2</text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live - abgeschottet </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Datentraeger (Copy-on-Write-Klone, das Original bleibt unberuehrt): </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> scsi0 ceph-vm:vm-105-disk-0 250G </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Netzwerk: keine Netzwerkkarte </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> net0 virtio=BC:24:11:00:69:40,bridge=vmbr0,firewall=1 </text>
<text x="31.2" y="273.3" fill="#4e9a06" textLength="335.4" lengthAdjust="spacing" xml:space="preserve">Anzeige: noVNC und SPICE (vga: qxl)</text>
<text x="31.2" y="291.3" fill="#4e9a06" textLength="378.4" lengthAdjust="spacing" xml:space="preserve">USB: 2 Weiterleitung(en) ueber SPICE</text>
<text x="31.2" y="309.3" fill="#4e9a06" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">Transfer: werkzeuge 4.0G im Gast als WERKZEUGE</text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> - Zugang auf zwei Wegen: noVNC im Browser und SPICE ueber </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 'pvesnap-recovery spice 9104' (remote-viewer). </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> - Im remote-viewer unter 'USB-Geraeteauswahl' das Geraet anhaken - es </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> kommt vom eigenen Rechner, nicht ueber das Netz des Gastes. Guest-Tools </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> braucht es dafuer nicht. </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> j = anlegen | n = abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer 101</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> /run/pvesnap/mnt/101-auto-taeglich-20260809-023000/etc/fstab 699B </text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># /etc/fstab: static file system information. </text>
<text x="14.0" y="93.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># Use 'blkid' to print the universally unique identifier for a device; this may </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># be used with UUID= as a more robust way to name devices that works even if </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># disks are added and removed. See fstab(5). </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"># &lt;file system&gt; &lt;mount point&gt; &lt;type&gt; &lt;options&gt; &lt;dump&gt; &lt;pass&gt; </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve">UUID=4c1a9f3e-2b77-4d18-9a5c-7e3f0d1b8a26 / ext4 errors=remount-ro 0 1 </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve">UUID=9f2c-31AD /boot/efi vfat umask=0077 0 1 </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve">/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi1 /srv/dumps xfs defaults 0 2 </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve">/swap.img none swap sw 0 0 </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Pfeiltasten/Bild blaettern | Pos1/Ende | q zurueck (Zeile 1 von 11) </text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+50
View File
@@ -0,0 +1,50 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer 101</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-explorer - VM 101 (db01) - Snapshot auto-taeglich-20260809-023000 </text>
<rect x="14.0" y="62.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="75.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> VM 101 (db01) @ auto-taeglich-20260809-023000 </text>
<text x="521.4" y="75.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│ Lokaler Rechner </text>
<text x="14.0" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">/run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv/du… </text>
<text x="521.4" y="93.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│/root </text>
<rect x="14.0" y="98.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="521.4" y="111.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="111.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="516.0" lengthAdjust="spacing" xml:space="preserve"> db01-20260808-0200.sql.gz 3.8G 08.08.26 02:42│</text>
<text x="530.0" y="129.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> holen &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="516.0" lengthAdjust="spacing" xml:space="preserve"> db01-20260809-0200.sql.gz 3.9G 09.08.26 02:30│</text>
<text x="530.0" y="147.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> werkzeuge &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> kunden-export.csv 84.3M 07.08.26 07:42│ notizen.md 3.1K 06.08.26 11:42 </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="507.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 4 Eintraege │ 4 Eintraege </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> /run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Tab Fenster | Enter Oeffnen | Leer Markieren | F5 Kopieren </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> F3 Ansehen | F7 Neuer Ordner | F2 Laufwerk | F10 Zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer</text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"> ┌─</text>
<text x="289.2" y="219.3" fill="#d3d7cf" font-weight="bold" textLength="258.0" lengthAdjust="spacing" xml:space="preserve"> Welcher Gast? (Esc beendet) </text>
<text x="547.2" y="219.3" fill="#d3d7cf" textLength="481.6" lengthAdjust="spacing" xml:space="preserve">────────────────────────┐ </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"></text>
<rect x="289.2" y="242.0" width="456.2" height="18.4" fill="#06989a"/>
<text x="289.2" y="255.3" fill="#2e3436" textLength="455.8" lengthAdjust="spacing" xml:space="preserve"> VM 100 web01 running pve1 </text>
<text x="745.0" y="255.3" fill="#d3d7cf" textLength="283.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 101 db01 running pve1 │ </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 102 fileserver running pve2 │ </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 105 warenwirtschaft running pve2 │ </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ LXC 110 mailgw running pve1 │ </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ LXC 111 gitlab running pve2 │ </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 120 build-test stopped pve2 │ </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 130 dc01 running pve1 │ </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 9101 db01-live running pve1 │ </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ VM 9102 warenwirtschaft-w stopped pve2 │ </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> └───────────────────────────────────────────────────────┘ </text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+31
View File
@@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer 101</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Tastenbelegung </text>
<text x="14.0" y="93.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Tab zwischen den Fenstern wechseln </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Pfeiltasten bewegen, Enter oeffnet Verzeichnis oder zeigt Datei </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Backspace ein Verzeichnis hoch </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Leertaste markieren, * umkehren, a alle, u keine </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F5 / c markiertes ins andere Fenster kopieren </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F3 / v Datei ansehen (Text oder Hex) </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F7 / n neues Verzeichnis (nur im lokalen Fenster) </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F6 / g Verzeichnis direkt eingeben </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F2 / m anderes Dateisystem des Snapshots waehlen </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> r neu einlesen </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F10 / q zurueck zur Snapshot-Auswahl </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Der Snapshot ist schreibgeschuetzt eingebunden - es kann nichts </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> kaputtgehen. Kopiert wird immer vom aktiven ins andere Fenster. </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Geraetedateien und Sockets werden uebersprungen, symbolische </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Verweise bleiben Verweise. </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Es gibt drei Ebenen: Gastauswahl -&gt; Snapshot-Auswahl -&gt; Dateien. </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> F10 oder q geht jeweils eine Ebene zurueck; der Snapshot wird </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> dabei wieder ausgehaengt. Das rechte Fenster behaelt sein </text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Verzeichnis, auch wenn man den naechsten Snapshot oeffnet. </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> beliebige Taste = zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer 101</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-explorer - VM 101 (db01) - Snapshot auto-taeglich-20260809-023000 </text>
<rect x="14.0" y="62.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="75.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> VM 101 (db01) @ auto-taeglich-20260809-023000 </text>
<text x="521.4" y="75.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│ Lokaler Rechner </text>
<text x="14.0" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">/run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv/du… </text>
<text x="521.4" y="93.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│/root </text>
<text x="14.0" y="111.3" fill="#06989a" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="521.4" y="111.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="111.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="14.0" y="129.3" fill="#fce94f" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">*db01-20260808-0200.sql.gz 3.8G 08.08.26 02:42</text>
<text x="521.4" y="129.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="129.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> holen &lt;DIR&gt; 06.08.26 11:42</text>
<rect x="14.0" y="134.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="147.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> db01-20260809-0200.sql.gz 3.9G 09.08.26 02:30</text>
<text x="521.4" y="147.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="147.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> werkzeuge &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> kunden-export.csv 84.3M 07.08.26 07:42│ notizen.md 3.1K 06.08.26 11:42 </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="507.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 4 Eintraege 1 markiert │ 4 Eintraege </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> /run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv/dumps/db01-20260809-0200.sql.gz </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#cc0000"/>
<text x="14.0" y="597.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> db01-20260808-0200.sql.gz (3.8G) nach /root kopieren? [j/n] </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> F3 Ansehen | F7 Neuer Ordner | F2 Laufwerk | F10 Zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

@@ -0,0 +1,53 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer 101</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-explorer - VM 101 (db01) - Snapshot auto-taeglich-20260809-023000 </text>
<rect x="14.0" y="62.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="75.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> VM 101 (db01) @ auto-taeglich-20260809-023000 </text>
<text x="521.4" y="75.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│ Lokaler Rechner </text>
<text x="14.0" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">/run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv/du… </text>
<text x="521.4" y="93.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│/root </text>
<text x="14.0" y="111.3" fill="#06989a" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="521.4" y="111.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="111.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="14.0" y="129.3" fill="#fce94f" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">*db01-20260808-0200.sql.gz 3.8G 08.08.26 02:42</text>
<text x="521.4" y="129.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="129.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> holen &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="147.3" fill="#fce94f" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">*db01-20260809-0200.sql.gz 3.9G 09.08.26 02:30</text>
<text x="521.4" y="147.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="147.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> werkzeuge &lt;DIR&gt; 06.08.26 11:42</text>
<rect x="14.0" y="152.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="165.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> kunden-export.csv 84.3M 07.08.26 07:42</text>
<text x="521.4" y="165.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│ notizen.md 3.1K 06.08.26 11:42 </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="507.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 4 Eintraege 2 markiert │ 4 Eintraege </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> /run/pvesnap/mnt/101-auto-taeglich-20260809-023000/srv/dumps/kunden-export.csv </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Tab Fenster | Enter Oeffnen | Leer Markieren | F5 Kopieren </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> F3 Ansehen | F7 Neuer Ordner | F2 Laufwerk | F10 Zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-explorer</text>
<text x="14.0" y="75.3" fill="#d3d7cf" textLength="86.0" lengthAdjust="spacing" xml:space="preserve"> ┌─</text>
<text x="100.0" y="75.3" fill="#d3d7cf" font-weight="bold" textLength="447.2" lengthAdjust="spacing" xml:space="preserve"> Welcher Snapshot? (Esc = zurueck zur Gastauswahl) </text>
<text x="547.2" y="75.3" fill="#d3d7cf" textLength="481.6" lengthAdjust="spacing" xml:space="preserve">──────────────────────────────────────────────┐ </text>
<text x="14.0" y="93.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="86.0" lengthAdjust="spacing" xml:space="preserve"></text>
<rect x="100.0" y="98.0" width="834.6" height="18.4" fill="#06989a"/>
<text x="100.0" y="111.3" fill="#2e3436" textLength="834.2" lengthAdjust="spacing" xml:space="preserve"> auto-stuendlich-20260809-100000 09.08.2026 10:00 pvesnap | Gruppe: stuendlich | erstellt… </text>
<text x="934.2" y="111.3" fill="#d3d7cf" textLength="94.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-090000 09.08.2026 09:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-080000 09.08.2026 08:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-070000 09.08.2026 07:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-060000 09.08.2026 06:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-050000 09.08.2026 05:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-040000 09.08.2026 04:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-030000 09.08.2026 03:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-020000 09.08.2026 02:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-010000 09.08.2026 01:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260809-000000 09.08.2026 00:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-230000 08.08.2026 23:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-220000 08.08.2026 22:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-210000 08.08.2026 21:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-200000 08.08.2026 20:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-190000 08.08.2026 19:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-180000 08.08.2026 18:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-170000 08.08.2026 17:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-160000 08.08.2026 16:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-150000 08.08.2026 15:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-140000 08.08.2026 14:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-130000 08.08.2026 13:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="507.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-120000 08.08.2026 12:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-stuendlich-20260808-110000 08.08.2026 11:00 pvesnap | Gruppe: stuendlich | erstellt… │ </text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-taeglich-20260808-023000 08.08.2026 02:30 pvesnap | Gruppe: taeglich | erstellt: … │ </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ auto-taeglich-20260807-023000 07.08.2026 02:30 pvesnap | Gruppe: taeglich | erstellt: … │ </text>
<text x="14.0" y="579.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="597.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> └───────────────────────────────────────────────────────────────────────────────────────────────────┘ </text>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → einrichten</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Wiederherstellung einrichten - VM 101 (db01) </text>
<rect x="14.0" y="80.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="93.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: recovery - mit Netz, gleiche Identitaet </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Netzwerk: automatisch (nach Modus) </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Arbeitsspeicher: wenn im Snapshot vorhanden </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke: werkzeuge </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> SPICE-Anzeige: nein - nur noVNC im Browser </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> USB-Weiterleitung: keine </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Neue VMID: 9103 </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Node: pve1 </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Danach starten: ja </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> live = zum Hineinschauen. recovery = die Maschine wirklich wieder in Betrieb nehmen. </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Pfeile Auswaehlen | Enter/Leer Aendern | v Laufwerke verwalten | F10 Anlegen | q Abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Wiederherstellung VM 9101 </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve">Maschine: VM 9101 auf pve1</text>
<text x="31.2" y="111.3" fill="#4e9a06" textLength="172.0" lengthAdjust="spacing" xml:space="preserve">Zustand: running</text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Herkunft: VM 101 @ auto-stuendlich-20260809-100000 </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Angelegt: 09.08.2026 11:04 </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Konsole im Browser: </text>
<text x="31.2" y="237.3" fill="#c4a000" textLength="713.8" lengthAdjust="spacing" xml:space="preserve"> https://10.20.0.11:8006/?console=kvm&amp;novnc=1&amp;vmid=9101&amp;node=pve1&amp;resize=off&amp;cmd=</text>
<text x="31.2" y="255.3" fill="#c4a000" textLength="464.4" lengthAdjust="spacing" xml:space="preserve"> SPICE (remote-viewer): pvesnap-recovery spice 9101</text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Der Arbeitsspeicher des Snapshots wurde geladen - beim ersten Start lief die Maschine genau weiter. </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke: </text>
<text x="31.2" y="345.3" fill="#4e9a06" textLength="335.4" lengthAdjust="spacing" xml:space="preserve"> dumps eingesteckt als scsi1</text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> e = auswerfen / einklinken, im laufenden Betrieb </text>
<text x="31.2" y="399.3" fill="#c4a000" textLength="455.8" lengthAdjust="spacing" xml:space="preserve">Datentraeger: Linked Clone - haengt am Quell-Snapshot</text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Belegt nur, was seither geschrieben wurde. Der Quell-Snapshot ist dafuer unloeschbar. </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Fuer den Dauerbetrieb mit f loesen (kopiert die Daten wirklich). </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> s Starten | h Herunterfahren | e Auswerfen/Einklinken | v Laufwerke </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> p SPICE/USB | f Loesen | x Verwerfen | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-recovery </text>
<text x="340.8" y="345.3" fill="#d3d7cf" font-weight="bold" textLength="352.6" lengthAdjust="spacing" xml:space="preserve">Ermittle, wieviel kopiert werden muss ...</text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#cc0000"/>
<text x="14.0" y="597.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> 2.4G kopieren und dauerhaft loesen? [j/n] </text>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Neustart noetig </text>
<rect x="31.2" y="80.0" width="619.6" height="18.4" fill="#cc0000"/>
<text x="31.2" y="93.3" fill="#eeeeec" font-weight="bold" textLength="619.2" lengthAdjust="spacing" xml:space="preserve">Dafuer muss VM 9101 neu starten - die Grafikkarte laesst sich im Betrieb</text>
<rect x="31.2" y="98.0" width="129.4" height="18.4" fill="#cc0000"/>
<text x="31.2" y="111.3" fill="#eeeeec" font-weight="bold" textLength="129.0" lengthAdjust="spacing" xml:space="preserve">nicht wechseln.</text>
<rect x="31.2" y="116.0" width="636.8" height="18.4" fill="#cc0000"/>
<text x="31.2" y="129.3" fill="#eeeeec" font-weight="bold" textLength="636.4" lengthAdjust="spacing" xml:space="preserve">Gleich kannst du waehlen, wie: entweder du faehrst im Gast selbst herunter</text>
<rect x="31.2" y="134.0" width="628.2" height="18.4" fill="#cc0000"/>
<text x="31.2" y="147.3" fill="#eeeeec" font-weight="bold" textLength="627.8" lengthAdjust="spacing" xml:space="preserve">und pvesnap startet ihn danach wieder, oder Proxmox versucht es per ACPI.</text>
<rect x="31.2" y="152.0" width="611.0" height="18.4" fill="#cc0000"/>
<text x="31.2" y="165.3" fill="#eeeeec" font-weight="bold" textLength="610.6" lengthAdjust="spacing" xml:space="preserve">Der erste Weg ist der verlaesslichere - Windows ignoriert den Aus-Knopf</text>
<rect x="31.2" y="170.0" width="43.4" height="18.4" fill="#cc0000"/>
<text x="31.2" y="183.3" fill="#eeeeec" font-weight="bold" textLength="43.0" lengthAdjust="spacing" xml:space="preserve">gern.</text>
<rect x="31.2" y="188.0" width="611.0" height="18.4" fill="#cc0000"/>
<text x="31.2" y="201.3" fill="#eeeeec" font-weight="bold" textLength="610.6" lengthAdjust="spacing" xml:space="preserve">Ein Neustart von innen im Gast wuerde nichts bringen - dabei setzt sich</text>
<rect x="31.2" y="206.0" width="602.4" height="18.4" fill="#cc0000"/>
<text x="31.2" y="219.3" fill="#eeeeec" font-weight="bold" textLength="602.0" lengthAdjust="spacing" xml:space="preserve">nur die Maschine zurueck, der QEMU-Prozess laeuft weiter mit der alten</text>
<rect x="31.2" y="224.0" width="103.6" height="18.4" fill="#cc0000"/>
<text x="31.2" y="237.3" fill="#eeeeec" font-weight="bold" textLength="103.2" lengthAdjust="spacing" xml:space="preserve">Grafikkarte.</text>
<rect x="31.2" y="242.0" width="585.2" height="18.4" fill="#cc0000"/>
<text x="31.2" y="255.3" fill="#eeeeec" font-weight="bold" textLength="584.8" lengthAdjust="spacing" xml:space="preserve">ACHTUNG: der geladene Arbeitsspeicher ist danach endgueltig weg. Die</text>
<rect x="31.2" y="260.0" width="585.2" height="18.4" fill="#cc0000"/>
<text x="31.2" y="273.3" fill="#eeeeec" font-weight="bold" textLength="584.8" lengthAdjust="spacing" xml:space="preserve">Maschine bootet kalt, offene Programme und ungespeicherte Daten sind</text>
<rect x="31.2" y="278.0" width="77.8" height="18.4" fill="#cc0000"/>
<text x="31.2" y="291.3" fill="#eeeeec" font-weight="bold" textLength="77.4" lengthAdjust="spacing" xml:space="preserve">verloren.</text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> j = trotzdem | andere Taste = abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Stelle Anzeige um </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="318.2" lengthAdjust="spacing" xml:space="preserve">Setze usb0=spice, usb1=spice, vga=qxl</text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="223.6" lengthAdjust="spacing" xml:space="preserve"> ┌─</text>
<text x="237.6" y="273.3" fill="#d3d7cf" font-weight="bold" textLength="266.6" lengthAdjust="spacing" xml:space="preserve"> Wie soll VM 9101 neu starten? </text>
<text x="504.2" y="273.3" fill="#d3d7cf" textLength="524.6" lengthAdjust="spacing" xml:space="preserve">───────────────────────────────────┐ </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="223.6" lengthAdjust="spacing" xml:space="preserve"></text>
<rect x="237.6" y="296.0" width="559.4" height="18.4" fill="#06989a"/>
<text x="237.6" y="309.3" fill="#2e3436" textLength="559.0" lengthAdjust="spacing" xml:space="preserve"> Ich fahre im Gast herunter - pvesnap wartet und startet dann </text>
<text x="796.6" y="309.3" fill="#d3d7cf" textLength="232.2" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ Proxmox herunterfahren lassen (ACPI) - Windows blockt das oft │ </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ Gar nicht - spaeter selbst │ </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> └───────────────────────────────────────────────────────────────────┘ </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> bitte warten ... </text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → einrichten</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Wiederherstellung einrichten - VM 101 (db01) </text>
<rect x="14.0" y="80.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="93.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live - abgeschottet, ohne Netzwerk </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Netzwerk: automatisch (nach Modus) </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Arbeitsspeicher: wenn im Snapshot vorhanden </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke: werkzeuge </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> SPICE-Anzeige: nein - nur noVNC im Browser </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> USB-Weiterleitung: keine </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Neue VMID: 9103 </text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Node: pve1 </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Danach starten: ja </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> live = zum Hineinschauen. recovery = die Maschine wirklich wieder in Betrieb nehmen. </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Pfeile Auswaehlen | Enter/Leer Aendern | v Laufwerke verwalten | F10 Anlegen | q Abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+37
View File
@@ -0,0 +1,37 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Wiederherstellung VM 9101 </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve">Maschine: VM 9101 auf pve1</text>
<text x="31.2" y="111.3" fill="#4e9a06" textLength="172.0" lengthAdjust="spacing" xml:space="preserve">Zustand: running</text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Herkunft: VM 101 @ auto-stuendlich-20260809-100000 </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Angelegt: 09.08.2026 11:04 </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Konsole im Browser: </text>
<text x="31.2" y="237.3" fill="#c4a000" textLength="713.8" lengthAdjust="spacing" xml:space="preserve"> https://10.20.0.11:8006/?console=kvm&amp;novnc=1&amp;vmid=9101&amp;node=pve1&amp;resize=off&amp;cmd=</text>
<text x="31.2" y="255.3" fill="#c4a000" textLength="464.4" lengthAdjust="spacing" xml:space="preserve"> SPICE (remote-viewer): pvesnap-recovery spice 9101</text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"> Der Arbeitsspeicher des Snap┌─</text>
<text x="289.2" y="291.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve"> SPICE-Anzeige (jetzt: aus) </text>
<text x="538.6" y="291.3" fill="#d3d7cf" textLength="490.2" lengthAdjust="spacing" xml:space="preserve">─────────────────────────┐ genau weiter. </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke: │ </text>
<rect x="289.2" y="314.0" width="456.2" height="18.4" fill="#06989a"/>
<text x="289.2" y="327.3" fill="#2e3436" textLength="455.8" lengthAdjust="spacing" xml:space="preserve"> ein - zusaetzlich remote-viewer, noetig fuer USB </text>
<text x="745.0" y="327.3" fill="#d3d7cf" textLength="283.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="31.2" y="345.3" fill="#4e9a06" textLength="240.8" lengthAdjust="spacing" xml:space="preserve"> dumps eingesteck</text>
<text x="272.0" y="345.3" fill="#d3d7cf" textLength="756.8" lengthAdjust="spacing" xml:space="preserve">│ aus - nur noVNC im Browser │ </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> e = auswerfen / einklinke│ │ </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> └───────────────────────────────────────────────────────┘ </text>
<text x="31.2" y="399.3" fill="#c4a000" textLength="455.8" lengthAdjust="spacing" xml:space="preserve">Datentraeger: Linked Clone - haengt am Quell-Snapshot</text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Belegt nur, was seither geschrieben wurde. Der Quell-Snapshot ist dafuer unloeschbar. </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Fuer den Dauerbetrieb mit f loesen (kopiert die Daten wirklich). </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> s Starten | h Herunterfahren | e Auswerfen/Einklinken | v Laufwerke </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> p SPICE/USB | f Loesen | x Verwerfen | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-recovery 1.0.0 - Snapshots als Maschine starten </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="670.8" lengthAdjust="spacing" xml:space="preserve">Maschine Zustand Node Herkunft Modus</text>
<rect x="14.0" y="98.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> VM 9101 running pve1 VM 101 @ auto-stuendlich-20… live </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> VM 9102 stopped pve2 VM 105 @ auto-taeglich-2026… recover </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Enter Details | n Neu | s Starten | h Herunterfahren | x Verwerfen | v Laufwerke | r Aktualisieren | q Beenden </text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+47
View File
@@ -0,0 +1,47 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Wiederherstellung VM 9101 </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve">Maschine: VM 9101 auf pve1</text>
<text x="31.2" y="111.3" fill="#4e9a06" textLength="172.0" lengthAdjust="spacing" xml:space="preserve">Zustand: running</text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Herkunft: VM 101 @ auto-stuendlich-20260809-100000 </text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live </text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Angelegt: 09.08.2026 11:04 </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Konsole im Browser: </text>
<text x="31.2" y="237.3" fill="#c4a000" textLength="713.8" lengthAdjust="spacing" xml:space="preserve"> https://10.20.0.11:8006/?console=kvm&amp;novnc=1&amp;vmid=9101&amp;node=pve1&amp;resize=off&amp;cmd=</text>
<text x="31.2" y="255.3" fill="#c4a000" textLength="464.4" lengthAdjust="spacing" xml:space="preserve"> SPICE (remote-viewer): pvesnap-recovery spice 9101</text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="361.2" lengthAdjust="spacing" xml:space="preserve"> ┌─</text>
<text x="375.2" y="273.3" fill="#d3d7cf" font-weight="bold" textLength="266.6" lengthAdjust="spacing" xml:space="preserve"> USB-Weiterleitung (jetzt: 0) </text>
<text x="641.8" y="273.3" fill="#d3d7cf" textLength="387.0" lengthAdjust="spacing" xml:space="preserve">───┐ </text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"> Der Arbeitsspeicher des Snap┌─</text>
<text x="289.2" y="291.3" fill="#d3d7cf" font-weight="bold" textLength="68.8" lengthAdjust="spacing" xml:space="preserve"> SPICE-A</text>
<text x="358.0" y="291.3" fill="#d3d7cf" textLength="670.8" lengthAdjust="spacing" xml:space="preserve">│ │─────────┐ genau weiter. </text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="361.2" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<rect x="375.2" y="296.0" width="284.2" height="18.4" fill="#06989a"/>
<text x="375.2" y="309.3" fill="#2e3436" textLength="283.8" lengthAdjust="spacing" xml:space="preserve"> keine </text>
<text x="659.0" y="309.3" fill="#d3d7cf" textLength="369.8" lengthAdjust="spacing" xml:space="preserve"> │ │ </text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="275.2" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke: │ </text>
<rect x="289.2" y="314.0" width="69.2" height="18.4" fill="#06989a"/>
<text x="289.2" y="327.3" fill="#2e3436" textLength="68.8" lengthAdjust="spacing" xml:space="preserve"> ein - </text>
<text x="358.0" y="327.3" fill="#d3d7cf" textLength="318.2" lengthAdjust="spacing" xml:space="preserve">│ 1 Anschluss │</text>
<rect x="676.2" y="314.0" width="69.2" height="18.4" fill="#06989a"/>
<text x="676.2" y="327.3" fill="#2e3436" textLength="68.8" lengthAdjust="spacing" xml:space="preserve">r USB </text>
<text x="745.0" y="327.3" fill="#d3d7cf" textLength="283.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="31.2" y="345.3" fill="#4e9a06" textLength="240.8" lengthAdjust="spacing" xml:space="preserve"> dumps eingesteck</text>
<text x="272.0" y="345.3" fill="#d3d7cf" textLength="756.8" lengthAdjust="spacing" xml:space="preserve">│ aus - │ 2 Anschluesse │ │ </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> e = auswerfen / einklinke│ │ 4 Anschluesse │ │ </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> └─────────│ │─────────┘ </text>
<text x="31.2" y="399.3" fill="#c4a000" textLength="326.8" lengthAdjust="spacing" xml:space="preserve">Datentraeger: Linked Clone - haengt am</text>
<text x="358.0" y="399.3" fill="#d3d7cf" textLength="670.8" lengthAdjust="spacing" xml:space="preserve">└───────────────────────────────────┘ </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Belegt nur, was seither geschrieben wurde. Der Quell-Snapshot ist dafuer unloeschbar. </text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Fuer den Dauerbetrieb mit f loesen (kopiert die Daten wirklich). </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> s Starten | h Herunterfahren | e Auswerfen/Einklinken | v Laufwerke </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> p SPICE/USB | f Loesen | x Verwerfen | q zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-recovery 1.0.0 - Snapshots als Maschine starten </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="670.8" lengthAdjust="spacing" xml:space="preserve">Maschine Zustand Node Herkunft Modus</text>
<rect x="14.0" y="98.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> VM 9101 running pve1 VM 101 @ auto-stuendlich-20… live </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> VM 9102 stopped pve2 VM 105 @ auto-taeglich-2026… recover </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#cc0000"/>
<text x="14.0" y="597.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> VM 9101 wirklich verwerfen? Alle Aenderungen darin sind weg. [j/n] </text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → einrichten</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Vorhaben pruefen </text>
<text x="14.0" y="93.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Quelle: VM 101 (db01) auf pve1 </text>
<text x="14.0" y="111.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Snapshot: auto-stuendlich-20260809-100000 </text>
<text x="31.2" y="129.3" fill="#d3d7cf" font-weight="bold" textLength="249.4" lengthAdjust="spacing" xml:space="preserve">Neu: VM 9103 auf pve1</text>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Betriebsart: live - abgeschottet </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Datentraeger (Copy-on-Write-Klone, das Original bleibt unberuehrt): </text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> scsi0 ceph-vm:vm-101-disk-0 400G </text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Netzwerk: Karte vorhanden, Leitung abgeklemmt </text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> net0 virtio=BC:24:11:00:65:40,bridge=vmbr0,firewall=1 </text>
<text x="31.2" y="273.3" fill="#4e9a06" textLength="593.4" lengthAdjust="spacing" xml:space="preserve">Arbeitsspeicher: wird geladen (32.0G) - laeuft weiter statt zu booten</text>
<text x="31.2" y="291.3" fill="#4e9a06" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">Transfer: werkzeuge 4.0G im Gast als WERKZEUGE</text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> - Netzwerkkarte bleibt vorhanden, aber abgeklemmt (link_down): mit </text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> geladenem Arbeitsspeicher darf sich die Geraeteausstattung nicht aendern. </text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> - Der Arbeitsspeicher (32.0G) wird kopiert; die Maschine laeuft danach </text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> genau dort weiter, wo sie beim Snapshot stand. </text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> - Proxmox gibt den kopierten Arbeitsspeicher nach dem ersten Start wieder </text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> frei - ein spaeterer Neustart bootet dann kalt. </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> j = anlegen | n = abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → einrichten</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Welche Transfer-Laufwerke anhaengen? </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="516.0" lengthAdjust="spacing" xml:space="preserve"> Name Groesse Zustand Wofuer</text>
<rect x="14.0" y="98.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> [ ] dumps 20.0G in Benutzung von 9101 Datenbank-Dumps und Expor… </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> [x] werkzeuge 4.0G frei Skripte, Treiber, Install… </text>
<rect x="14.0" y="134.0" width="1006.6" height="18.4" fill="#cc0000"/>
<text x="14.0" y="147.3" fill="#d3d7cf" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> [ ] austausch 8.0G am Host eingehaengt Alles andere </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> Belegte Laufwerke lassen sich nicht anhaengen - erst dort aushaengen. </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Leertaste Aus/Abwaehlen | Enter Uebernehmen | q Abbrechen </text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → dumps</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> pvesnap-explorer - Transfer-Laufwerk dumps - beide Seiten beschreibbar </text>
<rect x="14.0" y="62.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="75.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> Transfer: dumps (DUMPS) </text>
<text x="521.4" y="75.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│ Lokaler Rechner </text>
<text x="14.0" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">/run/pvesnap/transfer/dumps </text>
<text x="521.4" y="93.3" fill="#d3d7cf" textLength="507.4" lengthAdjust="spacing" xml:space="preserve">│/root </text>
<rect x="14.0" y="98.0" width="507.8" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> ausgang &lt;DIR&gt; 29.06.26 11:42</text>
<text x="521.4" y="111.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="111.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> .. &lt;hoch&gt; </text>
<text x="14.0" y="129.3" fill="#06989a" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> eingang &lt;DIR&gt; 29.06.26 11:42</text>
<text x="521.4" y="129.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="129.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> holen &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="147.3" fill="#06989a" textLength="507.4" lengthAdjust="spacing" xml:space="preserve"> werkzeuge &lt;DIR&gt; 29.06.26 11:42</text>
<text x="521.4" y="147.3" fill="#d3d7cf" textLength="8.6" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="530.0" y="147.3" fill="#06989a" textLength="490.2" lengthAdjust="spacing" xml:space="preserve"> werkzeuge &lt;DIR&gt; 06.08.26 11:42</text>
<text x="14.0" y="165.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> LIESMICH.txt 401B 29.06.26 11:42│ notizen.md 3.1K 06.08.26 11:42 </text>
<text x="14.0" y="183.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="201.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="219.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="237.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="255.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="273.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="291.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="309.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="327.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="345.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="363.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="381.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="399.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="417.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="435.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="453.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="471.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="489.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="507.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="525.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"></text>
<text x="14.0" y="543.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> 4 Eintraege │ 4 Eintraege </text>
<text x="14.0" y="561.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> /run/pvesnap/transfer/dumps/ausgang </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Tab Fenster | Enter Oeffnen | Leer Markieren | F5 Kopieren </text>
<rect x="14.0" y="602.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="615.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> F3 Ansehen | F7 Neuer Ordner | F2 Laufwerk | F10 Zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

+17
View File
@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → Laufwerke</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke - Austausch mit der Wiederherstellung </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="559.0" lengthAdjust="spacing" xml:space="preserve">Name Groesse belegt Zustand Wofuer</text>
<rect x="14.0" y="98.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> dumps 20.0G 4.0M in Benutzung von 9101 Datenbank-Dumps und Exporte </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> werkzeuge 4.0G 4.0M frei Skripte, Treiber, Installer </text>
<text x="14.0" y="147.3" fill="#4e9a06" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> austausch 8.0G 0B am Host eingehaengt Alles andere </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Name (kurz, klein, z.B. dumps): </text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1042.8 634.0" width="1043" height="634" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, 'DejaVu Sans Mono', monospace" font-size="14.5">
<rect width="1042.8" height="634.0" rx="7" fill="#1b1e24"/>
<path d="M0 7a7 7 0 0 1 7-7h1028.8a7 7 0 0 1 7 7v23.0H0z" fill="#2b2f38"/>
<circle cx="16.0" cy="15" r="5" fill="#ed6a5e"/>
<circle cx="33.0" cy="15" r="5" fill="#f4bf4f"/>
<circle cx="50.0" cy="15" r="5" fill="#61c554"/>
<text x="521.4" y="20" fill="#9aa3b2" font-size="12" text-anchor="middle">root@pve1 — pvesnap-recovery → Laufwerke</text>
<rect x="14.0" y="44.0" width="1006.6" height="18.4" fill="#3465a4"/>
<text x="14.0" y="57.3" fill="#eeeeec" font-weight="bold" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Transfer-Laufwerke - Austausch mit der Wiederherstellung </text>
<text x="31.2" y="93.3" fill="#d3d7cf" font-weight="bold" textLength="559.0" lengthAdjust="spacing" xml:space="preserve">Name Groesse belegt Zustand Wofuer</text>
<rect x="14.0" y="98.0" width="1006.6" height="18.4" fill="#06989a"/>
<text x="14.0" y="111.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> dumps 20.0G 4.0M in Benutzung von 9101 Datenbank-Dumps und Exporte </text>
<text x="14.0" y="129.3" fill="#d3d7cf" textLength="1014.8" lengthAdjust="spacing" xml:space="preserve"> werkzeuge 4.0G 4.0M frei Skripte, Treiber, Installer </text>
<text x="14.0" y="147.3" fill="#4e9a06" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> austausch 8.0G 0B am Host eingehaengt Alles andere </text>
<rect x="14.0" y="584.0" width="1006.6" height="18.4" fill="#d3d7cf"/>
<text x="14.0" y="597.3" fill="#2e3436" textLength="1006.2" lengthAdjust="spacing" xml:space="preserve"> Enter Oeffnen | n Neu | u Aushaengen | l Loeschen | r Aktualisieren | q Zurueck </text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+164
View File
@@ -0,0 +1,164 @@
# Der Explorer
```bash
pvesnap-explorer # Gast und Snapshot auswählen
pvesnap-explorer 101 # Snapshot von VM 101 auswählen
pvesnap-explorer 101 <snapname> # direkt öffnen
```
Braucht `root` — Snapshots einbinden und mounten geht nicht anders.
---
## Drei Ebenen
<div class="ablauf" markdown>
**Gastauswahl****Snapshot-Auswahl****Dateien**
</div>
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.
### 1. Welcher Gast?
![Gastauswahl](../bilder/explorer-gastauswahl.svg)
Alle VMs und Container des Clusters, mit Zustand und Node. Ein gestoppter Gast
ist kein Hindernis — sein Snapshot lässt sich genauso lesen.
### 2. Welcher Snapshot?
![Snapshot-Auswahl](../bilder/explorer-snapshotauswahl.svg)
Neueste zuerst, mit Datum und Beschreibung. Die Beschreibung ist der Grund,
warum es sich lohnt, sie in der Konfiguration ordentlich zu setzen — hier sucht
man später danach.
Halbfertige Einträge aus abgebrochenen Läufen werden ausgeblendet; wenn welche
da sind, sagt eine Meldung, wie man sie loswird.
### 3. Die Dateien
![Der Explorer mit zwei Fenstern](../bilder/explorer-fenster.svg)
Links der Snapshot, rechts der lokale Rechner. Oben steht, welcher Gast und
welcher Snapshot offen ist, unten der volle Pfad des markierten Eintrags.
---
## Tastenbelegung
| Taste | |
|---|---|
| ++tab++ | Fenster wechseln |
| ++up++ ++down++ | bewegen |
| ++enter++ | Verzeichnis öffnen oder Datei ansehen |
| ++backspace++ ++left++ | ein Verzeichnis hoch |
| ++home++ ++end++ | Anfang / Ende |
| ++page-up++ ++page-down++ | seitenweise |
| ++space++ | markieren |
| ++asterisk++ | Markierung umkehren |
| ++a++ / ++u++ | alle markieren / keine |
| ++f5++ oder ++c++ | Markiertes ins andere Fenster kopieren |
| ++f3++ oder ++v++ | Datei ansehen (Text oder Hex) |
| ++f7++ oder ++n++ | neues Verzeichnis (nur im lokalen Fenster) |
| ++f6++ oder ++g++ | Verzeichnis direkt eingeben |
| ++f2++ oder ++m++ | anderes Dateisystem des Snapshots wählen |
| ++r++ | neu einlesen |
| ++f10++ oder ++q++ | eine Ebene zurück |
| ++question++ oder ++f1++ | Hilfe |
Die Hilfe ist jederzeit erreichbar:
![Die Tastenhilfe](../bilder/explorer-hilfe.svg)
---
## Kopieren
Markieren mit ++space++ — markierte Einträge stehen in Gelb:
![Markierte Dateien](../bilder/explorer-markiert.svg)
Dann ++f5++:
![Der Kopierdialog](../bilder/explorer-kopieren.svg)
Kopiert wird **immer vom aktiven ins andere Fenster**. In den Snapshot hinein
geht nichts — er ist schreibgeschützt eingebunden.
Während des Kopierens läuft ein Fortschrittsbalken; ++esc++ bricht ab. Bereits
kopierte Dateien bleiben dabei liegen, die gerade laufende wird entfernt, damit
kein halbes Stück zurückbleibt.
Existiert eine Datei am Ziel schon, wird gefragt:
| Antwort | |
|---|---|
| `ja` | diese überschreiben |
| `nein` | diese überspringen |
| `alle` | alles Weitere überschreiben, nicht mehr fragen |
| `keine` | alles Weitere überspringen |
**Was übersprungen wird:** Gerätedateien, Sockets und FIFOs. Symbolische
Verweise bleiben Verweise — sie werden nicht aufgelöst.
!!! tip "Aus mehreren Snapshots ins selbe Ziel"
Das rechte Fenster behält sein Verzeichnis, auch wenn man zurückgeht und
den nächsten Snapshot öffnet. Damit lassen sich mehrere Stände bequem
nebeneinander sammeln — etwa dieselbe Konfigurationsdatei von gestern,
vorgestern und vom Monatsersten.
---
## Dateien ansehen
++f3++ zeigt eine Datei, ohne sie zu kopieren:
![Eine Datei ansehen](../bilder/explorer-ansehen.svg)
Text wird als Text dargestellt, alles andere als Hexdump. Das reicht meist
schon, um die Frage „ist das die richtige Fassung?" zu beantworten — ohne die
Datei erst auf den Host zu holen.
---
## Mehrere Dateisysteme
Eine VM hat oft mehr als eine Partition: `/` und `/boot`, dazu vielleicht ein
eigenes Datenlaufwerk. Hat der Snapshot mehrere brauchbare Dateisysteme, fragt
der Explorer beim Öffnen, welches er zeigen soll.
Wechseln geht später mit ++f2++, ohne den Snapshot zu schließen.
!!! note "Was nicht auftaucht"
Swap-Partitionen, EFI-Partitionen ohne lesbaren Inhalt und alles, was der
Kernel nicht kennt. LVM innerhalb der VM wird erkannt und aktiviert;
verschlüsselte Volumes bleiben verschlüsselt.
---
## Aufrufparameter
| Parameter | Vorgabe | Bedeutung |
|---|---|---|
| `vmid` | — | Gast direkt vorwählen |
| `snapshot` | — | Snapshot direkt vorwählen |
| `-l`, `--local <pfad>` | aktuelles Verzeichnis | Startverzeichnis im rechten Fenster |
| `-p`, `--path <pfad>` | — | beliebiges Verzeichnis öffnen, ohne Proxmox zu fragen |
| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden |
| `-v`, `--verbose` | — | ausführliches Protokoll |
| `-V`, `--version` | — | Version anzeigen |
```bash
pvesnap-explorer --local /srv/restore # rechtes Fenster startet dort
pvesnap-explorer --path /mnt/x # ohne Proxmox, zum Ausprobieren
pvesnap-explorer --cleanup # nach einem Absturz aufräumen
```
`--path` ist auch der Weg, den Commander auf etwas ganz anderes loszulassen —
ein gemountetes Backup etwa. Dann braucht es weder Proxmox noch root.
+95
View File
@@ -0,0 +1,95 @@
# Daten zurückholen
Ein Snapshot ist kein Verzeichnis. Er liegt als Blockgerät oder als Eintrag im
qcow2-Kopf im Storage, und dort kann man nicht einfach hineinsehen.
pvesnap nimmt einem das ab: Der Snapshot wird **schreibgeschützt** eingebunden
und gemountet, und danach ist er ein ganz gewöhnlicher Pfad.
---
## Drei Wege hinein
| | wofür |
|---|---|
| [**Der Explorer**](explorer.md) | Auf dem Host, im Terminal. Zwei Fenster wie im Midnight Commander. Der schnellste Weg, wenn man ohnehin per SSH angemeldet ist. |
| [**Die Web-Oberfläche**](web.md) | Im Browser, auch vom Arbeitsplatz. Anmeldung mit den Proxmox-Benutzern. Verzeichnisse kommen als ZIP. |
| [**Als Maschine starten**](../wiederherstellen/index.md) | Wenn Dateien nicht reichen — eine Datenbank etwa wird erst brauchbar, wenn ihr Server läuft und selbst einen Dump schreibt. |
Die ersten beiden lesen nur. Sie können nichts kaputt machen.
---
## Was dabei im Hintergrund passiert
Je nach Storage geht es unterschiedlich hinein:
| Storage | Weg |
|---|---|
| `rbd` (Ceph) | `rbd map pool/image@snap`; bei nicht unterstützten Image-Features über `rbd-nbd` |
| `zfspool` | Container direkt über `.zfs/snapshot/…`, VMs über einen temporären Klon |
| `lvmthin`, `lvm` | die Snapshot-LV `snap_<volume>_<snapname>` aktivieren |
| `dir`, `nfs`, `cifs` | `qemu-nbd --load-snapshot` — nur bei qcow2 |
Danach werden die Partitionen erkannt und gemountet, und zwar mit
`ro,noload` bzw. `ro,norecovery,nouuid`.
!!! info "Warum diese Optionen wichtig sind"
Der Snapshot einer **laufenden** Maschine hat fast immer ein unsauberes
Journal — die VM war ja mitten in der Arbeit. Ohne `noload` bzw.
`norecovery` würde der Kernel das Journal abspielen wollen, also
**schreiben**. In einen Snapshot, der schreibgeschützt sein soll.
Deshalb wird bewusst darauf verzichtet. Die Folge: Die letzten Sekunden vor
dem Snapshot können in den Dateien fehlen, auch wenn sie im Journal stünden.
Für eine Datenbank ist das der Grund, den Weg über
[`pvesnap-recovery`](../wiederherstellen/index.md) zu nehmen — dort startet
der Datenbankserver und räumt selbst auf.
---
## Aufräumen
Beim Beenden wird alles wieder ausgehängt — automatisch, auch beim Verlassen
über ++q++ oder ++ctrl+c++.
Bleibt nach einem Absturz etwas hängen:
```bash
pvesnap-explorer --cleanup
pvesnap-web --cleanup
```
Beide räumen dieselben Reste weg: Einbindungen unter `/run/pvesnap/`,
`rbd`-Zuordnungen, NBD-Geräte, temporäre ZFS-Klone.
Nachsehen, ob wirklich nichts mehr liegt:
```bash
findmnt | grep pvesnap
rbd showmapped
losetup -a
ls /run/pvesnap/
```
---
## Was nicht geht
!!! warning "Schreiben"
In einen Snapshot hinein geht nichts. Kein Kopieren, kein Löschen, kein
Umbenennen. Das ist keine Vorsichtsmaßnahme, sondern eine Eigenschaft der
Sache: Ein Snapshot ist ein festgehaltener Stand.
Wer einen Stand **ändern** will, um daraus weiterzuarbeiten, braucht
[`pvesnap-recovery`](../wiederherstellen/index.md) — dort entsteht ein Klon,
der voll beschreibbar ist.
!!! warning "Verschlüsselte Dateisysteme"
LUKS, BitLocker oder eine verschlüsselte Datenbankpartition bleiben auch im
Snapshot verschlüsselt. Es gibt keinen Weg daran vorbei — die Daten müssen
im laufenden Gast entschlüsselt werden. Auch das ist ein Fall für
`pvesnap-recovery`.
+223
View File
@@ -0,0 +1,223 @@
# Die Web-Oberfläche
`pvesnap-web` zeigt Snapshots im Browser. Gedacht für die Fälle, in denen der
[Explorer](explorer.md) nicht passt: Jemand ohne SSH-Zugang soll etwas
herausbekommen, oder die Dateien sollen direkt auf dem Arbeitsplatzrechner
landen statt auf dem Hypervisor.
Sie liest **ausschließlich**. Ändern oder löschen kann sie nichts.
---
## Als Dienst einrichten
```bash
./install.sh --with-webexplorer --port 8823
```
Danach läuft die Oberfläche dauerhaft auf `http://<host>:8823/` und startet mit
dem System.
```bash
./install.sh --with-webexplorer --port 8443 --bind 10.0.0.5 # nur auf einer IP
./install.sh --with-webexplorer --port 8823 --no-start # erst später starten
```
Alle Aufrufparameter des Dienstes stehen in **`/etc/pvesnap/web.conf`**:
```
PVESNAP_WEB_ARGS=--bind 0.0.0.0 --port 8823 --auth pve
```
Dort lässt sich nachträglich alles ändern — Port, Adresse, Anmeldeart, Rechte:
```bash
nano /etc/pvesnap/web.conf
systemctl restart pvesnap-web
```
### Dienst verwalten
```bash
systemctl status pvesnap-web # läuft er?
systemctl restart pvesnap-web # nach Änderungen in web.conf
systemctl stop pvesnap-web # anhalten (hängt offene Snapshots aus)
systemctl disable pvesnap-web # nicht mehr automatisch starten
journalctl -u pvesnap-web -f # Protokoll, auch fehlgeschlagene Anmeldungen
```
---
## Von Hand starten
Für einen einmaligen Einsatz, ohne etwas zu installieren:
```bash
pvesnap-web # Gast und Snapshot im Browser wählen
pvesnap-web 101 # nur diesen Gast anbieten
pvesnap-web 101 auto-taeglich-20260809-023000 # Snapshot gleich öffnen
```
++ctrl+c++ beendet und hängt den Snapshot wieder aus.
---
## Wer darf was sehen?
Das ist der Punkt, an dem sich diese Oberfläche von einem schnell
hingeworfenen Dateiserver unterscheidet.
### Anmeldung mit den Proxmox-Benutzern
Die Oberfläche zeigt dieselbe Anmeldemaske wie Proxmox — Benutzername,
Passwort, Realm-Auswahl. Geprüft wird über `POST /access/ticket` auf der
lokalen Proxmox-API, also genau den Weg, den auch das Proxmox-Webinterface
geht. Alle Realms (`pam`, `pve`, LDAP, AD …) funktionieren damit automatisch.
Zwei-Faktor-Anmeldungen werden **abgewiesen** statt halb durchgewinkt.
### Ein Passwort allein reicht nicht
Sonst könnte jeder Proxmox-Benutzer sämtliche Dateien aller Gäste lesen.
Zusätzlich muss der Benutzer auf dem jeweiligen Gast das Recht **`VM.Snapshot`**
besitzen — geprüft auf `/`, `/vms`, `/vms/<id>` und dem Pool des Gastes.
In der Gastliste tauchen nur Gäste auf, für die das zutrifft. `root@pam` sieht
wie in Proxmox alles.
Anpassen:
```
--require-privilege VM.Backup # anderes Recht verlangen
--allow-user backup@pve # einzelne Benutzer generell zulassen
--auth token # stattdessen Schlüssel in der Adresse
--auth none # ohne Anmeldung (nur im vertrauten Netz)
```
Ohne Proxmox (`--path`) gibt es keine Benutzer — dort wird automatisch ein
Zugangsschlüssel erzeugt und beim Start als vollständige Adresse ausgegeben.
---
## Bedienung
Snapshot anklicken, durchklicken.
| | |
|---|---|
| **Einzelne Datei** | wird direkt heruntergeladen |
| **Verzeichnis** | kommt als ZIP |
| **Mehrfachauswahl** | ebenfalls als ZIP |
Die ZIP-Dateien werden **im Strom erzeugt** — es wird nichts auf dem Host
zwischengespeichert. Ein 200-GB-Verzeichnis braucht also keinen freien Platz,
nur Geduld.
---
## Parameter
| Parameter | Vorgabe | Bedeutung |
|---|---|---|
| `-P`, `--port <nummer>` | `8823` | Port, auf dem gelauscht wird |
| `-b`, `--bind <adresse>` | `0.0.0.0` | Netzwerkadresse; `127.0.0.1` = nur lokal |
| `-a`, `--auth pve\|token\|none` | `pve` | Anmeldeart |
| `--require-privilege <recht>` | `VM.Snapshot` | nötiges Recht auf dem Gast |
| `--allow-user <user@realm>` | — | dieser Benutzer darf alles (mehrfach möglich) |
| `-t`, `--token <schlüssel>` | zufällig | Zugangsschlüssel selbst vorgeben |
| `--no-token` | — | Kurzform für `--auth none` |
| `-p`, `--path <verzeichnis>` | — | beliebiges Verzeichnis statt eines Snapshots |
| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden |
| `-v`, `--verbose` | — | ausführliches Protokoll |
| `-V`, `--version` | — | Version anzeigen |
---
## Beispiele
=== "Über einen SSH-Tunnel"
Der sicherste Weg — nichts liegt im Netz offen:
```bash
# auf dem Host
pvesnap-web --bind 127.0.0.1 --port 8823
# am eigenen Rechner
ssh -L 8823:localhost:8823 root@pve
# dann im Browser: http://localhost:8823/
```
Als Dienst: in `web.conf` `--bind 127.0.0.1` setzen.
=== "Ein Team ohne Snapshot-Rechte"
```bash
pvesnap-web --port 8823 --require-privilege VM.Backup
```
Wer sichern darf, darf auch aus einer Sicherung lesen — das ist meist die
passendere Grenze.
=== "Ein einzelner Dienstleister"
```bash
pvesnap-web --port 8823 --allow-user dienstleister@pve
```
Unabhängig von den Proxmox-Rechten. Danach wieder entfernen.
=== "Schnell etwas herausgeben"
```bash
pvesnap-web --port 8823 --auth token
```
Der Zugangsschlüssel steht in der Adresse, die beim Start ausgegeben wird.
Kein Proxmox-Benutzer nötig.
=== "Ein beliebiges Verzeichnis"
```bash
pvesnap-web --path /mnt/restore --port 8823
```
Etwa ein bereits gemountetes Backup. Braucht kein Proxmox.
---
## Sicherheit
!!! danger "HTTP, nicht HTTPS"
Die Verbindung ist **unverschlüsselt**. Passwörter gehen im Klartext über
das Netz.
Über unsichere Netze deshalb immer einen SSH-Tunnel legen — siehe oben.
Innerhalb eines vertrauenswürdigen Verwaltungsnetzes ist es vertretbar, an
einer offenen Firewall-Regel nach draußen nicht.
Was sonst gilt:
* Es wird ausschließlich **gelesen**. Die Oberfläche kann nichts ändern oder
löschen.
* Pfade außerhalb des eingebundenen Snapshots werden abgewiesen — über die
Adresszeile kommt man nicht an den Rest des Hosts.
* **Fehlgeschlagene Anmeldungen** landen mit Absender-IP im Journal:
```bash
journalctl -u pvesnap-web | grep -i "anmeldung\|failed"
```
* Beim Beenden (++ctrl+c++ oder `systemctl stop pvesnap-web`) wird ein offener
Snapshot wieder ausgehängt.
!!! tip "Nicht dauerhaft laufen lassen, wenn nicht nötig"
Wer die Oberfläche nur gelegentlich braucht, installiert sie ohne
Autostart:
```bash
systemctl disable pvesnap-web
systemctl start pvesnap-web # bei Bedarf
```
+107
View File
@@ -0,0 +1,107 @@
# pvesnap
**pvesnap** legt nach Zeitplan Snapshots von Proxmox-VMs und -Containern an,
räumt alte wieder weg — und hilft danach dabei, an die Daten darin zu kommen.
Das ist der Teil, den andere Werkzeuge gern offen lassen. Einen Snapshot
anzulegen ist leicht. Ihn im Ernstfall zu *benutzen*, wenn die Datenbank
gelöscht wurde und der Chef daneben steht, ist die eigentliche Arbeit. Dafür
gibt es hier drei Wege — vom einzelnen Verzeichnis bis zur kompletten Maschine,
die läuft, als wäre nie etwas gewesen.
---
## Die vier Werkzeuge
<div class="grid cards" markdown>
- **`pvesnap`** — der Dienst
Legt Snapshots nach Zeitplan an und löscht sie nach Vorhaltezeit wieder.
Eingestellt wird alles in einer INI-Datei oder im
[Konfigurationseditor](sichern/editor.md).
- **`pvesnap-explorer`** — zwei Fenster im Terminal
Wie der Midnight Commander: links der Snapshot, rechts der Host. Dateien
herüberkopieren, fertig. → [Kapitel](holen/explorer.md)
- **`pvesnap-web`** — im Browser
Dasselbe im Webbrowser, auch vom Arbeitsplatz aus, mit den Proxmox-Benutzern
als Anmeldung. Verzeichnisse kommen als ZIP. → [Kapitel](holen/web.md)
- **`pvesnap-recovery`** — der Snapshot als laufende Maschine
Startet den Snapshot als eigenständige VM — abgeschottet zum Hineinschauen
oder mit vollem Netz als echte Wiederherstellung.
→ [Kapitel](wiederherstellen/index.md)
</div>
---
## Wo fange ich an?
| Ich will … | → |
|---|---|
| das Ganze erst einmal zum Laufen bringen | [Schnellstart](schnellstart.md) |
| es sauber installieren | [Installation](installation.md) |
| einen Zeitplan einrichten | [Sichern](sichern/index.md) |
| eine einzelne Datei zurückholen | [Der Explorer](holen/explorer.md) |
| jemandem ohne Shell-Zugang etwas herausgeben | [Die Web-Oberfläche](holen/web.md) |
| einen Datenbank-Dump aus einem Snapshot ziehen | [Wiederherstellen](wiederherstellen/index.md) |
| wissen, was ein bestimmter Parameter tut | [Nachschlagen](nachschlagen/index.md) |
| verstehen, warum etwas nicht geht | [Fehlersuche](nachschlagen/fehlersuche.md) |
---
## Das Wichtigste vorweg
!!! warning "Ein Snapshot ist kein Backup"
Snapshots liegen auf demselben Storage wie die VM. Stirbt das Storage,
sind beide weg. pvesnap ersetzt kein `vzdump` und keinen Proxmox Backup
Server — es ergänzt sie um etwas, das die nicht können: einen Stand von vor
zwanzig Minuten, in Sekunden verfügbar.
Die beiden Werkzeuge lösen unterschiedliche Probleme:
| | Backup | Snapshot |
|---|---|---|
| Schützt vor | Ausfall des Storages, Brand, Verschlüsselung | Fehlbedienung, missratenem Update, gelöschter Tabelle |
| Liegt | woanders | daneben |
| Zurück in | Stunden | Sekunden |
!!! tip "pvesnap fasst fremde Snapshots nie an"
Gelöscht wird ausschließlich, was exakt auf das eigene
[Namensschema](sichern/index.md#namensschema) passt. Ein von Hand angelegter
Snapshot `vor-update` bleibt liegen, bis du ihn selbst entfernst.
---
## Ein Blick voraus
So sieht die Übersicht aus, wenn zwei Snapshots als Maschinen laufen:
![Übersicht der Wiederherstellungen](bilder/recovery-uebersicht.svg)
Und so der Weg an die Dateien darin — links der Snapshot, rechts der Host:
![Der Explorer mit zwei Fenstern](bilder/explorer-fenster.svg)
---
## Voraussetzungen
* **Proxmox VE**, Version 7 oder neuer
* **root** auf dem Host — `pvesh` und das Einbinden von Snapshots verlangen es
* **Python 3.9+** — bringt Proxmox selbst mit
* Ein Storage, das Snapshots kann: Ceph/RBD, ZFS, LVM-thin oder qcow2 auf
`dir`/`nfs`. Was jeweils geht, steht unter
[Speicherarten](nachschlagen/speicher.md).
Der Dienst selbst braucht **keine zusätzlichen Python-Pakete**. Für die
Austauschlaufwerke werden `parted` und `exfatprogs` nachinstalliert — das
erledigt das Installationsskript.
+156
View File
@@ -0,0 +1,156 @@
# Installation
## Der übliche Weg
Auf dem Proxmox-Host als `root`:
```bash
git clone <dieses-repo> pvesnap && cd pvesnap
./install.sh # nur der Snapshot-Dienst
./install.sh --with-webexplorer --port 8823 # zusätzlich die Web-Oberfläche
./install.sh --help # alle Optionen
```
Das Skript prüft zuerst, ob es wirklich auf einem Proxmox-VE-Host läuft, und
bricht sonst ab. Wer es trotzdem will — etwa zum Ausprobieren in einer VM —
nimmt `--force`.
### Optionen
| Option | |
|---|---|
| `--with-webexplorer` | Web-Oberfläche als Dienst einrichten (**erfordert `--port`**) |
| `--port <nummer>` | Port der Web-Oberfläche |
| `--bind <adresse>` | Adresse, auf der sie lauscht (Vorgabe `0.0.0.0`) |
| `--no-start` | installieren, aber Dienste nicht starten |
| `--no-install-deps` | fehlende Werkzeuge (`parted`, `exfatprogs`) nicht nachinstallieren |
| `--force` | auch ohne erkanntes Proxmox VE installieren |
`--port` hat bewusst **keine Vorgabe**. Es gibt keine Portnummer, die auf jedem
Host frei ist, und ein stillschweigend gewählter Port wäre genau die Art
Überraschung, die man auf einem Hypervisor nicht braucht.
---
## Was installiert wird
| Pfad | Inhalt |
|---|---|
| `/usr/lib/pvesnap/` | Programmcode |
| `/usr/local/bin/pvesnap` | Dienst und Konfiguration |
| `/usr/local/bin/pvesnap-explorer` | Zwei-Fenster-Explorer |
| `/usr/local/bin/pvesnap-web` | Web-Oberfläche |
| `/usr/local/bin/pvesnap-recovery` | Snapshot als Maschine starten |
| `/etc/pvesnap/pvesnap.conf` | Konfiguration — bei Updates **nicht** überschrieben |
| `/etc/pvesnap/web.conf` | Port und Anmeldeart der Web-Oberfläche |
| `/var/lib/pvesnap/state.json` | merkt sich die letzten Läufe |
| `/var/lib/pvesnap/transfer/` | [Austauschlaufwerke](wiederherstellen/transfer.md) |
| `/etc/systemd/system/pvesnap.service` | Unit des Snapshot-Dienstes |
| `/etc/systemd/system/pvesnap-web.service` | Unit der Web-Oberfläche |
### Zusätzliche Pakete
Zwei Werkzeuge werden bei Bedarf nachinstalliert:
| Paket | wofür |
|---|---|
| `parted` | Partitionstabelle der Austauschlaufwerke |
| `exfatprogs` | deren Dateisystem (exFAT) |
Ohne sie funktioniert alles außer den Austauschlaufwerken. Mit
`--no-install-deps` bleibt das System unangetastet; die Laufwerksverwaltung
sagt dann beim Öffnen, was fehlt.
!!! info "Warum kein `ntfs-3g`?"
exFAT reicht: Windows und Linux lesen und schreiben es von Haus aus, es
kennt keine 4-GB-Grenze pro Datei und speichert keine Besitzrechte — was
beim Austausch zwischen Systemen ein Vorteil ist, kein Mangel. Mehr dazu
unter [Austauschlaufwerke](wiederherstellen/transfer.md#warum-exfat).
---
## Aktualisieren
```bash
cd pvesnap
git pull
./install.sh --with-webexplorer --port 8823
```
Dieselben Parameter wie beim ersten Mal — sonst wird die Web-Oberfläche nicht
wieder mit eingerichtet.
**Die Konfiguration bleibt.** `/etc/pvesnap/pvesnap.conf` wird nie
überschrieben.
**Eine laufende Web-Oberfläche wird sauber angehalten**, bevor der Code
ausgetauscht wird, und danach wieder gestartet. Das ist wichtig, weil sie
möglicherweise noch einen Snapshot eingebunden hat — den Prozess einfach unter
den Füßen wegzuziehen würde eine Einbindung zurücklassen.
Sitzt ein **fremder** Prozess auf dem Port, gibt es eine Warnung samt PID, aber
kein automatisches Beenden. Es könnte schließlich etwas ganz anderes sein.
---
## Deinstallation
```bash
./uninstall.sh # Programm und Dienst entfernen, Konfiguration bleibt
./uninstall.sh --purge # zusätzlich /etc/pvesnap und /var/lib/pvesnap löschen
```
!!! danger "Was `--purge` mitnimmt"
Auch die **Austauschlaufwerke** unter `/var/lib/pvesnap/transfer/`. Was
dort an Werkzeugen und Dumps liegt, ist danach weg. Ohne `--purge` bleibt
alles liegen.
**Bereits angelegte Snapshots werden nie angerührt** — weder beim Entfernen
noch beim Aktualisieren. Sie gehören Proxmox, nicht pvesnap.
Laufen noch [Wiederherstellungen](wiederherstellen/index.md), warnt
`uninstall.sh` davor. Die sollten vorher mit `pvesnap-recovery destroy`
verworfen werden, sonst bleiben Klone und geschützte Snapshots liegen.
---
## Die systemd-Unit
Ein Punkt, der später viel Zeit sparen kann: Die mitgelieferte Unit enthält
**bewusst keine Sandbox-Optionen**.
```ini
# NICHT in die Unit aufnehmen:
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
```
Der Grund ist unangenehm indirekt. `pvesh` führt die Proxmox-API im eigenen
Prozess aus — der Snapshot-Task ist also ein *Kindprozess von pvesnap* und erbt
dessen Einschränkungen. `ProtectSystem=` hängt `/etc` schreibgeschützt ein, und
pmxcfs legt seine Sperren als Verzeichnisse unter `/etc/pve/priv/lock/` an. Das
`mkdir` scheitert, Proxmox versucht es erfolglos weiter — und meldet am Ende
keinen Rechtefehler, sondern:
```
TASK ERROR: cfs-lock 'storage-data' error: got lock request timeout
```
Man sucht dann tagelang am Storage. `pvesnap check` weist von sich aus darauf
hin; mehr dazu unter [Fehlersuche](nachschlagen/fehlersuche.md#storage-lock).
---
## Nach der Installation
```bash
systemctl status pvesnap # läuft der Dienst?
pvesnap check # ist die Konfiguration stimmig?
journalctl -u pvesnap -f # was tut er gerade?
```
Weiter geht es beim [Schnellstart](schnellstart.md#2-gaste-markieren) oder
direkt im Kapitel [Sichern](sichern/index.md).
+243
View File
@@ -0,0 +1,243 @@
# Alle Befehle
## `pvesnap`
Der Snapshot-Dienst und seine Verwaltung.
```bash
pvesnap [globale Optionen] <befehl> [optionen]
```
### Globale Optionen
| Option | Vorgabe | |
|---|---|---|
| `-c`, `--config <pfad>` | `/etc/pvesnap/pvesnap.conf` | andere Konfiguration verwenden (auch über `PVESNAP_CONFIG`) |
| `-n`, `--dry-run` | — | nichts wirklich anlegen oder löschen, nur anzeigen |
| `-v`, `--verbose` | — | ausführliche Ausgabe |
| `-V`, `--version` | — | Version anzeigen |
### Unterbefehle
| Befehl | |
|---|---|
| `status` | Übersicht über Gruppen, letzte und nächste Läufe — **auch die Vorgabe ohne Befehl** |
| `vms` | alle VMs und Container mit den Gruppen, in denen sie stecken |
| `list` | vorhandene pvesnap-Snapshots |
| `check` | Konfiguration prüfen |
| `config` (oder `edit`) | [ncurses-Editor](../sichern/editor.md) |
| `run` | fällige Gruppen jetzt ausführen |
| `prune` | nur aufräumen, nichts anlegen |
| `daemon` | Dienst im Vordergrund starten (für systemd) |
### `run`
| Option | |
|---|---|
| `-g`, `--group <name>` | nur diese Gruppe (mehrfach möglich) |
| `-f`, `--force` | unabhängig vom Zeitplan ausführen |
| `--no-prune` | nicht aufräumen |
```bash
pvesnap run # was fällig ist
pvesnap run --force -g taeglich # diese Gruppe sofort
pvesnap run --force --dry-run # Probelauf
pvesnap run -g taeglich -g monatlich # zwei Gruppen
```
### `prune`
| Option | |
|---|---|
| `-g`, `--group <name>` | nur diese Gruppe |
### `list`
| Option | |
|---|---|
| `-g`, `--group <name>` | nur diese Gruppe |
| `-a`, `--all` | auch fremde und von Hand angelegte Snapshots anzeigen |
---
## `pvesnap-explorer`
```bash
pvesnap-explorer [vmid] [snapshot] [optionen]
```
| Parameter | Vorgabe | |
|---|---|---|
| `vmid` | — | Gast direkt vorwählen |
| `snapshot` | — | Snapshot direkt vorwählen |
| `-l`, `--local <pfad>` | aktuelles Verzeichnis | Startverzeichnis im rechten Fenster |
| `-p`, `--path <pfad>` | — | beliebiges Verzeichnis öffnen, ohne Proxmox zu fragen |
| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden |
| `-v`, `--verbose` | — | ausführliches Protokoll |
| `-V`, `--version` | — | Version anzeigen |
```bash
pvesnap-explorer # alles auswählen
pvesnap-explorer 101 # Snapshot von VM 101 wählen
pvesnap-explorer 101 auto-taeglich-20260809-023000 # direkt öffnen
pvesnap-explorer --local /srv/restore # rechtes Fenster startet dort
pvesnap-explorer --path /mnt/x # ohne Proxmox
pvesnap-explorer --cleanup # aufräumen
```
Braucht `root`, außer bei `--path`.
---
## `pvesnap-web`
```bash
pvesnap-web [vmid] [snapshot] [optionen]
```
| Parameter | Vorgabe | |
|---|---|---|
| `-P`, `--port <nummer>` | `8823` | Port, auf dem gelauscht wird |
| `-b`, `--bind <adresse>` | `0.0.0.0` | Netzwerkadresse; `127.0.0.1` = nur lokal |
| `-a`, `--auth pve\|token\|none` | `pve` | Anmeldeart |
| `--require-privilege <recht>` | `VM.Snapshot` | nötiges Recht auf dem Gast |
| `--allow-user <user@realm>` | — | dieser Benutzer darf alles (mehrfach möglich) |
| `-t`, `--token <schlüssel>` | zufällig | Zugangsschlüssel selbst vorgeben |
| `--no-token` | — | Kurzform für `--auth none` |
| `-p`, `--path <verzeichnis>` | — | beliebiges Verzeichnis statt eines Snapshots |
| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden |
| `-v`, `--verbose` | — | ausführliches Protokoll |
| `-V`, `--version` | — | Version anzeigen |
Als Dienst stehen dieselben Parameter in `/etc/pvesnap/web.conf`:
```
PVESNAP_WEB_ARGS=--bind 0.0.0.0 --port 8823 --auth pve
```
---
## `pvesnap-recovery`
```bash
pvesnap-recovery # ncurses-Oberfläche
pvesnap-recovery <befehl> [optionen]
```
### Unterbefehle
| Befehl | |
|---|---|
| `live <VMID> [snapshot]` | abgeschottet starten (ohne Snapshot: der neueste) |
| `recover <VMID> [snapshot]` | mit Netzwerk und gleicher Identität starten |
| `list` | vorhandene Wiederherstellungen anzeigen |
| `start <VMID>` | starten |
| `stop <VMID>` | herunterfahren |
| `console <VMID>` | noVNC-Adresse ausgeben |
| `spice <VMID>` | Verbindungsdatei für `remote-viewer` |
| `flatten <VMID>` | [vom Quell-Snapshot lösen](../wiederherstellen/loesen.md) |
| `cleanup` | [Klone entfernen, zu denen es keinen Gast mehr gibt](../wiederherstellen/verwerfen.md#cleanup) |
| `destroy <VMID>` | [restlos verwerfen](../wiederherstellen/verwerfen.md) |
### Optionen von `live` und `recover`
| Parameter | |
|---|---|
| `--newid <ID>` | VMID der neuen Maschine (Vorgabe: nächste freie) |
| `--node <name>` | auf welchem Node sie laufen soll |
| `--net none\|down\|on` | keine Karte / Karte ohne Leitung / voll am Netz |
| `--resume` / `--no-resume` | Arbeitsspeicher laden bzw. bewusst kalt starten |
| `--transfer <name>` | [Austauschlaufwerk](../wiederherstellen/transfer.md) anhängen (mehrfach möglich) |
| `--spice` | SPICE-Anzeige (`vga: qxl`), zusätzlich zu noVNC |
| `--usb <n>` | so viele USB-Weiterleitungen über SPICE (schaltet `--spice` mit ein) |
| `--iso <volid>` | Abbild als CD einlegen, z. B. `local:iso/virtio-win.iso` |
| `--memory <MB>` | abweichender Arbeitsspeicher (nicht mit `--resume`) |
| `--cores <n>` | abweichende Kernzahl (nicht mit `--resume`) |
| `--name <name>` | Name der neuen Maschine |
| `--keep-binds` | durchgereichte Host-Verzeichnisse des Originals übernehmen |
| `--no-start` | nur einrichten, nicht starten |
| `-y`, `--yes` | Routinefragen überspringen |
| `--force` | auch anlegen, wenn das Original noch läuft |
!!! danger "`-y` deckt `--force` nicht ab"
`recover` mit `-y` bei laufendem Original wird **abgewiesen** (Rückgabewert
2). Siehe [Mit Netzwerk](../wiederherstellen/netzwerk.md).
### Optionen der übrigen Befehle
| Befehl | Option | |
|---|---|---|
| `spice` | `-o <datei>` | in eine Datei schreiben statt auf die Standardausgabe |
| `flatten` | `--force` | auch bei knappem Speicherplatz |
| `cleanup` | `--all` | auch Datenträger ohne Elternteil |
| `cleanup`, `destroy`, `flatten` | `-y`, `--yes` | ohne Rückfrage |
### Beispiele
```bash
# Kurz hineinschauen, mit Werkzeugen
pvesnap-recovery live 101 --transfer werkzeuge
# Ein bestimmter Snapshot, kalt gestartet, mit Dongle
pvesnap-recovery live 105 auto-taeglich-20260809-023000 --usb 2 --no-resume
# Ernstfall: Original steht, Wiederherstellung übernimmt
qm stop 101
pvesnap-recovery recover 101 --newid 9101
pvesnap-recovery flatten 9101
# Verbindungsdatei für den Arbeitsplatz
ssh root@pve2 pvesnap-recovery spice 9104 > vm.vv && remote-viewer vm.vv
# Aufräumen
pvesnap-recovery destroy 9101
pvesnap-recovery cleanup
```
---
## systemd
```bash
systemctl status pvesnap
systemctl reload pvesnap # Konfiguration neu einlesen (SIGHUP)
systemctl restart pvesnap
journalctl -u pvesnap -f
systemctl status pvesnap-web
systemctl restart pvesnap-web # nach Änderungen in web.conf
systemctl stop pvesnap-web
journalctl -u pvesnap-web -f
```
---
## Installation
```bash
./install.sh [optionen]
./uninstall.sh [--purge]
```
| Option | |
|---|---|
| `--with-webexplorer` | Web-Oberfläche als Dienst einrichten (erfordert `--port`) |
| `--port <nummer>` | Port der Web-Oberfläche |
| `--bind <adresse>` | Adresse, auf der sie lauscht (Vorgabe `0.0.0.0`) |
| `--no-start` | installieren, aber Dienste nicht starten |
| `--no-install-deps` | `parted` und `exfatprogs` nicht nachinstallieren |
| `--force` | auch ohne erkanntes Proxmox VE installieren |
| `--help` | Übersicht |
---
## Hilfswerkzeug
```bash
tools/get-guest-tools.sh <iso-storage>
```
Holt die virtio-win-Treiber auf einen ISO-Storage — für Windows-Gäste, die
Treiber brauchen, oder für Zwischenablage und Mauszeiger unter
[SPICE](../wiederherstellen/dongle.md).
+281
View File
@@ -0,0 +1,281 @@
# Fehlersuche
## Es passiert gar nichts
Der Dienst läuft, das Protokoll ist ruhig, und trotzdem entstehen keine
Snapshots.
```bash
pvesnap status # hat eine Gruppe einen nächsten Termin?
pvesnap vms # steht überhaupt eine VM in einer Gruppe?
pvesnap check
```
| Ursache | |
|---|---|
| **Alle Gruppen auf `enabled = no`** | So wird ausgeliefert. Der häufigste Fall. |
| Auswahl trifft auf nichts zu | Tag falsch geschrieben? `pvesnap vms` zeigt es. |
| `dry_run = yes` in `[global]` | Es wird nur protokolliert. |
| Konfiguration nach der Änderung nicht neu geladen | `systemctl reload pvesnap` |
---
## Storage-Lock
```
trying to acquire cfs lock 'storage-data' ...
TASK ERROR: cfs-lock 'storage-data' error: got lock request timeout
```
!!! note "`storage-data` ist kein falsch gelesener Name"
In pmxcfs heißen Storage-Sperren immer `storage-<name>`. Gemeint ist also
das Storage `data`.
Proxmox nimmt diese Sperre beim Anlegen eines Snapshots und gibt nach 60
Sekunden auf, wenn sie jemand anderes hält.
pvesnap geht damit so um:
* Der Aufruf **wartet, bis der Proxmox-Task wirklich fertig ist**, bevor die
nächste VM drankommt — sonst würden sich die eigenen Läufe aussperren
* Sperr-Fehler gelten als vorübergehend und werden `retries`-mal mit
`retry_delay` Abstand wiederholt
* Echte Fehler (etwa „storage does not support snapshots") werden **nicht**
wiederholt
* `pause_between = 10s` nimmt zusätzlich Druck vom Storage
### Der Dienst scheitert, von Hand geht es
Das ist der Fall, an dem man am längsten sucht — und er hat mit dem Storage
nichts zu tun.
!!! danger "Sandbox-Optionen in der systemd-Unit"
`pvesh` führt die Proxmox-API im eigenen Prozess aus. Der Snapshot-Task ist
also ein **Kindprozess von pvesnap** und erbt alles, was in der Unit
eingeschränkt wurde.
`ProtectSystem=` hängt `/etc` schreibgeschützt ein — und pmxcfs legt seine
Sperren als **Verzeichnisse** unter `/etc/pve/priv/lock/` an. Das `mkdir`
scheitert, Proxmox wiederholt es erfolglos und meldet am Ende einen
Lock-Timeout statt eines Rechtefehlers.
Prüfen:
```bash
systemctl show pvesnap -p ProtectSystem -p ProtectHome -p PrivateTmp -p ReadOnlyPaths
pvesnap check # meldet so etwas von sich aus
```
Alle müssen leer bzw. `no` sein. Die mitgelieferte Unit enthält deshalb
**bewusst keine** Sandbox-Optionen.
`ProtectHome=yes` hat denselben Effekt an anderer Stelle: Es ersetzt `/root`
durch ein leeres Verzeichnis — und Proxmox erreicht andere Cluster-Nodes über
die SSH-Schlüssel in `/root/.ssh`.
### Wenn die Sperre wirklich belegt ist
```bash
pvesm status # ist das Storage online und erreichbar?
grep -A6 "^[a-z]*: data" /etc/pve/storage.cfg
pvesh get /cluster/tasks --output-format json | head # hängt noch ein Task?
systemctl status pvestatd pve-cluster
journalctl -u pvestatd -n 50
```
Häufigste Ursachen: ein hängender Backup- oder Replikationsjob auf demselben
Storage, ein nicht erreichbares NFS/CIFS-Storage (dann blockiert `pvestatd`),
oder ein abgebrochener Task, der die Sperre nicht freigegeben hat.
---
## `snapshot is protected`
```
TASK ERROR: rbd snapshot 'auto-taeglich-20260809-023000' is protected from removal
```
Am Snapshot hängt ein Klon — also eine
[Wiederherstellung](../wiederherstellen/index.md). Das ist zunächst richtig so:
Ceph verlangt den Schutz, solange ein Klon existiert.
| Lage | |
|---|---|
| Die Wiederherstellung wird noch gebraucht | Nichts tun. Der Snapshot bleibt eben liegen. |
| Sie wird nicht mehr gebraucht | `pvesnap-recovery destroy <VMID>` |
| Sie soll bleiben | `pvesnap-recovery flatten <VMID>` |
| Sie wurde in der **Weboberfläche** gelöscht | `pvesnap-recovery cleanup` |
Der letzte Fall ist der, den man sonst nicht findet: Der Klon ist weg, der
Schutz steht noch, und die Vorhaltezeit scheitert für immer.
Nachsehen:
```bash
rbd -p <pool> snap ls vm-101-disk-0 # Spalte PROTECTED
rbd -p <pool> ls -l | grep vm-101 # gibt es noch Klone?
```
---
## `storage does not support snapshots`
Die VM liegt auf LVM-thick oder auf einem `raw`-Image in einem
Verzeichnis-Storage. Dort kann Proxmox keine Snapshots — pvesnap überspringt sie
und macht mit den übrigen weiter.
Der Ausweg ist ein Umzug:
```bash
qm move-disk 101 scsi0 <thin-oder-ceph-storage>
```
Siehe [Speicherarten](speicher.md).
---
## Der Snapshot lässt sich nicht einbinden
```bash
pvesnap-explorer --cleanup # erst einmal aufräumen
pvesnap-explorer -v 101 # dann mit ausführlichem Protokoll
```
| Meldung | |
|---|---|
| `rbd: image uses unsupported features` | Der Kernel kann das Image nicht direkt; pvesnap weicht auf `rbd-nbd` aus. Fehlt das Paket `rbd-nbd`, nachinstallieren. |
| `unknown filesystem type` | Das Dateisystem im Gast kennt der Host nicht (z. B. Btrfs ohne Modul, oder ReFS). |
| `wrong fs type … or superblock corrupt` | Meist eine verschlüsselte Partition (LUKS, BitLocker). Dort führt nur der Weg über eine [laufende Maschine](../wiederherstellen/index.md). |
| Es wird gar kein Dateisystem angeboten | Reine Datenplatte ohne Partitionstabelle, oder LVM im Gast, das nicht aktiviert werden konnte. |
Reste nach einem Absturz aufspüren:
```bash
findmnt | grep pvesnap
rbd showmapped
losetup -a
ls /run/pvesnap/
```
---
## Die Maschine startet, aber der RAM-Zustand fehlt
Symptom: Die Wiederherstellung bootet, statt weiterzulaufen. Die Uhr im Gast
zeigt die aktuelle Zeit statt der Snapshot-Zeit.
!!! warning "Proxmox meldet das als `TASK OK`"
Schlägt das Laden fehl, quittiert Proxmox den Start trotzdem mit Erfolg und
lässt die Maschine in `prelaunch` stehen. `pvesnap-recovery` liest das
Task-Protokoll mit, setzt sie fort und sagt es deutlich.
| Meldung im Task-Protokoll | |
|---|---|
| `Size mismatch: vga.vram` | SPICE (`vga: qxl`) und geladener Arbeitsspeicher schließen sich aus. Siehe [Dongle und USB](../wiederherstellen/dongle.md#spice-und-geladener-arbeitsspeicher-schlieen-sich-aus). |
| `Unknown savevm section or instance 'vmgenid'` | Die Geräteausstattung passt nicht zum gespeicherten Zustand. Tritt bei von Hand veränderten Konfigurationen auf. |
| `unable to open vmstate` | Der Arbeitsspeicher-Datenträger ist nicht mehr da — Proxmox gibt ihn nach dem **ersten** Start frei. Ein zweiter Start bootet immer kalt. |
Der letzte Punkt ist kein Fehler, sondern das erwartete Verhalten. Wer den
warmen Zustand braucht, arbeitet damit **beim ersten Start**.
---
## Das Austauschlaufwerk lässt sich nicht auswerfen
```
'dumps' liess sich nicht abziehen: ...
Meist haelt der Gast es noch - dort erst aushaengen
(Linux: umount, Windows: Auswerfen im Explorer).
```
Genau das ist es meistens. Im Gast aushängen, dann noch einmal ++e++.
Weitere Fälle:
| Meldung | |
|---|---|
| `ist gerade in Benutzung von 9101` | Das Laufwerk steckt in einer anderen Maschine. Dort auswerfen. |
| `ist gerade am Host eingehängt` | Mit ++u++ in der Laufwerksübersicht aushängen. |
| `Bei Containern gibt es nichts auszuwerfen` | Dort ist es ein durchgereichtes Verzeichnis — Host und Gast sehen dieselben Dateien ohnehin gleichzeitig. |
---
## SPICE oder USB gehen nicht
| Symptom | |
|---|---|
| Maschine startet nicht, `no spice port` | `vga: qxl` fehlt. Detailansicht ++p++ → SPICE ein. |
| Kein Eintrag unter „USB-Geräteauswahl" | Der `remote-viewer` läuft nicht auf dem Rechner, an dem der Dongle steckt. |
| `.vv`-Datei wird abgewiesen | Sie ist zu alt — das Kennwort darin gilt nur wenige Sekunden. Neu erzeugen. |
| `remote-viewer` nicht gefunden | Paket `virt-viewer` installieren. |
Mehr unter [Dongle und USB](../wiederherstellen/dongle.md#wenn-der-dongle-nicht-auftaucht).
---
## Die Web-Oberfläche zeigt keine Gäste
Ein angemeldeter Benutzer sieht nur Gäste, auf denen er das Recht
**`VM.Snapshot`** hat. Ist die Liste leer, fehlt genau das.
```bash
pveum user permissions <benutzer>@<realm>
```
Anpassen mit `--require-privilege VM.Backup` oder
`--allow-user <benutzer>@<realm>` in `/etc/pvesnap/web.conf`, danach
`systemctl restart pvesnap-web`.
Fehlgeschlagene Anmeldungen stehen mit Absender-IP im Journal:
```bash
journalctl -u pvesnap-web -n 50
```
---
## Der Port ist belegt
Beim Installieren:
```
Auf Port 8823 laeuft bereits ein anderer Prozess (PID 12345)
```
Das ist eine Warnung, kein Abbruch — es könnte etwas ganz anderes sein. Nachsehen:
```bash
ss -lntp | grep 8823
```
Eine bereits laufende `pvesnap-web` wird dagegen von selbst sauber angehalten
und danach wieder gestartet.
---
## Zeitpläne stimmen nicht
| Beobachtung | |
|---|---|
| Der Termin liegt eine Stunde daneben | Zeitumstellung. Es wird mit lokaler Zeit gerechnet. |
| Nach einem Neustart kam sofort ein Lauf | Ein verpasster Termin wird nachgeholt — einmal, nicht für jeden ausgefallenen. |
| Im Februar fiel der Monatslauf aus | Sollte nicht passieren: `day_of_month = 31` wird auf den Monatsletzten gezogen. Prüfen mit der Vorschauzeile in der [Gruppenmaske](../sichern/editor.md#eine-gruppe-bearbeiten). |
| Die Snapshots liegen auf krummen Zeiten | `align = no`. Mit `align = yes` richten sie sich an der Uhr aus. |
---
## Wenn gar nichts mehr hilft
```bash
pvesnap -v check # ausführlich
pvesnap -v run --force --dry-run # zeigt jeden pvesh-Aufruf
journalctl -u pvesnap -n 200 --no-pager
```
Mit `log_level = DEBUG` in `[global]` protokolliert der Dienst jeden
`pvesh`-Aufruf mit allen Argumenten. Damit lässt sich der fehlschlagende Aufruf
von Hand nachstellen — und dann sieht man die echte Meldung von Proxmox statt
der aufbereiteten.
+89
View File
@@ -0,0 +1,89 @@
# Nachschlagen
Der Teil zum Blättern, wenn man schon weiß, was man sucht.
<div class="grid cards" markdown>
- **[Alle Befehle](befehle.md)**
Jeder Unterbefehl und jeder Parameter der vier Werkzeuge.
- **[Alle Einstellungen](konfiguration.md)**
Jeder Schlüssel der INI-Datei, mit Vorgabe und Bedeutung.
- **[Tastenkürzel](tasten.md)**
Alle Tasten aller Oberflächen auf einer Seite.
- **[Speicherarten](speicher.md)**
Was Ceph, ZFS, LVM-thin und Datei-Storages jeweils können.
- **[Fehlersuche](fehlersuche.md)**
Meldungen, ihre Ursachen und was dagegen hilft.
</div>
---
## Wo was liegt
| Pfad | |
|---|---|
| `/etc/pvesnap/pvesnap.conf` | die Konfiguration |
| `/etc/pvesnap/web.conf` | Parameter der Web-Oberfläche |
| `/var/lib/pvesnap/state.json` | wann welche Gruppe zuletzt lief |
| `/var/lib/pvesnap/recovery.json` | Merkliste der Wiederherstellungen |
| `/var/lib/pvesnap/transfer/` | Austauschlaufwerke (Abbilddateien) |
| `/run/pvesnap/mnt/` | eingebundene Snapshots |
| `/run/pvesnap/transfer/` | am Host eingehängte Austauschlaufwerke |
| `/usr/lib/pvesnap/` | Programmcode |
| `/usr/local/bin/pvesnap*` | die vier Werkzeuge |
---
## Rückgabewerte
Für Skripte:
| Wert | |
|---|---|
| `0` | in Ordnung |
| `1` | abgebrochen (Rückfrage verneint) |
| `2` | Fehler in der Konfiguration oder in den Parametern |
| `3` | Proxmox-Fehler |
| `4` | Laufzeitfehler |
| `130` | mit ++ctrl+c++ abgebrochen |
---
## Aufbau des Programms
```
pvesnap/
config.py INI lesen und schreiben, Gruppen- und Globaleinstellungen
schedule.py Berechnung des nächsten Termins
naming.py Namensschema und Beschreibungs-Vorlagen
proxmox.py pvesh-Anbindung (Inventar, Snapshots anlegen/löschen)
engine.py Auswahl der Gäste, Anlegen, Aufräumen
daemon.py Hauptschleife, Signale, Sperren
state.py merkt sich die letzten Läufe
preflight.py prüft die Umgebung (root, /etc/pve beschreibbar, /root)
cli.py Kommandozeile
curses_util.py gemeinsame curses-Bausteine
tui.py ncurses-Konfigurationseditor
snapfs.py Snapshots einbinden und mounten (rbd/zfs/lvm/qcow2)
explorer.py Zwei-Fenster-Explorer im Terminal
recovery.py Snapshot als Maschine starten (Klone, Konfiguration, Aufräumen)
recovery_ui.py ncurses-Oberfläche und Kommandozeile dazu
transfer.py Austauschlaufwerke: anlegen, ein-/aushängen, Verriegelung
transfer_ui.py deren Bildschirm (Taste v in pvesnap-recovery)
web/ Web-Oberfläche zum Herunterladen
```
Alle vier Werkzeuge teilen sich `proxmox.py`. `snapfs.py` liefert Explorer und
Web-Oberfläche einen ganz gewöhnlichen Pfad, sodass sie nichts über Ceph, ZFS
oder LVM wissen müssen; `recovery.py` geht den anderen Weg und überlässt das
Klonen der Storage-Schicht von Proxmox selbst.
+256
View File
@@ -0,0 +1,256 @@
# Alle Einstellungen
Die Datei liegt unter `/etc/pvesnap/pvesnap.conf` und ist eine gewöhnliche
INI-Datei mit drei Arten von Abschnitten:
```ini
[global] # gilt für alles
[defaults] # Vorgaben für alle Gruppen (optional)
[group:NAME] # eine Gruppe, beliebig viele davon
```
Nach jeder Änderung:
```bash
pvesnap check
systemctl reload pvesnap
```
!!! note "Zeitangaben"
Überall dasselbe Format: `30s`, `15m`, `1h`, `6h`, `2d12h`, `1w`.
`0` heißt „unbegrenzt" bzw. „keine".
!!! note "Kommentare am Zeilenende"
Hinter jedem Wert darf ein Kommentar stehen:
```ini
keep_time = 7d # eine Woche
```
Einzige Ausnahme ist `description` — dort bleibt die Zeile unverändert,
damit ein `#` in der Beschreibung erhalten bleibt.
---
## `[global]`
| Schlüssel | Vorgabe | |
|---|---|---|
| `prefix` | `auto` | Namenspräfix aller pvesnap-Snapshots. Bestimmt zugleich, was jemals gelöscht wird — siehe [Namensschema](../sichern/index.md#namensschema). |
| `check_interval` | `60s` | wie oft der Dienst nach Fälligem schaut |
| `state_file` | `/var/lib/pvesnap/state.json` | merkt sich die letzten Läufe |
| `log_level` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
| `log_file` | — | zusätzliche Protokolldatei; ohne sie geht alles ins Journal |
| `task_timeout` | `15m` | Geduld mit einem einzelnen Proxmox-Task |
| `retries` | `2` | zusätzliche Versuche, wenn eine Sperre belegt ist |
| `retry_delay` | `60s` | Wartezeit vor dem nächsten Versuch |
| `pause_between` | `0s` | Pause zwischen zwei Gästen |
| `run_on_start` | `no` | beim Dienststart sofort einen Durchlauf machen |
| `dry_run` | `no` | `yes` = nichts wirklich tun, nur protokollieren |
| `description` | siehe unten | Standard-Beschreibung aller Snapshots |
```ini
[global]
prefix = auto
check_interval = 60s
state_file = /var/lib/pvesnap/state.json
log_level = INFO
task_timeout = 15m
retries = 2
retry_delay = 60s
pause_between = 0s
run_on_start = no
dry_run = no
description = pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time} | max: {keep_count}
```
---
## `[defaults]`
Vorgaben für **alle** Gruppen. Jede Gruppe darf sie einzeln überschreiben.
```ini
[defaults]
enabled = yes
skip_stopped = no
vmstate = no
```
Erlaubt sind hier dieselben Schlüssel wie in einer Gruppe.
!!! warning "Der Editor löst `[defaults]` auf"
Beim Speichern im [ncurses-Editor](../sichern/editor.md) verschwindet der
Abschnitt — die Werte stehen danach in jeder Gruppe einzeln. Inhaltlich
ändert sich nichts.
---
## `[group:NAME]`
### Allgemein
| Schlüssel | Vorgabe | |
|---|---|---|
| `enabled` | `yes` | Gruppe aktiv. In der Auslieferung stehen alle auf `no`. |
### Zeitplan
Entweder `interval` **oder** `schedule` — nicht beides.
| Schlüssel | Vorgabe | |
|---|---|---|
| `interval` | — | `30m`, `1h`, `6h`, `2d12h`, `1w` |
| `align` | `no` | `yes` = an der Uhr ausgerichtet (00:00, 00:30 …) |
| `schedule` | — | `hourly`, `daily`, `weekly`, `monthly`, `yearly` |
| `at` | — | Uhrzeit `HH:MM` bei `daily` und größer |
| `minute` | `0` | Minute bei `hourly` |
| `day_of_week` | `mo` | `mo di mi do fr sa so` bei `weekly` |
| `day_of_month` | `1` | 131 bei `monthly`; zu große Werte fallen auf den Monatsletzten |
| `month` | `1` | 112 bei `yearly` |
Einzelheiten unter [Zeitpläne](../sichern/zeitplan.md).
### Vorhaltezeit
| Schlüssel | Vorgabe | |
|---|---|---|
| `keep_count` | `0` | höchstens so viele je VM und Gruppe (`0` = unbegrenzt) |
| `keep_time` | `0` | nichts älter als (`0` = unbegrenzt) |
| `keep_min` | `0` | so viele bleiben in jedem Fall stehen |
**Mindestens eine von `keep_count` und `keep_time` muss gesetzt sein.**
Einzelheiten unter [Vorhaltezeit](../sichern/vorhaltezeit.md).
### Auswahl der Gäste
| Schlüssel | Vorgabe | |
|---|---|---|
| `all` | `no` | alle VMs und Container |
| `vmids` | — | `100,101,105-110` |
| `names` | — | `web-*, db-0?` — mit `*`, `?`, `[abc]` |
| `tags` | — | `produktion, wichtig` |
| `pools` | — | `Kunden` |
| `types` | — | `qemu` oder `lxc`; leer = beides |
| `exclude_vmids` | — | |
| `exclude_names` | — | |
| `exclude_tags` | — | |
Einschlüsse wirken als **ODER**, Ausschlüsse gewinnen immer. Einzelheiten unter
[Welche Gäste?](../sichern/auswahl.md).
### Snapshot-Optionen
| Schlüssel | Vorgabe | |
|---|---|---|
| `vmstate` | `no` | Arbeitsspeicher mitsichern — nur QEMU, nur bei laufender VM |
| `skip_stopped` | `no` | gestoppte Gäste überspringen |
| `description` | aus `[global]` | eigene Beschreibung für diese Gruppe |
---
## Platzhalter der Beschreibung
| | | |
|---|---|---|
| `{group}` | Name der Gruppe | `täglich` |
| `{group_slug}` | Kurzname im Snapshot | `taeglich` |
| `{vmid}` | VMID | `101` |
| `{name}` | Name des Gastes | `db01` |
| `{type}` | `VM` oder `LXC` | `VM` |
| `{node}` | Cluster-Knoten | `pve1` |
| `{pool}` | Proxmox-Pool | `Hausnetz` |
| `{tags}` | Tags des Gastes | `produktion,datenbank` |
| `{date}` | Datum | `2026-08-09` |
| `{time}` | Uhrzeit | `02:30:00` |
| `{datetime}` | beides | `2026-08-09 02:30:00` |
| `{timestamp}` | Unix-Zeit | `1786257000` |
| `{keep_time}` | Vorhaltezeit der Gruppe | `3w` |
| `{keep_count}` | Anzahl | `14` |
| `{schedule}` | Zeitplan im Klartext | `täglich um 02:30` |
Ein unbekannter Platzhalter führt nicht zum Abbruch — dann wird eine
Ersatzbeschreibung eingesetzt.
---
## Deutsche Schreibweisen
Damit die Datei lesbar bleibt, versteht pvesnap eine Reihe deutscher Synonyme:
| deutsch | entspricht |
|---|---|
| `aktiv`, `aktiviert` | `enabled` |
| `intervall` | `interval` |
| `zeitplan` | `schedule` |
| `uhrzeit`, `zeit` | `at` |
| `wochentag` | `day_of_week` |
| `monatstag` | `day_of_month` |
| `monat` | `month` |
| `anzahl`, `max_anzahl`, `behalte_anzahl` | `keep_count` |
| `vorhaltezeit`, `behalte_zeit`, `max_alter` | `keep_time` |
| `mindestens` | `keep_min` |
| `alle` | `all` |
| `namen` | `names` |
| `typen` | `types` |
| `beschreibung` | `description` |
| `ausschluss_vmids` | `exclude_vmids` |
| `ausschluss_namen` | `exclude_names` |
| `gestoppte_ueberspringen` | `skip_stopped` |
Bindestriche und Unterstriche sind austauschbar, Groß- und Kleinschreibung egal.
---
## Eine vollständige Beispieldatei
```ini
[global]
prefix = auto
check_interval = 60s
state_file = /var/lib/pvesnap/state.json
log_level = INFO
task_timeout = 15m
retries = 2
retry_delay = 60s
pause_between = 0s
run_on_start = no
dry_run = no
description = pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time} | max: {keep_count}
[defaults]
enabled = yes
skip_stopped = no
vmstate = no
[group:stuendlich]
interval = 1h
align = yes
keep_count = 24
keep_time = 2d
keep_min = 1
tags = stuendlich
skip_stopped = yes
[group:taeglich]
schedule = daily
at = 02:30
keep_count = 14
keep_time = 21d
keep_min = 1
tags = produktion
exclude_tags = nosnap
[group:monatlich]
schedule = monthly
day_of_month = 1
at = 04:00
keep_count = 6
keep_time = 400d
all = yes
exclude_tags = nosnap, pvesnap-recovery
description = Monatssicherung {name} ({vmid}) vom {date}
```
+192
View File
@@ -0,0 +1,192 @@
# Speicherarten
Nicht jedes Proxmox-Storage kann alles. Diese Seite fasst zusammen, was wo geht.
---
## Die Übersicht
| Storage | Snapshots | Dateien lesen | Als Maschine starten | Vom Snapshot lösen |
|---|---|---|---|---|
| **`rbd`** (Ceph) | ✅ | ✅ | ✅ | ✅ nötig |
| **`zfspool`** | ✅ | ✅ | ✅ | ❌ nicht möglich |
| **`lvmthin`** | ✅ | ✅ | ✅ | — nicht nötig |
| **`lvm`** (dick) | ❌ | — | — | — |
| **`dir`, `nfs`, `cifs`** (qcow2) | ✅ | ✅ | ❌ | — |
| **`dir`, `nfs`, `cifs`** (raw) | ❌ | — | — | — |
---
## Ceph / RBD
**Die beste Grundlage für pvesnap.** Alles geht, und es geht clusterweit.
| | |
|---|---|
| Snapshot | `rbd snap create`, sofort |
| Hineinsehen | `rbd map pool/image@snap`, bei nicht unterstützten Image-Features über `rbd-nbd` |
| Klonen | `rbd clone`, in etwa einer Sekunde — unabhängig von der Größe |
| Wiederherstellung startbar auf | **jedem Node des Clusters** (geteiltes Storage) |
### Die eine Eigenheit
RBD verlangt für einen Klon, dass der Quell-Snapshot **geschützt** ist
(`rbd snap protect`). Solange ein Klon existiert, ist der Snapshot damit
unlöschbar, und die [Vorhaltezeit](../sichern/vorhaltezeit.md) scheitert an ihm
mit `snapshot is protected`.
pvesnap geht damit sauber um: Beim [Verwerfen](../wiederherstellen/verwerfen.md)
wird der Schutz wieder aufgehoben, und
[`flatten`](../wiederherstellen/loesen.md) löst die Abhängigkeit ganz.
### Platz im Blick
```bash
ceph df # MAX AVAIL beachten
rbd -p <pool> du
rbd -p <pool> ls -l # Spalte PARENT zeigt Klone
```
!!! danger "`% RAW USED` täuscht"
Es berücksichtigt die Replikation nicht. Bei dreifacher Replikation kosten
13 GB Nutzdaten 39 GB im Pool. **`MAX AVAIL`** ist die Zahl, die zählt.
Läuft ein Pool voll, blockiert er jeden Schreibvorgang — und damit alle VMs
darauf. Selbst das Aufräumen wird dann schwierig, weil Löschen ebenfalls
ein Schreibvorgang ist.
---
## ZFS
| | |
|---|---|
| Snapshot | `zfs snapshot`, sofort |
| Hineinsehen | Container direkt über `.zfs/snapshot/…`, VMs über einen temporären Klon |
| Klonen | `zfs clone`, sofort |
| Wiederherstellung startbar auf | dem Node mit dem Pool (bzw. clusterweit bei ZFS over iSCSI) |
### Lösen geht nicht
Der Klon hängt wie bei Ceph am Snapshot. `zfs promote` würde die Abhängigkeit
nur **umdrehen** — danach hinge das Original am Klon. Das verschiebt das
Problem, statt es zu lösen.
Echte Unabhängigkeit ginge nur über `zfs send | zfs recv` in einen neuen
Datenträger. Das macht `flatten` nicht und sagt es auch so.
```bash
zfs list -o space # USEDSNAP zeigt, was die Snapshots kosten
zfs list -t snapshot
```
---
## LVM-thin
**In einem Punkt angenehmer als Ceph:** Ein Thin-Snapshot ist ein
eigenständiges Volume, das sich mit anderen nur die Blöcke im Pool teilt.
```
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
```
Platzsparend geteilt **und** trotzdem frei löschbar. Es gibt dort weder
geschützte Snapshots noch etwas zu flatten.
### Der Preis: lokaler Speicher
`lvmthin` liegt auf einem einzelnen Node. Die Wiederherstellung muss auf
demselben Node laufen wie das Original; `pvesnap-recovery` prüft das und lehnt
einen anderen Node ab.
!!! warning "Auch Thin-Pools laufen voll"
Und dann stehen alle Volumes darin. Überprovisionierung im Blick behalten:
```bash
lvs -o lv_name,data_percent,metadata_percent
```
Besonders die **Metadaten** — läuft der Metadatenbereich voll, ist der Pool
nicht mehr zu retten, auch wenn noch Datenplatz frei ist.
---
## LVM (dick)
Proxmox kann dort **gar keine Snapshots**. Betroffene VMs melden im Protokoll:
```
storage does not support snapshots
```
Sie werden übersprungen, die übrigen laufen normal weiter.
Der Ausweg ist ein Umzug auf `lvmthin` — technisch derselbe Speicher, nur mit
Thin-Provisioning:
```bash
qm move-disk 101 scsi0 <thin-storage>
```
---
## Verzeichnis-Storages (`dir`, `nfs`, `cifs`)
Hier kommt es auf das **Dateiformat** an:
| Format | Snapshots | |
|---|---|---|
| `qcow2` | ✅ | interne Snapshots im Abbild selbst |
| `raw` | ❌ | keine Snapshot-Fähigkeit |
| `vmdk` | ❌ | |
### Hineinsehen: ja
`qemu-nbd --load-snapshot` bindet den Snapshot schreibgeschützt ein. Explorer
und Web-Oberfläche funktionieren also.
### Als Maschine starten: nein
!!! warning "Kein Klon aus einem qcow2-internen Snapshot"
Ein qcow2-interner Snapshot lässt sich nicht als eigenständiges Volume
klonen. `pvesnap-recovery` lehnt das ab und verweist auf den Explorer.
Wer die Fähigkeit braucht, verschiebt die VM auf ein Storage, das Klone
kann — Ceph, ZFS oder LVM-thin.
---
## Was passiert bei gemischten Datenträgern?
Hat eine VM Platten auf verschiedenen Storages, gilt für die
**Wiederherstellung** die strengste Regel:
* Liegt eine Platte auf einem Storage, das keine Klone kann, geht es gar nicht
* Liegt eine Platte auf lokalem Storage, ist der Node festgelegt
* Ist ein Storage auf dem Zielnode nicht verfügbar, wird abgelehnt
Die [Zusammenfassung](../wiederherstellen/einrichten.md#die-zusammenfassung)
nennt den Grund, bevor etwas angelegt wird.
---
## Empfehlung
| Lage | |
|---|---|
| **Cluster mit Ceph** | Ideal. Alles geht, von jedem Node aus. Nur an `flatten` denken, wenn eine Wiederherstellung bleibt. |
| **Einzelner Host** | `lvmthin` oder ZFS. LVM-thin ist beim Lösen unkomplizierter, ZFS bietet dafür Prüfsummen und `zfs send`. |
| **NFS-Storage** | qcow2 statt raw verwenden, dann geht wenigstens das Lesen. Für Wiederherstellungen ist es die falsche Grundlage. |
| **LVM dick** | Auf `lvmthin` umstellen. Ohne Snapshot-Fähigkeit hat pvesnap dort nichts zu tun. |
+148
View File
@@ -0,0 +1,148 @@
# Tastenkürzel
Alle Oberflächen auf einer Seite. Zum Ausdrucken und Danebenlegen.
---
## `pvesnap config` — Gruppenliste
| Taste | |
|---|---|
| ++enter++ | Gruppe bearbeiten |
| ++space++ | Gruppe ein- oder ausschalten |
| ++n++ | neue Gruppe |
| ++c++ | Gruppe kopieren |
| ++d++ | Gruppe löschen |
| ++g++ | globale Einstellungen |
| ++v++ | Übersicht: welche VM in welcher Gruppe |
| ++s++ | speichern |
| ++r++ | Dienst neu laden |
| ++q++ | Ende |
### Gruppenmaske
| Taste | |
|---|---|
| ++up++ ++down++ | Feld wählen |
| ++page-up++ ++page-down++ | zehn Felder weiter |
| ++enter++ ++space++ | Feld ändern bzw. umschalten |
| ++v++ | VM-Auswahl öffnen |
| ++q++ ++esc++ | zurück |
### VM-Auswahl
| Taste | |
|---|---|
| ++space++ | Gast an- oder abwählen |
| ++a++ | alle |
| ++n++ | keine |
| ++i++ | Auswahl umkehren |
| ++slash++ | filtern |
| ++enter++ | übernehmen |
| ++esc++ | abbrechen |
---
## `pvesnap-explorer`
| Taste | |
|---|---|
| ++tab++ | Fenster wechseln |
| ++up++ ++down++ | bewegen |
| ++enter++ | Verzeichnis öffnen oder Datei ansehen |
| ++backspace++ ++left++ | ein Verzeichnis hoch |
| ++home++ ++end++ | Anfang / Ende |
| ++page-up++ ++page-down++ | seitenweise |
| ++space++ | markieren |
| ++asterisk++ | Markierung umkehren |
| ++a++ | alle markieren |
| ++u++ | Markierung aufheben |
| ++f5++ ++c++ | Markiertes ins andere Fenster kopieren |
| ++f3++ ++v++ | Datei ansehen (Text oder Hex) |
| ++f7++ ++n++ | neues Verzeichnis (nur lokal) |
| ++f6++ ++g++ | Verzeichnis direkt eingeben |
| ++f2++ ++m++ | anderes Dateisystem des Snapshots wählen |
| ++r++ | neu einlesen |
| ++f1++ ++question++ | Hilfe |
| ++f10++ ++q++ | eine Ebene zurück |
Während des Kopierens bricht ++esc++ ab.
---
## `pvesnap-recovery` — Übersicht
| Taste | |
|---|---|
| ++enter++ | Detailansicht |
| ++n++ | neue Wiederherstellung |
| ++s++ | starten |
| ++h++ | herunterfahren |
| ++x++ | verwerfen |
| ++v++ | Austauschlaufwerke |
| ++r++ | neu einlesen |
| ++q++ | Ende |
### Detailansicht
| Taste | |
|---|---|
| ++s++ | starten |
| ++h++ | herunterfahren |
| ++e++ | Austauschlaufwerk auswerfen / einklinken |
| ++v++ | Austauschlaufwerke verwalten |
| ++p++ | SPICE und USB |
| ++f++ | vom Quell-Snapshot lösen |
| ++x++ | verwerfen |
| ++q++ | zurück |
### Optionsmaske
| Taste | |
|---|---|
| ++up++ ++down++ | Feld wählen |
| ++enter++ ++space++ | Feld ändern |
| ++v++ | Austauschlaufwerke verwalten |
| ++f10++ | anlegen |
| ++q++ | abbrechen |
### Zusammenfassung
| Taste | |
|---|---|
| ++j++ | anlegen |
| ++n++ ++q++ | abbrechen |
| beliebige Taste | weiterblättern, wenn der Text länger ist als der Bildschirm |
---
## Austauschlaufwerke
| Taste | |
|---|---|
| ++enter++ | am Host einhängen und im Commander öffnen |
| ++n++ | neues Laufwerk anlegen |
| ++u++ | am Host aushängen |
| ++l++ | löschen |
| ++r++ | neu einlesen |
| ++q++ | zurück |
### Mehrfachauswahl beim Anlegen einer Maschine
| Taste | |
|---|---|
| ++space++ | aus- oder abwählen |
| ++enter++ | übernehmen |
| ++q++ | abbrechen |
---
## Überall gleich
| Taste | |
|---|---|
| ++up++ ++down++ oder ++k++ ++j++ | in Auswahlfenstern bewegen |
| ++enter++ | bestätigen |
| ++esc++ ++q++ | abbrechen / zurück |
| ++j++ ++n++ | Ja/Nein-Rückfragen |
| ++ctrl+u++ | Eingabezeile leeren |
+137
View File
@@ -0,0 +1,137 @@
# Schnellstart
Vom leeren Host zur laufenden Sicherung. Rechne mit einer Viertelstunde,
davon zehn Minuten Nachdenken darüber, was wie oft gesichert werden soll.
---
## 1. Installieren
Auf dem Proxmox-Host, als `root`:
```bash
git clone <dieses-repo> pvesnap && cd pvesnap
./install.sh --with-webexplorer --port 8823
```
Ohne `--with-webexplorer` wird nur der Snapshot-Dienst eingerichtet; die
Web-Oberfläche lässt sich jederzeit nachrüsten. Einzelheiten:
[Installation](installation.md).
!!! note "Es passiert erst einmal nichts"
In der mitgelieferten Konfiguration stehen **alle Gruppen auf `enabled = no`**.
Der Dienst läuft, legt aber nichts an, bis du eine Gruppe einschaltest. Das
ist Absicht — niemand soll nach der Installation überrascht feststellen,
dass sein Storage vollläuft.
---
## 2. Gäste markieren
pvesnap sucht sich die Gäste über Proxmox-**Tags** aus. Das ist der bequemste
Weg, weil du danach nie wieder die Konfiguration anfassen musst: Eine neue VM
bekommt ihr Tag, und sie ist dabei.
In der Proxmox-Oberfläche bei jeder VM unter **Optionen → Tags**, oder auf der
Shell:
```bash
qm set 101 --tags produktion,datenbank
pct set 110 --tags produktion
```
Für den Anfang reicht ein einziges Tag, etwa `produktion`.
---
## 3. Eine Gruppe einschalten
```bash
pvesnap config
```
![Die Gruppenliste](bilder/config-gruppen.svg)
Mit den Pfeiltasten auf `taeglich`, dann:
<div class="ablauf" markdown>
`Leertaste` — schaltet die Gruppe ein (Spalte **Aktiv** springt auf `ja`)
`Enter` — öffnet sie, um Zeitplan und Auswahl zu prüfen
`s` — speichern
`r` — Dienst neu laden
</div>
In der Gruppe siehst du unten sofort, auf wie viele Gäste die Auswahl gerade
zutrifft — du musst also nicht raten, ob das Tag richtig geschrieben ist:
![Eine Gruppe bearbeiten](bilder/config-gruppe.svg)
---
## 4. Nachsehen, ob es stimmt
```bash
pvesnap check # meckert über Widersprüche in der Konfiguration
pvesnap vms # welche VM landet in welcher Gruppe?
pvesnap status # letzte und nächste Läufe
```
`pvesnap vms` ist der ehrlichste Test: Dort steht schwarz auf weiß, welche
Maschine gesichert wird und welche nicht.
---
## 5. Einmal von Hand auslösen
Nicht bis morgen früh warten wollen:
```bash
pvesnap run --force -g taeglich --dry-run # Probelauf, ändert nichts
pvesnap run --force -g taeglich # jetzt wirklich
```
Danach stehen die Snapshots in der Proxmox-Oberfläche unter **Snapshots**
mit Namen wie `auto-taeglich-20260809-023000` und einer Beschreibung, an der
man sie wiedererkennt.
---
## 6. Den Ernstfall einmal proben
Das ist der Schritt, den fast alle auslassen — und der im Ernstfall den
Unterschied macht. Hol dir jetzt, in Ruhe, eine Datei aus einem Snapshot:
```bash
pvesnap-explorer 101
```
Snapshot auswählen, mit den Pfeiltasten durch das Dateisystem, `Leertaste` zum
Markieren, `F5` kopiert nach rechts auf den Host.
![Dateien kopieren](bilder/explorer-kopieren.svg)
Wenn du das einmal gemacht hast, weißt du im Notfall, wo die Tasten liegen.
---
## Fertig. Und dann?
| | |
|---|---|
| Mehrere Zeitpläne nebeneinander | [Sichern](sichern/index.md) — stündlich für die Datenbank, monatlich für den Rest |
| Etwas herausgeben, ohne Shell-Zugang | [Die Web-Oberfläche](holen/web.md) |
| Eine ganze Maschine zurückholen | [Wiederherstellen](wiederherstellen/index.md) |
| Austauschlaufwerke vorbereiten | [Austauschlaufwerke](wiederherstellen/transfer.md) — *vor* dem Notfall, nicht mittendrin |
!!! tip "Der eine Rat, wenn du nur einen mitnimmst"
Leg dir **jetzt** ein Austauschlaufwerk an und pack die Werkzeuge hinein,
die du im Notfall brauchst — `pg_dump`, ein Packprogramm, deine Skripte.
Im Ernstfall ist keine Zeit, so etwas zusammenzusuchen, und die
wiederhergestellte Maschine hat kein Netzwerk.
+210
View File
@@ -0,0 +1,210 @@
# Welche Gäste?
Eine Gruppe sucht sich ihre Gäste über eine oder mehrere Regeln. Dabei gilt:
<div class="ablauf" markdown>
Alle Einschluss-Regeln wirken als **ODER** — wer *eine* davon erfüllt, ist dabei.
Alle Ausschluss-Regeln **gewinnen immer** — auch gegen `all = yes`.
</div>
```ini
all = yes # alle VMs und Container
vmids = 100,101,105-110 # nach ID, auch Bereiche
names = web-*, db-0? # nach Name, mit Platzhaltern
tags = produktion, wichtig # nach Proxmox-Tag
pools = Kunden # nach Proxmox-Pool
types = qemu # nur VMs (lxc = nur Container; leer = beides)
exclude_vmids = 999
exclude_names = *-test
exclude_tags = nosnap
```
Kontrollieren lässt sich das Ergebnis an zwei Stellen: unten in der
[Gruppenmaske](editor.md#eine-gruppe-bearbeiten) („Trifft aktuell zu auf …") und
mit `pvesnap vms`.
---
## Tags
**Der empfohlene Weg.** Die Auswahl steht dann bei der VM, nicht in der
Konfiguration — und eine neue Maschine ist mit einem Klick dabei, ohne dass
jemand `pvesnap.conf` anfassen muss.
```ini
tags = produktion
```
In Proxmox setzen:
```bash
qm set 101 --tags produktion,datenbank
pct set 110 --tags produktion
```
Oder in der Oberfläche unter **VM → Optionen → Tags**.
Groß- und Kleinschreibung ist egal; pvesnap vergleicht in Kleinbuchstaben.
Mehrere Tags an einer VM trennt Proxmox mit `;` oder `,` — beides wird gelesen.
!!! tip "Ein Tag je Zeitplan"
Bewährt hat sich, die Tags nach dem Sicherungsrhythmus zu benennen und
nicht nach der Funktion:
```ini
[group:stuendlich]
tags = stuendlich
[group:taeglich]
tags = produktion
```
Dann sieht man in der Proxmox-Oberfläche an jeder VM sofort, wie oft sie
gesichert wird — ohne die Konfiguration zu öffnen.
---
## VMIDs
```ini
vmids = 100,101,105-110,200
```
Einzelne IDs und Bereiche, durch Komma getrennt. Am bequemsten über die
[VM-Auswahl](editor.md#vms-aus-einer-liste-wahlen) im Editor.
Präzise, aber pflegeintensiv: Jede neue Maschine muss von Hand nachgetragen
werden. Für kleine, feste Zusammenstellungen in Ordnung, für alles Wachsende
sind Tags besser.
---
## Namensmuster
```ini
names = web-*, db-0?
```
| Zeichen | |
|---|---|
| `*` | beliebig viele Zeichen |
| `?` | genau ein Zeichen |
| `[abc]` | eines dieser Zeichen |
Praktisch bei sauberer Namenskonvention (`web-01`, `web-02`, `db-01`),
gefährlich ohne: Eine VM, die jemand `web-test-alt` nennt, fällt in `web-*`
hinein. Dagegen hilft ein `exclude_names = *-test`.
---
## Pools
```ini
pools = Kunden
```
Nach dem Proxmox-Pool. Sinnvoll, wenn die Pools ohnehin nach Kunde oder
Abteilung geschnitten sind — dann sichert eine Gruppe genau einen Mandanten.
---
## Gasttyp
```ini
types = qemu # nur virtuelle Maschinen
types = lxc # nur Container
types = # beides (Vorgabe)
```
Nützlich in Verbindung mit `vmstate = yes`, das ohnehin nur für QEMU gilt: Eine
Gruppe mit RAM-Sicherung für die VMs, eine ohne für die Container.
---
## Ausschlüsse
```ini
exclude_vmids = 999
exclude_names = *-test, temp-*
exclude_tags = nosnap, pvesnap-recovery
```
Ausschlüsse schlagen alles. Auch `all = yes`.
!!! success "Zwei Ausschlüsse, die in jede `all`-Gruppe gehören"
```ini
exclude_tags = nosnap, pvesnap-recovery
```
**`nosnap`** ist der Notausgang: Wer eine einzelne Maschine aus der
Sicherung nehmen will, setzt ihr das Tag — ohne die Konfiguration
anzufassen.
**`pvesnap-recovery`** trägt jede laufende
[Wiederherstellung](../wiederherstellen/index.md). Ohne den Ausschluss
bekämen diese kurzlebigen Maschinen eigene Snapshots — die dann wiederum
verhindern, dass sie sich sauber verwerfen lassen.
---
## Mehrere Regeln kombiniert
```ini
[group:taeglich]
tags = produktion
vmids = 300
exclude_names = *-test
exclude_tags = nosnap
```
Gelesen: *alles mit dem Tag `produktion`* **oder** *VM 300*, **aber nichts**,
was auf `-test` endet oder das Tag `nosnap` trägt.
---
## Gestoppte Gäste
```ini
skip_stopped = yes
```
Übergeht Gäste, die gerade aus sind. Vorgabe ist `no` — auch von einer
gestoppten Maschine wird ein Snapshot angelegt.
Beides ist vertretbar:
| | |
|---|---|
| `skip_stopped = no` | Der Stand ist gesichert, auch wenn die VM länger aus ist. Kostet bei kurzen Intervallen viele identische Snapshots. |
| `skip_stopped = yes` | Nur laufende Maschinen. Passt zu stündlichen Gruppen, spart Platz — aber eine über Wochen abgeschaltete VM bekommt gar nichts mehr. |
!!! note "Der Mischbetrieb"
Eine stündliche Gruppe mit `skip_stopped = yes`, eine monatliche mit
`skip_stopped = no`. Dann bekommt jede Maschine mindestens den monatlichen
Stand, und die laufenden zusätzlich die feine Auflösung.
---
## Nachsehen, was herauskommt
```bash
pvesnap vms
```
```
VMID Name Status Gruppen
100 web01 running stuendlich, taeglich, monatlich
101 db01 running stuendlich, taeglich, monatlich
120 build-test stopped (keine Gruppe)
```
Dasselbe im Editor unter Taste ++v++:
![Übersicht: welche VM in welcher Gruppe](../bilder/config-uebersicht.svg)
+175
View File
@@ -0,0 +1,175 @@
# Im Betrieb
## Der Dienst
```bash
systemctl status pvesnap # läuft er?
systemctl reload pvesnap # Konfiguration neu einlesen (SIGHUP)
systemctl restart pvesnap # kompletter Neustart
journalctl -u pvesnap -f # Protokoll mitlesen
```
`reload` ist der Normalfall nach einer Änderung: Der Dienst liest die
Konfiguration neu ein, ohne laufende Vorgänge abzubrechen und ohne den
gemerkten Zustand zu verlieren.
---
## Nachsehen
```bash
pvesnap status # Gruppen, letzte und nächste Läufe
pvesnap vms # welche VM landet in welcher Gruppe
pvesnap list # vorhandene pvesnap-Snapshots (-a = auch fremde)
pvesnap check # Konfiguration prüfen
```
### `pvesnap check`
Der Befehl, den man nach jeder Änderung laufen lassen sollte. Er findet unter
anderem:
* Gruppen ohne `keep_count` **und** ohne `keep_time` — die würden endlos wachsen
* zwei Gruppen mit demselben Kurznamen — die räumten sich gegenseitig ab
* Zeitpläne, die sich widersprechen (`interval` und `schedule` zugleich)
* eine Auswahl, die auf keinen einzigen Gast passt
* [Sandbox-Optionen in der systemd-Unit](../installation.md#die-systemd-unit),
an denen später jeder Snapshot scheitern würde
---
## Von Hand auslösen
```bash
pvesnap run # jetzt fällige Gruppen ausführen
pvesnap run --force -g taeglich # diese Gruppe sofort, egal ob fällig
pvesnap run --force --dry-run # Probelauf, ändert nichts
pvesnap prune -g stuendlich # nur aufräumen
```
`--force` heißt „auch wenn nicht fällig". Ohne das passiert bei einem Aufruf
zwischen zwei Terminen schlicht nichts.
!!! note "Parallel zum Dienst"
Ein manueller `pvesnap run` ist auch möglich, während der Dienst läuft.
Beide teilen sich eine Sperre und kommen sich nicht in die Quere — der
zweite wartet, bis der erste fertig ist.
### Eine andere Konfiguration ausprobieren
```bash
pvesnap -c /root/test.conf check
pvesnap -c /root/test.conf run --force --dry-run
```
`-c` gilt für alle Unterbefehle. Zusammen mit `--dry-run` ein gefahrloser Weg,
eine neue Gruppenstruktur durchzuspielen, bevor sie scharf geschaltet wird.
---
## Der Probelauf
```ini
[global]
dry_run = yes
```
oder einmalig:
```bash
pvesnap run --force --dry-run
```
Protokolliert, was geschähe, ohne etwas anzulegen oder zu löschen:
```
[TESTLAUF] wuerde Snapshot anlegen: VM 101 (db01) -> auto-taeglich-20260809-023000
[TESTLAUF] wuerde Snapshot loeschen: VM 101 (db01) -> auto-taeglich-20260719-023000
```
Besonders die zweite Zeilenart ist es wert, einmal angesehen zu werden, bevor
eine neue Vorhaltezeit produktiv geht.
---
## Protokoll
```ini
[global]
log_level = INFO
# log_file = /var/log/pvesnap.log
```
| Stufe | |
|---|---|
| `DEBUG` | jeder `pvesh`-Aufruf mit Argumenten — für die Fehlersuche |
| `INFO` | was angelegt und gelöscht wird (Vorgabe) |
| `WARNING` | nur Auffälligkeiten |
| `ERROR` | nur Fehler |
Ohne `log_file` geht alles ins Journal:
```bash
journalctl -u pvesnap -f # mitlesen
journalctl -u pvesnap --since "-1h" # letzte Stunde
journalctl -u pvesnap -p err # nur Fehler
journalctl -u pvesnap --since today | grep -i fehl
```
---
## Was im Protokoll normal ist
Nicht jede Meldung ist ein Problem:
| Meldung | Bedeutung |
|---|---|
| `storage does not support snapshots` | Diese VM liegt auf LVM-thick oder einem `raw`-Image. Sie wird übersprungen, die übrigen laufen weiter. |
| `got lock request timeout` | Das Storage war gerade belegt. Wird `retries`-mal wiederholt — erst danach ist es ein Fehler. |
| `VM is locked (backup)` | Ein `vzdump` läuft gerade. Ebenfalls vorübergehend. |
Dauerhaft wiederkehrende Lock-Fehler sind dagegen ein Zeichen — siehe
[Fehlersuche](../nachschlagen/fehlersuche.md#storage-lock).
---
## Wenn viele VMs auf demselben Storage liegen
```ini
[global]
pause_between = 10s # Pause zwischen zwei Gästen
retries = 2 # zusätzliche Versuche je Snapshot
retry_delay = 60s # Wartezeit davor
task_timeout = 15m # Geduld mit einem einzelnen Proxmox-Task
```
`pause_between` nimmt Druck vom Storage-Lock, wenn dreißig Maschinen im selben
Pool nacheinander drankommen. Vorgabe ist `0s`; bei Problemen sind `5s` bis
`15s` ein guter erster Versuch.
---
## Überwachen
Ein einfacher Weg, im Blick zu behalten, ob die Sicherung wirklich läuft:
```bash
# Gab es in den letzten 25 Stunden einen taeglichen Snapshot?
pvesnap list | grep auto-taeglich | tail -1
```
Oder gezielt über den Dienstzustand:
```bash
systemctl is-active pvesnap # active / failed
journalctl -u pvesnap --since "-25h" -p err -q # leer = keine Fehler
```
!!! warning "Der Klassiker"
Der Dienst läuft, das Protokoll ist ruhig — und trotzdem entsteht nichts,
weil alle Gruppen auf `enabled = no` stehen. Genau so wird ausgeliefert.
`pvesnap status` zeigt es sofort: Eine ausgeschaltete Gruppe hat keinen
nächsten Termin.
+186
View File
@@ -0,0 +1,186 @@
# Der Konfigurationseditor
```bash
pvesnap config
```
Braucht `root` — der Editor schreibt nach `/etc/pvesnap/` und fragt Proxmox nach
dem Inventar.
Alles, was hier geht, geht auch von Hand in der INI-Datei. Der Editor hat aber
zwei Vorteile, die man beim Tippen nicht hat: Er zeigt **live, auf wie viele
Gäste eine Auswahl gerade zutrifft**, und er rechnet den **nächsten Termin**
aus, bevor man speichert.
---
## Die Gruppenliste
![Die Gruppenliste](../bilder/config-gruppen.svg)
Der Einstiegsbildschirm. Oben steht die Datei, an der gearbeitet wird, und
rechts daneben, ob der Dienst gerade läuft. Ein `*` vor dem Dateinamen bedeutet:
ungespeicherte Änderungen.
| Taste | |
|---|---|
| ++enter++ | Gruppe bearbeiten |
| ++space++ | Gruppe ein- oder ausschalten |
| ++n++ | neue Gruppe |
| ++c++ | Gruppe kopieren — der schnellste Weg zu einer Variante |
| ++d++ | Gruppe löschen |
| ++g++ | [globale Einstellungen](#globale-einstellungen) |
| ++v++ | [Übersicht: welche VM in welcher Gruppe?](#die-ubersicht) |
| ++s++ | speichern |
| ++r++ | [Dienst neu laden](#dienst-neu-laden) |
| ++q++ | Ende |
Die Spalte **Auswahl** fasst zusammen, wie die Gruppe ihre Gäste findet —
`Tags: produktion`, `alle VMs`, `VMIDs: 100-110`. Wer viele Gruppen hat, sieht
hier auf einen Blick, wo eine Maschine hineinfallen könnte.
---
## Eine Gruppe bearbeiten
![Eine Gruppe bearbeiten](../bilder/config-gruppe.svg)
Eine lange Maske in vier Abschnitten: **Allgemein**, **Zeitplan**,
**Vorhaltezeit**, **Welche VMs?**, **Snapshot-Optionen**. Pfeiltasten bewegen
sich, ++enter++ ändert ein Feld, ++space++ schaltet Ja/Nein um.
Zwei Zeilen darin sind keine Einstellungen, sondern Antworten:
!!! tip "Nächster Termin (Vorschau)"
```
Naechster Termin (Vorschau) Mo 10.08.2026 02:30 (taeglich um 02:30)
```
Rechnet sofort nach, was der eingestellte Zeitplan bedeutet. Damit fällt
ein Denkfehler auf, bevor er drei Wochen lang unbemerkt bleibt — etwa
`day_of_month = 31` in einem 30-Tage-Monat.
!!! tip "Trifft aktuell zu auf"
```
Trifft aktuell zu auf 6 Gast/Gaeste (100, 101, 102, 105, 110, 130)
```
Die Auswahlregeln, angewendet auf das echte Inventar — mitsamt VMIDs. Steht
dort `0 Gast/Gaeste`, ist meist ein Tag falsch geschrieben.
Oben in der Kopfzeile steht außerdem, wie die Snapshots dieser Gruppe heißen
werden:
```
Kurzname im Snapshot: auto-taeglich-JJJJMMTT-HHMMSS
```
---
## VMs aus einer Liste wählen
Auf dem Feld **VMs aus Liste wählen …** öffnet ++enter++ das Inventar; aus der
Gruppenmaske heraus geht auch direkt ++v++:
![Die VM-Auswahl](../bilder/config-vms.svg)
| Taste | |
|---|---|
| ++space++ | Gast an- oder abwählen |
| ++a++ | alle |
| ++n++ | keine |
| ++i++ | Auswahl umkehren |
| ++slash++ | filtern (Name, VMID oder Tag) |
| ++enter++ | übernehmen |
Die Spalte **Tags** ist der eigentliche Grund, hier hereinzuschauen: Sie zeigt,
womit die Gäste in Proxmox markiert sind — und damit, ob eine Auswahl über
`tags = …` besser wäre als eine feste Liste von VMIDs.
!!! note "Feste Listen altern schlecht"
Eine Auswahl über VMIDs muss bei jeder neuen VM angefasst werden. Eine
Auswahl über Tags nicht. Für alles, was länger als ein paar Wochen leben
soll, sind [Tags](auswahl.md#tags) die bessere Wahl.
---
## Globale Einstellungen
Taste ++g++ in der Gruppenliste:
![Globale Einstellungen](../bilder/config-global.svg)
Das gilt für alle Gruppen — Präfix, Prüfintervall, Wiederholungen bei belegter
Sperre, Standard-Beschreibung. Was welcher Wert genau bedeutet, steht unter
[Alle Einstellungen](../nachschlagen/konfiguration.md#global).
Der wichtigste ist **Testlauf (nichts wirklich tun)** — das `dry_run = yes` der
INI-Datei. Damit protokolliert der Dienst nur, was er täte. Zum Ausprobieren
einer neuen Konfiguration ohne Risiko.
---
## Die Übersicht
Taste ++v++ in der Gruppenliste:
![Übersicht: welche VM in welcher Gruppe](../bilder/config-uebersicht.svg)
Die Gegenprobe von der anderen Seite: nicht „welche Gäste hat diese Gruppe",
sondern **„in welchen Gruppen steckt dieser Gast"**. Wer hier steht, wird
gesichert. Wer `(keine Gruppe)` daneben stehen hat, nicht.
Das ist der Bildschirm, den man nach jeder Änderung einmal ansehen sollte. Er
deckt beide typischen Fehler auf: eine vergessene Maschine — und eine, die
versehentlich in vier Gruppen gleichzeitig liegt.
!!! example "Was hier auffällt"
Im Bild trägt `build-test` das Tag `nosnap` und fällt damit überall heraus —
gewollt. Die beiden Maschinen `db01-live` und `warenwirtschaft-w` sind
laufende [Wiederherstellungen](../wiederherstellen/index.md); sie stehen
ebenfalls in keiner Gruppe, weil die monatliche Gruppe
`exclude_tags = nosnap, pvesnap-recovery` gesetzt hat.
**Das ist ein Griff, der sich lohnt.** Ohne ihn bekämen kurzlebige
Wiederherstellungsmaschinen eigene Snapshots — die dann wiederum verhindern,
dass ihre Klone sauber verworfen werden können.
---
## Dienst neu laden
Taste ++r++ — entspricht `systemctl reload pvesnap`, ohne den Editor zu
verlassen. Der Editor nimmt einem dabei das Mitdenken ab:
<div class="ablauf" markdown>
Ungespeicherte Änderungen? → bietet vorher das Speichern an
Dienst gestoppt? → bietet das Starten an
Neuladen scheitert? → bietet einen Neustart an
</div>
Nach ++s++ fragt er ohnehin gleich, ob neu geladen werden soll. In der Kopfzeile
steht jederzeit, ob der Dienst läuft.
---
## Beim Speichern
!!! warning "Die INI-Datei wird neu geschrieben"
Eigene Kommentare gehen dabei verloren. Ein `[defaults]`-Abschnitt
ebenfalls — inhaltlich bleiben die Werte erhalten, sie stehen danach nur in
jeder Gruppe einzeln.
Eine Sicherung der alten Fassung wird als **`pvesnap.conf.bak`** abgelegt.
Wer eine handgepflegte Konfiguration mit vielen Kommentaren hat, bearbeitet sie
besser weiter im Texteditor. Der ncurses-Editor ist für alle anderen da — und
für die beiden Vorschauzeilen, die es im Texteditor nicht gibt.
+176
View File
@@ -0,0 +1,176 @@
# Sichern
Alles, was pvesnap tut, steckt in **Gruppen**. Eine Gruppe beantwortet drei
Fragen:
<div class="ablauf" markdown>
**Wann?** — [Zeitplan](zeitplan.md): alle 30 Minuten, täglich um 02:30, am
Monatsersten
**Wer?** — [Auswahl](auswahl.md): diese VMIDs, alles mit dem Tag `produktion`,
alle außer `nosnap`
**Wie lange?** — [Vorhaltezeit](vorhaltezeit.md): die letzten 24, nichts älter
als zwei Tage
</div>
Davon darf es beliebig viele geben, und eine VM darf in mehreren stecken. Genau
das ist der Sinn: Die Datenbank bekommt stündliche Snapshots mit kurzer
Vorhaltezeit, dieselbe Datenbank zusätzlich einen monatlichen, der ein Jahr
liegen bleibt.
---
## Ein typischer Aufbau
```ini
[group:stuendlich]
interval = 1h
align = yes # an der Uhr: 00:00, 01:00, 02:00 ...
keep_count = 24
keep_time = 2d
tags = stuendlich
skip_stopped = yes
[group:taeglich]
schedule = daily
at = 02:30
keep_count = 14
keep_time = 21d
tags = produktion
exclude_tags = nosnap
[group:monatlich]
schedule = monthly
day_of_month = 1
at = 04:00
keep_count = 6
keep_time = 400d
all = yes
exclude_tags = nosnap
```
Drei Gruppen, drei Zeithorizonte. Die Datenbank mit den Tags
`produktion,stuendlich` landet in allen dreien — und hat damit den Stand von
vor einer Stunde, von gestern Nacht und vom Monatsersten.
So sieht das im Editor aus:
![Die Gruppenliste](../bilder/config-gruppen.svg)
---
## Namensschema
Jeder Snapshot heißt nach demselben Muster:
```
auto-taeglich-20260809-023000
│ │ │ └── Uhrzeit
│ │ └────────── Datum
│ └──────────────────── Kurzname der Gruppe
└─────────────────────────── prefix aus [global]
```
Das ist keine Kosmetik, sondern die **Sicherheitsgrenze** des ganzen Programms.
!!! success "pvesnap löscht ausschließlich, was exakt auf dieses Muster passt"
Und zusätzlich muss der Gruppen-Kurzname zu einer Gruppe gehören, die
gerade aufräumt. Ein Snapshot namens `vor-update-14.2` oder
`bkp_20260101` wird nie angefasst — egal wie alt er ist, egal wie voll das
Storage läuft.
Umlaute und Sonderzeichen im Gruppennamen werden für den Kurznamen
umgeschrieben (`täglich``taeglich`). Ergäben zwei Gruppen denselben
Kurznamen, meldet das `pvesnap check` — sonst würden sie sich gegenseitig die
Snapshots wegräumen.
!!! warning "Gruppe umbenennen"
Der neue Name bekommt einen eigenen Kurznamen. Snapshots unter dem alten
Namen passen dann zu keiner Gruppe mehr und werden **nicht mehr
aufgeräumt** — sie bleiben für immer liegen. Entweder vorher aufräumen
lassen oder die alten hinterher von Hand entfernen.
---
## Der Ablauf eines Laufs
Der Dienst schaut alle `check_interval` (Vorgabe: 60 s) nach, ob eine Gruppe
fällig ist. Ist sie es:
1. **Gäste bestimmen** — nach den Auswahlregeln der Gruppe
2. **Snapshot anlegen**, ein Gast nach dem anderen; dazwischen optional
`pause_between`
3. **Warten, bis Proxmox wirklich fertig ist** — nicht nur, bis der Aufruf
zurückkommt
4. **Aufräumen** — alles, was `keep_count` oder `keep_time` reißt
Schritt 3 ist wichtiger, als er aussieht. Proxmox nimmt beim Anlegen eine
Sperre auf dem Storage (`cfs-lock 'storage-<name>'`). Würde pvesnap gleich die
nächste VM anstoßen, sperrten sich die eigenen Läufe gegenseitig aus. Mehr dazu
unter [Fehlersuche](../nachschlagen/fehlersuche.md#storage-lock).
Wird ein Termin verpasst, weil der Host aus war, wird er beim nächsten Start
**nachgeholt** — einmal, nicht für jeden ausgefallenen Termin einzeln.
---
## Was in einen Snapshot gehört
| Einstellung | Vorgabe | |
|---|---|---|
| `vmstate` | `no` | Arbeitsspeicher mitsichern — nur QEMU, nur bei laufender VM |
| `skip_stopped` | `no` | gestoppte Gäste überspringen |
| `description` | siehe unten | Beschreibung, die am Snapshot hängt |
### `vmstate` — der Arbeitsspeicher
Mit `vmstate = yes` wird der RAM mitgeschrieben. Die Maschine friert dafür kurz
ein, und es braucht Platz in Höhe des zugewiesenen Arbeitsspeichers — bei einer
32-GB-Datenbank also 32 GB **pro Snapshot**.
Für regelmäßige Läufe ist das meist zu teuer. Es hat aber einen konkreten
Nutzen: Beim [Wiederherstellen](../wiederherstellen/index.md) läuft die
Maschine dann genau dort weiter, wo sie stand — statt zu booten wie nach einem
Stromausfall. Datenbanken sind dabei bereits offen und konsistent.
!!! tip "Der Mittelweg"
Eine eigene Gruppe mit `vmstate = yes`, die einmal täglich läuft und nur
zwei Stände vorhält. Kostet zweimal RAM-Größe und gibt im Ernstfall einen
warmen Einstiegspunkt.
### Die Beschreibung
Sie steht in der Proxmox-Oberfläche neben jedem Snapshot:
```ini
description = pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time} | max: {keep_count}
```
Verfügbare Platzhalter:
| | | |
|---|---|---|
| `{group}` | `{group_slug}` | `{schedule}` |
| `{vmid}` | `{name}` | `{node}` |
| `{type}` | `{pool}` | `{tags}` |
| `{date}` | `{time}` | `{datetime}` |
| `{timestamp}` | `{keep_time}` | `{keep_count}` |
Ein unbekannter Platzhalter führt nicht zum Abbruch — es wird dann eine
brauchbare Ersatzbeschreibung eingesetzt.
---
## Weiter
* [Der Konfigurationseditor](editor.md) — alles das mit Menüs statt INI-Datei
* [Zeitpläne](zeitplan.md)
* [Welche Gäste?](auswahl.md)
* [Vorhaltezeit](vorhaltezeit.md)
* [Im Betrieb](betrieb.md) — Dienst, Protokoll, Probelauf
+159
View File
@@ -0,0 +1,159 @@
# Vorhaltezeit
Ohne Aufräumen wächst die Zahl der Snapshots ins Unendliche — und jeder hält
alte Blöcke fest. Die Vorhaltezeit ist deshalb keine Kür.
```ini
keep_count = 24 # höchstens 24 Snapshots je VM und Gruppe (0 = unbegrenzt)
keep_time = 7d # nichts älter als 7 Tage (0 = unbegrenzt)
keep_min = 1 # so viele bleiben in jedem Fall stehen
```
---
## Die beiden Grenzen
Beide sind kombinierbar. Gelöscht wird, was **eine** der beiden reißt — es ist
ein ODER, kein UND.
!!! example "Beispiel: `keep_count = 24`, `keep_time = 2d`"
Eine stündliche Gruppe. Nach zwei Tagen sind 48 Snapshots angefallen —
`keep_count` greift und lässt nur die letzten 24 stehen.
Läuft eine VM zwei Wochen lang nicht (mit `skip_stopped = yes`), sind ihre
Snapshots irgendwann älter als zwei Tage — `keep_time` greift und räumt sie
weg, auch wenn es weniger als 24 sind.
**Mindestens eine der beiden muss gesetzt sein.** Sonst würde die Zahl der
Snapshots unbegrenzt wachsen; `pvesnap check` meldet das als Fehler.
---
## `keep_min` — die Untergrenze
```ini
keep_min = 1
```
So viele Snapshots bleiben in jedem Fall stehen, auch wenn sie beide Grenzen
reißen.
Der Sinn: `keep_time = 2d` würde bei einer Maschine, die drei Tage aus war,
**alles** löschen — und damit den letzten bekannten Stand. Mit `keep_min = 1`
bleibt immer einer übrig.
!!! tip "Eine Empfehlung"
`keep_min = 1` in jeder Gruppe mit `keep_time`. Es kostet fast nichts und
verhindert den einen Fall, in dem das Aufräumen genau das wegräumt, was man
gebraucht hätte.
---
## Getrennt je VM und Gruppe
Beide Grenzen zählen **pro Gast und pro Gruppe**, nicht insgesamt.
`keep_count = 24` bei 30 Maschinen heißt also: bis zu 720 Snapshots — aber je
Maschine nur 24. Und eine VM, die in drei Gruppen steckt, hat 24 + 14 + 6
Snapshots nebeneinander, die sich nicht gegenseitig verdrängen.
---
## Was pvesnap anfasst — und was nicht
Aufgeräumt wird ausschließlich, was zum
[Namensschema](index.md#namensschema) der jeweiligen Gruppe passt:
```
auto-taeglich-20260809-023000 ← wird von der Gruppe "taeglich" gezählt
auto-stuendlich-20260809-100000 ← zählt für "stuendlich", nicht für "taeglich"
vor-update-14.2 ← wird nie angefasst
```
!!! success "Von Hand angelegte Snapshots bleiben"
Ein Snapshot, den jemand in der Proxmox-Oberfläche angelegt hat, passt nie
auf das Muster. Er zählt weder gegen `keep_count`, noch wird er jemals
gelöscht.
Das hat eine Kehrseite: Er wird auch nicht aufgeräumt. Wer sich vor einem
Update einen Sicherheitsstand anlegt, sollte ihn hinterher selbst wieder
entfernen — sonst hält er ewig alte Blöcke fest.
---
## Nur aufräumen, ohne Neues anzulegen
```bash
pvesnap prune # alle Gruppen
pvesnap prune -g stuendlich # nur diese
pvesnap prune --dry-run # zeigt, was wegkäme
```
Praktisch nach dem Verkürzen einer Vorhaltezeit: Der Dienst würde erst beim
nächsten regulären Termin aufräumen, `prune` macht es sofort.
---
## Platz im Blick behalten
```bash
pvesnap list # alle pvesnap-Snapshots
pvesnap list -a # auch fremde
```
Und auf dem Storage selbst:
=== "Ceph / RBD"
```bash
ceph df # MAX AVAIL beachten, nicht nur % RAW USED
rbd -p <pool> du # PROVISIONED gegen USED
```
=== "ZFS"
```bash
zfs list -o space # USEDSNAP zeigt, was die Snapshots kosten
```
=== "LVM-thin"
```bash
lvs -o lv_name,data_percent,metadata_percent
```
!!! danger "Ein volles Storage blockiert alles"
Läuft ein Ceph-Pool oder ein Thin-Pool voll, stehen **alle** Volumes darin
— nicht nur die schreibfreudige VM. Und das Aufräumen wird dann selbst
schwierig, weil Löschen ebenfalls ein Schreibvorgang ist.
Bei Ceph ist `MAX AVAIL` die Zahl, auf die es ankommt; `% RAW USED` täuscht,
weil es die Replikation nicht berücksichtigt.
---
## Wie viel kostet ein Snapshot?
Anfangs: nichts. Ein Snapshot ist ein Zeiger, kein Abbild.
Er wächst mit dem, was **danach** geschrieben wird — jeder geänderte Block
bedeutet, dass der alte Stand zusätzlich vorgehalten werden muss.
| VM | Verhalten |
|---|---|
| Ein Fileserver, auf dem selten geschrieben wird | Snapshots kosten fast nichts, lange Vorhaltezeiten sind billig |
| Eine Datenbank mit ständigem Schreibverkehr | Jeder Snapshot wächst spürbar; kurze Vorhaltezeit, dafür engmaschig |
| Eine VM, die gerade ein Update bekommt | Ein einzelner Snapshot kann in Minuten mehrere GB kosten |
Deshalb passen kurze Intervalle und lange Vorhaltezeiten schlecht zusammen. Ein
guter Ausgangspunkt:
```ini
[group:stuendlich] keep_count = 24 keep_time = 2d
[group:taeglich] keep_count = 14 keep_time = 21d
[group:monatlich] keep_count = 6 keep_time = 400d
```
+129
View File
@@ -0,0 +1,129 @@
# Zeitpläne
Jede Gruppe hat **entweder** ein Intervall **oder** einen festen Termin. Beides
gleichzeitig geht nicht — `pvesnap check` weist darauf hin.
---
## Intervall
```ini
interval = 30m # 30m, 1h, 6h, 2d12h, 1w ...
align = yes
```
Zeitangaben gelten überall im selben Format: `30m`, `1h`, `6h`, `2d12h`, `1w`.
`0` heißt „unbegrenzt".
Der Unterschied steckt in `align`:
| | |
|---|---|
| `align = yes` | **an der Uhr ausgerichtet** — 00:00, 00:30, 01:00 … Die Snapshots liegen auf runden Zeiten, unabhängig davon, wann der Dienst gestartet wurde. |
| `align = no` | **30 Minuten nach dem letzten Lauf.** Startet der Dienst um 14:07 neu, liegen die Snapshots danach bei :07 und :37. |
Für alles, was regelmäßig aussehen soll, ist `align = yes` die richtige Wahl.
`align = no` ist nur dann sinnvoll, wenn der Abstand wichtiger ist als der
Zeitpunkt.
---
## Fester Termin
```ini
schedule = daily
at = 02:30
```
| `schedule` | zusätzliche Angaben | ergibt |
|---|---|---|
| `hourly` | `minute = 15` | jede Stunde um :15 |
| `daily` | `at = 02:30` | täglich um 02:30 |
| `weekly` | `at`, `day_of_week = so` | sonntags um 03:00 |
| `monthly` | `at`, `day_of_month = 1` | am 1. jedes Monats |
| `yearly` | `at`, `day_of_month`, `month` | einmal jährlich |
Wochentage: `mo di mi do fr sa so` (auch `mon`, `tue` … funktionieren).
### Der 31. in kurzen Monaten
```ini
schedule = monthly
day_of_month = 31
```
Wird automatisch auf den **letzten Tag des Monats** gezogen — im Februar also
auf den 28. bzw. 29. Kein ausgefallener Lauf, keine Sonderbehandlung nötig.
---
## Verpasste Termine
Läuft der Host zum Termin nicht, wird der Lauf beim nächsten Start
**nachgeholt**. Einmal — nicht für jeden ausgefallenen Termin einzeln.
Ein Host, der eine Woche aus war, legt beim Hochfahren also einen täglichen
Snapshot an, nicht sieben.
Merken tut sich das die Zustandsdatei:
```ini
[global]
state_file = /var/lib/pvesnap/state.json
```
Wird sie gelöscht, gilt jede Gruppe als „noch nie gelaufen" und wird beim
nächsten Prüfintervall sofort ausgeführt.
---
## Beim Start sofort loslegen
```ini
[global]
run_on_start = no
```
Mit `yes` macht der Dienst direkt nach dem Start einen Durchlauf, statt auf den
nächsten regulären Termin zu warten.
Auf einem Host, der oft neu startet, führt das zu vielen zusätzlichen
Snapshots. Vorgabe ist deshalb `no`.
---
## Die Zeitumstellung
!!! warning "Gerechnet wird mit lokaler Zeit"
Ein täglicher Termin um 02:30 kann in der Umstellungsnacht **ausfallen**
(im Frühjahr existiert 02:30 nicht) oder **doppelt anstehen** (im Herbst
gibt es sie zweimal).
Wen das stört, legt den Termin auf eine Zeit außerhalb des Fensters —
04:00 statt 02:30 — oder nimmt ein Intervall statt eines festen Termins.
---
## Wie oft ist richtig?
Es gibt keine allgemeingültige Antwort, aber eine brauchbare Faustregel: Der
Abstand ist die Datenmenge, die du im Ernstfall verlierst.
| Abstand | Verlust im schlimmsten Fall | Passt zu |
|---|---|---|
| 15 min | eine Viertelstunde Arbeit | Datenbanken, Warenwirtschaft |
| 1 h | eine Stunde | Anwendungsserver |
| täglich | ein Arbeitstag | Fileserver, Infrastruktur |
| monatlich | — | der lange Rückweg, „wie war es vor dem Update?" |
Was dagegen spricht, öfter zu sichern, ist der **Platz**: Jeder Snapshot hält
die alten Blöcke fest. Bei einer schreibfreudigen VM wächst das schnell. Deshalb
gehören kurze Intervalle immer mit kurzer [Vorhaltezeit](vorhaltezeit.md)
zusammen — 24 stündliche Snapshots über zwei Tage sind billiger als sieben
tägliche über eine Woche.
!!! tip "Gestoppte Gäste überspringen"
Bei kurzen Intervallen lohnt sich `skip_stopped = yes`. Sonst sammelt eine
abgeschaltete Test-VM stündlich Snapshots vom immer gleichen Zustand.
+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="12" fill="#37474f"/>
<circle cx="32" cy="34" r="15" fill="none" stroke="#4db6ac" stroke-width="4"/>
<circle cx="32" cy="34" r="6" fill="#4db6ac"/>
<path d="M22 16h20l-3 6H25z" fill="#4db6ac"/>
<path d="M32 26v8l6 3" fill="none" stroke="#263238" stroke-width="3"
stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 406 B

+45
View File
@@ -0,0 +1,45 @@
/* Bildschirmfotos aus dem Terminal.
Sie sind SVG, also beliebig skalierbar - eine feste Breite wuerde sie nur
unnoetig klein halten. Der Rahmen soll sich in beiden Farbschemata absetzen. */
.md-typeset img[src$=".svg"] {
width: 100%;
max-width: 100%;
display: block;
border-radius: 8px;
box-shadow: 0 2px 14px rgba(0, 0, 0, .28);
margin: 1.2em 0 .6em;
}
/* Die Bildunterschrift direkt darunter, kleiner und ruhiger. */
.md-typeset figure {
margin: 1.4em 0;
}
.md-typeset figcaption {
font-size: .78rem;
color: var(--md-default-fg-color--light);
margin-top: -.2em;
text-align: left;
}
/* Tastenkuerzel in Tabellen nicht umbrechen lassen. */
.md-typeset table kbd,
.md-typeset table code {
white-space: nowrap;
}
/* Die Tabellen im Nachschlageteil sind breit - lieber scrollen als quetschen. */
.md-typeset__table {
width: 100%;
}
/* Ein ruhiger Kasten fuer die "So geht es"-Ablaeufe. */
.md-typeset .ablauf {
border-left: 3px solid var(--md-accent-fg-color);
padding: .1em 0 .1em 1em;
margin: 1.2em 0;
}
.md-typeset .ablauf p {
margin: .3em 0;
}
+176
View File
@@ -0,0 +1,176 @@
# Damit arbeiten
++enter++ auf einer Maschine in der Übersicht öffnet die Detailansicht. Das ist
der Bildschirm, auf dem man im Ernstfall die meiste Zeit verbringt:
![Die Detailansicht](../bilder/recovery-detail.svg)
| Taste | |
|---|---|
| ++s++ | starten |
| ++h++ | [herunterfahren](#herunterfahren) |
| ++e++ | [Austauschlaufwerk auswerfen / einklinken](transfer.md#im-laufenden-betrieb-wechseln) |
| ++v++ | [Austauschlaufwerke verwalten](transfer.md) |
| ++p++ | [SPICE und USB](dongle.md#nachtraglich-umstellen) |
| ++f++ | [vom Quell-Snapshot lösen](loesen.md) |
| ++x++ | [verwerfen](verwerfen.md) |
| ++q++ | zurück |
---
## An die Konsole kommen
In der Detailansicht steht die fertige Adresse:
```
https://10.20.0.11:8006/?console=kvm&novnc=1&vmid=9101&node=pve1&resize=off&cmd=
```
Dasselbe liefert `pvesnap-recovery console 9101`. Es ist die ganz normale
noVNC-Konsole von Proxmox — die Maschine taucht auch in der Weboberfläche auf,
markiert mit dem Tag `pvesnap-recovery`.
Bei Containern geht zusätzlich `pct enter 9101` auf dem Host.
!!! info "Warum eine IP-Adresse und kein Name?"
Vorne steht bewusst die **IP** des Nodes, nicht sein Name: Auf dem
Proxmox-Host löst der Name auf, am Arbeitsplatz meist nicht. Sie kommt aus
`/etc/pve/.members`.
Der Node hinter `node=` bleibt der Name — so erwartet Proxmox ihn.
Für SPICE und USB-Weiterleitung siehe [Dongle und USB](dongle.md).
---
## Was man drinnen tut
Die Maschine ist voll beschreibbar. Alles, was man an einer echten Maschine
täte, geht auch hier — nur eben ohne Netz.
Der typische Ablauf für eine Datenbank:
<div class="ablauf" markdown>
**1.** Im Gast prüfen, ob der Dienst läuft — mit geladenem Arbeitsspeicher ist
er bereits offen
**2.** Dump schreiben, auf das [Austauschlaufwerk](transfer.md):
`pg_dump -Fc kunden > /media/DUMPS/kunden.dump`
**3.** Im Gast aushängen (`umount`, unter Windows „Auswerfen")
**4.** In pvesnap ++e++ — Laufwerk auswerfen
**5.** ++v++ ++enter++ — Commander auf das Laufwerk, Dump abholen
**6.** ++x++ — Maschine verwerfen
</div>
Schritt 3 ist der, den man vergisst. Mehr dazu unter
[Austauschlaufwerke](transfer.md#im-laufenden-betrieb-wechseln).
---
## Herunterfahren
Taste ++h++ fährt **sauber** herunter — und schaltet nicht von selbst hart ab.
Weigert sich der Gast, kommt eine Wahl:
| | |
|---|---|
| **Im Gast selbst herunterfahren** | pvesnap wartet und meldet, wenn sie aus ist |
| **Hart ausschalten** | wie Stecker ziehen, mit ausdrücklicher Bestätigung |
| **Abbrechen** | läuft weiter |
!!! quote "Warum nicht einfach nach 60 Sekunden abschalten?"
Weil ein stilles Abschalten nach Zeitablauf ein Stromausfall mit Ansage
wäre — und genau der Grund, warum ein Dateisystem hinterher unsauber ist.
Wenn man gerade dabei ist, aus einer Maschine konsistente Daten zu holen,
ist das die falsche Voreinstellung.
Beim Warten zeigt pvesnap, was zu tun ist, und verfolgt den Zustand:
```
Bitte jetzt IM GAST herunterfahren.
Windows Start -> Ein/Aus -> Herunterfahren
Linux poweroff bzw. shutdown -h now
Zustand: running seit 1:23
```
++q++ bricht das Warten ab — die Maschine läuft dann weiter.
---
## Neu starten
Ein Neustart wird nötig, wenn sich die [Grafikkarte ändert](dongle.md) — also
beim Ein- oder Ausschalten von SPICE. Dann kommt dieselbe Wahl:
![Wie soll die Maschine neu starten?](../bilder/recovery-neustartwahl.svg)
| | |
|---|---|
| **Ich fahre im Gast herunter** | pvesnap wartet, startet danach selbst wieder — **der verlässliche Weg** |
| **Proxmox herunterfahren lassen (ACPI)** | klappt bei Linux, bei Windows oft nicht |
| **Gar nicht** | später selbst; die Änderung greift beim nächsten Start |
!!! warning "ACPI und Windows"
Bei Linux funktioniert der ACPI-Aus-Knopf zuverlässig. Windows blockt ihn
gern: Dort hält eine Anwendung den Vorgang auf, oder der Shutdown Event
Tracker fragt nach einem Grund und wartet auf eine Eingabe, die nie kommt.
Wer im Gast selbst herunterfährt, umgeht das. Deshalb steht dieser Weg an
erster Stelle.
!!! danger "Ein Neustart *im* Gast hilft nicht"
Dabei setzt sich nur die Maschine zurück — der QEMU-Prozess läuft weiter,
mit der alten Grafikkarte. Es braucht wirklich den Umweg über „aus und
wieder an".
### Der geladene Arbeitsspeicher ist danach weg
!!! danger "Endgültig"
Proxmox gibt den kopierten Arbeitsspeicher schon beim **ersten** Start
wieder frei. Ein zweiter Start bootet also kalt — offene Programme und
ungespeicherte Daten sind dann verloren.
pvesnap warnt vorher ausdrücklich, wenn eine Maschine mit geladenem
Arbeitsspeicher neu starten soll:
![Warnung vor dem Neustart](../bilder/recovery-neustart.svg)
Wer den warmen Zustand braucht, erledigt seine Arbeit also **vor** dem ersten
Neustart — oder verzichtet gleich mit `--no-resume` darauf und bootet kalt.
---
## Starten und Stoppen von außen
```bash
pvesnap-recovery start 9101
pvesnap-recovery stop 9101
pvesnap-recovery list
pvesnap-recovery console 9101
```
`list` zeigt dieselbe Tabelle wie die Übersicht:
```
Maschine Zustand Node Herkunft Modus
VM 9101 running pve1 VM 101 @ auto-stuendlich-20260809-… live
VM 9102 stopped pve2 VM 105 @ auto-taeglich-20260809-02… recover
```
Steht in der Spalte **Zustand** ein `weg`, wurde die Maschine außerhalb von
pvesnap entfernt — dann hilft [`cleanup`](verwerfen.md#cleanup).
+176
View File
@@ -0,0 +1,176 @@
# Dongle und USB
Eine abgeschottete Maschine hat kein Netzwerk. Ein Software-Schutzmodul hängt
sonst an einem USB-Server im Netz — der ist damit unerreichbar. Und ohne
Lizenzprüfung startet die Warenwirtschaft nicht, aus der man gerade Daten holen
wollte.
**SPICE löst das.** Die USB-Weiterleitung kommt vom **Rechner des Bedieners**,
nicht über das Gastnetz. Die Maschine bleibt also vollständig abgeschottet und
hat trotzdem ihren Dongle.
```
Dein Arbeitsplatz Proxmox-Host Gast
Dongle am USB ──SPICE──► QEMU ──virtuell──► "USB-Gerät"
remote-viewer (kein Netz nötig)
```
---
## Einrichten
Beim Anlegen in der [Optionsmaske](einrichten.md#die-optionsmaske) die beiden
Felder setzen — oder direkt:
```bash
pvesnap-recovery live 105 --usb 2 --no-resume
```
`--usb` schaltet `--spice` automatisch mit ein.
Die Zusammenfassung sagt dann, was zu tun ist:
![Vorhaben mit SPICE und USB](../bilder/dongle-vorhaben.svg)
---
## Verbinden
```bash
pvesnap-recovery spice 9104 -o vm.vv # Verbindungsdatei erzeugen
scp root@10.20.0.12:vm.vv . # auf den Arbeitsplatz holen
remote-viewer vm.vv # öffnen
```
Ohne `-o` wird die Datei auf die Standardausgabe geschrieben — praktisch für
eine Pipeline:
```bash
ssh root@pve2 pvesnap-recovery spice 9104 > vm.vv && remote-viewer vm.vv
```
Im **remote-viewer** dann unter **Datei → USB-Geräteauswahl** das Gerät
anhaken. Es taucht sofort im Gast auf.
!!! success "Guest-Tools braucht es dafür nicht"
Der Dongle erscheint im Gast als gewöhnliches USB-Gerät; QEMU und der
SPICE-Client machen die Arbeit.
Die Guest-Tools (`tools/get-guest-tools.sh` holt virtio-win auf einen
ISO-Storage) sind nur für Zwischenablage, automatische Auflösung und
Mauszeiger nützlich. Für den Dongle sind sie überflüssig.
Die Verbindungsdatei ist **kurzlebig**: Das darin enthaltene Kennwort gilt nur
für wenige Sekunden. Sie muss also zeitnah nach dem Erzeugen benutzt werden —
nicht am Vortag vorbereiten.
---
## Zwei Dinge, die man vorher wissen sollte
### USB braucht SPICE
Ohne `vga: qxl` legt QEMU die Weiterleitungen zwar an, aber es gibt keinen
Kanal, der sie transportiert — die Maschine startet dann mit `no spice port`
gar nicht erst.
Deshalb schaltet `--usb` die Anzeige automatisch mit ein.
Umgekehrt gilt das nicht: Wer SPICE **ausschaltet**, verliert die USB-Anschlüsse
mit — ohne Kanal wären sie ohnehin wirkungslos. pvesnap sagt das dazu, statt sie
stillschweigend stehen zu lassen.
### SPICE und geladener Arbeitsspeicher schließen sich aus
Das ist der unangenehme Teil, und er hängt davon ab, was im **Original** steht:
| Original | warmer RAM-Zustand | SPICE + Dongle |
|---|---|---|
| `vga: std` (Vorgabe) | ✅ | nur mit `--no-resume` |
| `vga: qxl` | ✅ | ✅ **gleichzeitig** |
Der Grund: `qxl` bringt 64 MB Grafikspeicher mit, die Vorgabe 16. Der
gespeicherte Zustand passt dann nicht mehr, und das Laden bricht ab:
```
Size mismatch: vga.vram
```
Die Maschine bleibt danach angehalten stehen, während Proxmox `TASK OK` meldet.
`pvesnap-recovery` lehnt die Kombination deshalb von vornherein ab und nennt die
Auswege.
!!! tip "Der Griff, der sich lohnt — für Produktivmaschinen"
Setz auf Maschinen, die ein Schutzmodul brauchen, **einmal** `vga: qxl`:
```bash
qm set 105 --vga qxl
```
Dann tragen alle künftigen Snapshots es mit, und im Ernstfall gibt es
warmen RAM-Zustand **und** Dongle gleichzeitig.
**Nachträglich lässt sich das bei einem vorhandenen Snapshot nicht mehr
reparieren** — der gespeicherte Zustand enthält die alte Grafikkarte. Es ist
also eine Entscheidung, die man vorher trifft oder gar nicht.
Auf der Produktivmaschine kostet `qxl` nichts außer ein paar Megabyte
Grafikspeicher.
---
## Nachträglich umstellen
Taste ++p++ in der [Detailansicht](arbeiten.md) ändert SPICE und USB auch an
einer bestehenden Maschine:
![SPICE ein- oder ausschalten](../bilder/recovery-spice.svg)
Dann die Zahl der Anschlüsse:
![Wie viele USB-Anschlüsse?](../bilder/recovery-usb.svg)
| Änderung | |
|---|---|
| **SPICE ein oder aus** | **Neustart nötig** — die Grafikkarte lässt sich im Betrieb nicht wechseln |
| **USB dazu oder weg** | sofort, solange der SPICE-Kanal schon steht |
Ist ein Neustart nötig, warnt pvesnap vorher deutlich:
![Warnung vor dem nötigen Neustart](../bilder/recovery-neustart.svg)
… und lässt danach die Wahl, **wie** neu gestartet wird — siehe
[Damit arbeiten](arbeiten.md#neu-starten).
!!! danger "Bei geladenem Arbeitsspeicher"
Der ist nach dem Neustart **endgültig weg**. Wer SPICE nachrüstet, um an
den Dongle zu kommen, verliert also genau den warmen Zustand, wegen dem er
vielleicht überhaupt mit `--resume` gestartet ist.
Deshalb: die Entscheidung möglichst **vor** dem ersten Start treffen.
Höchstens **14 Anschlüsse** sind möglich — mehr sieht QEMU nicht vor. Für einen
Dongle reicht einer; zwei sind bequem, wenn man zusätzlich einen USB-Stick
durchreichen will.
---
## Wenn der Dongle nicht auftaucht
| Symptom | woran es liegt |
|---|---|
| Kein Eintrag unter „USB-Geräteauswahl" | Der `remote-viewer` läuft auf dem falschen Rechner — er muss dort laufen, wo der Dongle steckt. |
| Maschine startet nicht, `no spice port` | `vga: qxl` fehlt. In der Detailansicht ++p++ → SPICE ein. |
| Gerät angehakt, im Gast nichts | Manche Dongles brauchen im Gast einen Treiber. Der gehört auf ein [Austauschlaufwerk](transfer.md). |
| `.vv`-Datei wird abgewiesen | Sie ist zu alt — neu erzeugen, das Kennwort gilt nur kurz. |
| Unter Linux fehlt `remote-viewer` | Paket `virt-viewer` installieren. |
!!! note "Der Dongle bleibt am Arbeitsplatz"
Er wird nicht in den Host gesteckt und nicht ins Netz gehängt. Genau das ist
der Punkt: Der Weg führt über die SPICE-Verbindung, die ohnehin schon
zwischen Arbeitsplatz und Maschine besteht — und nicht über ein Netz, das
die abgeschottete Maschine gar nicht hat.
@@ -0,0 +1,188 @@
# Eine Maschine einrichten
```bash
pvesnap-recovery
```
Dann ++n++ in der Übersicht. Der Assistent fragt nacheinander nach Gast und
Snapshot — dieselben Auswahlfenster wie im
[Explorer](../holen/explorer.md#1-welcher-gast) — und landet dann in der
Optionsmaske.
---
## Die Optionsmaske
![Die Optionsmaske](../bilder/recovery-optionen.svg)
Pfeiltasten bewegen sich, ++enter++ oder ++space++ ändert ein Feld. Unten steht
zu jedem Feld eine Erklärung — man muss also nichts auswendig wissen.
| Taste | |
|---|---|
| ++enter++ ++space++ | Feld ändern |
| ++v++ | [Austauschlaufwerke verwalten](transfer.md) — ohne die Maske zu verlassen |
| ++f10++ | anlegen |
| ++q++ | abbrechen |
### Betriebsart
![Die Wahl der Betriebsart](../bilder/recovery-betriebsart.svg)
| | |
|---|---|
| **live** | abgeschottet, ohne Netzwerk. Zum Hineinschauen. |
| **recovery** | mit Netzwerk und gleicher Identität. Siehe [Mit Netzwerk](netzwerk.md). |
### Die übrigen Felder
| Feld | Vorgabe | |
|---|---|---|
| **Netzwerk** | automatisch (nach Modus) | `keine Karte` / `Karte ohne Leitung` / `am Netz` — überschreibt die Vorgabe der Betriebsart |
| **Arbeitsspeicher** | wenn im Snapshot vorhanden | mit `nein` bootet die Maschine kalt |
| **Transfer-Laufwerke** | — | [Austauschlaufwerke](transfer.md), mehrere möglich |
| **SPICE-Anzeige** | nein | zusätzlich zu noVNC, nötig für [USB](dongle.md) |
| **USB-Weiterleitung** | keine | Anzahl der Anschlüsse; schaltet SPICE mit ein |
| **Neue VMID** | nächste freie | |
| **Node** | der des Originals | bei lokalem Storage nicht änderbar |
| **Danach starten** | ja | mit `nein` nur einrichten |
---
## Austauschlaufwerke auswählen
Auf dem Feld **Transfer-Laufwerke** öffnet ++enter++ die Mehrfachauswahl:
![Austauschlaufwerke auswählen](../bilder/transfer-auswahl.svg)
++space++ wählt aus, ++enter++ übernimmt. Belegte Laufwerke lassen sich nicht
anhängen — sie sind entweder am Host eingehängt oder stecken schon in einer
anderen Maschine. Warum das so streng ist, steht unter
[Austauschlaufwerke](transfer.md#host-oder-gast-nie-beides).
Fehlt noch eins, führt ++v++ direkt in die Verwaltung und wieder zurück.
---
## Die Zusammenfassung
++f10++ zeigt, was passieren wird — **bevor** etwas passiert:
![Das Vorhaben prüfen](../bilder/recovery-vorhaben.svg)
Hier lohnt sich das Lesen. Vier Arten von Zeilen stehen darin:
| | |
|---|---|
| **Klone** | welche Datenträger geklont werden, mit Größe |
| **Hinweise** (`-`) | was pvesnap von sich aus angepasst hat und warum |
| **ACHTUNG** | Dinge, die man wissen sollte, die aber in Ordnung sind |
| **GEFAHR** | Dinge, die so nicht gehen — siehe [Mit Netzwerk](netzwerk.md) |
Im Bild sind es drei Hinweise, die je einen Sachverhalt erklären:
> *Netzwerkkarte bleibt vorhanden, aber abgeklemmt (link_down): mit geladenem
> Arbeitsspeicher darf sich die Geräteausstattung nicht ändern.*
Die Karte zu entfernen würde den gespeicherten RAM-Zustand unbrauchbar machen —
also bleibt sie drin, und stattdessen wird die Leitung gekappt.
> *Proxmox gibt den kopierten Arbeitsspeicher nach dem ersten Start wieder frei
> — ein späterer Neustart bootet dann kalt.*
Der warme Zustand ist **einmalig**. Wer ihn braucht, sollte gleich beim ersten
Start damit arbeiten.
Mit ++j++ geht es los, mit ++n++ zurück.
---
## Auf der Kommandozeile
```bash
pvesnap-recovery live 101 # neuester Snapshot
pvesnap-recovery live 101 auto-stuendlich-20260809-100000 # ein bestimmter
pvesnap-recovery live 101 --newid 9103 --transfer werkzeuge -y
```
Ohne Snapshot-Namen wird der neueste brauchbare genommen.
| Parameter | Bedeutung |
|---|---|
| `--newid <ID>` | VMID der neuen Maschine (Vorgabe: nächste freie) |
| `--node <name>` | auf welchem Node sie laufen soll |
| `--net none\|down\|on` | keine Karte / Karte ohne Leitung / voll am Netz |
| `--resume` / `--no-resume` | Arbeitsspeicher laden bzw. bewusst kalt starten |
| `--transfer <name>` | Austauschlaufwerk anhängen (mehrfach möglich) |
| `--spice` | SPICE-Anzeige (`vga: qxl`), zusätzlich zu noVNC |
| `--usb <n>` | so viele USB-Weiterleitungen über SPICE (schaltet `--spice` mit ein) |
| `--iso <volid>` | Abbild als CD einlegen, z. B. `local:iso/virtio-win.iso` |
| `--memory <MB>`, `--cores <n>` | abweichende Ausstattung (nicht mit `--resume`) |
| `--name <name>` | Name der neuen Maschine |
| `--keep-binds` | durchgereichte Host-Verzeichnisse des Originals übernehmen |
| `--no-start` | nur einrichten, nicht starten |
| `-y`, `--yes` | Routinefragen überspringen |
| `--force` | auch anlegen, wenn das Original noch läuft |
Ohne `-y` wird die Zusammenfassung angezeigt und nachgefragt. Ein Probelauf ist
das aber nicht — wer nur sehen will, was geschähe, bricht an der Rückfrage ab.
!!! danger "`-y` deckt `--force` nicht ab"
`-y` überspringt **Routinefragen**. Es winkt nicht durch, dass eine
Wiederherstellung mit Netzwerk startet, während das Original läuft — das
verlangt ausdrücklich `--force`. Siehe [Mit Netzwerk](netzwerk.md).
---
## Was dabei angelegt wird
1. **Klone** aller Datenträger des Snapshots (`PVE::Storage::vdisk_clone`)
2. eine **Konfigurationsdatei** unter `/etc/pve/nodes/<node>/qemu-server/<id>.conf`
3. der **Tag** `pvesnap-recovery` an der neuen Maschine
4. ein Eintrag in der Merkliste `/var/lib/pvesnap/recovery.json`
Geht dabei etwas schief, wird **alles wieder abgeräumt** — es bleiben keine
halben Klone liegen.
!!! info "Auf Ceph wird der Quell-Snapshot geschützt"
RBD verlangt für einen Klon, dass der Quell-Snapshot geschützt ist
(`rbd snap protect`); Proxmox setzt das beim Klonen selbst.
Solange die Wiederherstellung existiert, ist ihr Quell-Snapshot damit
**unlöschbar** — die [Vorhaltezeit](../sichern/vorhaltezeit.md) kommt an ihm
nicht vorbei. Beim [Verwerfen](verwerfen.md) wird der Schutz wieder
aufgehoben.
Das ist der Grund, eine Wiederherstellung nicht länger stehen zu lassen als
nötig — oder sie mit [`flatten`](loesen.md) zu lösen.
---
## Was übernommen wird und was nicht
**Übernommen:** SMBIOS-UUID, `vmgenid`, MAC-Adressen, CPU- und
Speicherausstattung, Bootreihenfolge, alle Datenträger als Klone, bei Containern
der Hostname.
**Weggelassen:**
| | warum |
|---|---|
| `onboot`, `startup` | eine Wiederherstellung soll nicht beim nächsten Hostneustart von selbst hochkommen |
| `protection` | sie soll sich verwerfen lassen |
| `replicate` | sonst würde der Klon auf andere Nodes repliziert |
| `hookscript` | fremde Skripte laufen nicht ungefragt in der Kopie |
| `description`, `tags` | wird durch die eigenen ersetzt |
| durchgereichte Host-Verzeichnisse | nur mit `--keep-binds` |
| `parent`, `snaptime`, `snapstate` | gehören zum Snapshot, nicht zur Maschine |
!!! warning "`--keep-binds` mit Bedacht"
Reicht das Original ein Host-Verzeichnis in den Container durch, sieht die
Wiederherstellung mit `--keep-binds` **dieselben, echten Daten** — nicht
deren Stand vom Snapshot. Und sie kann hineinschreiben.
Ohne die Option bleibt das Verzeichnis weg; in der Zusammenfassung steht
dann, was weggelassen wurde.
+179
View File
@@ -0,0 +1,179 @@
# Wiederherstellen
Manchmal reicht es nicht, einzelne Dateien herauszukopieren.
Eine Datenbank liegt im Snapshot als Haufen halbfertiger Dateien — brauchbar
wird sie erst, wenn der Datenbankserver läuft und selbst einen Dump schreibt.
Eine Warenwirtschaft prüft beim Start ihre Lizenz. Ein Verzeichnisdienst ist
ohne laufende Maschine überhaupt nicht zu befragen.
`pvesnap-recovery` startet den Snapshot deshalb als **eigenständige Maschine**
ohne das Original anzufassen.
![Übersicht der Wiederherstellungen](../bilder/recovery-uebersicht.svg)
---
## Zwei Betriebsarten
| | **live** | **recovery** |
|---|---|---|
| Netzwerk | keins | volles Netz |
| Identität | bleibt (UUID, MACs, Hostname) | bleibt (UUID, MACs, Hostname) |
| Wofür | hineinschauen, Dump ziehen, Daten holen | die Maschine wirklich wieder in Betrieb nehmen |
| Risiko | keins — sie kann nichts erreichen | hoch, solange das Original läuft |
**Der Unterschied ist ausschließlich das Netzwerk** — und die Zahl der
Sicherheitsabfragen. An den Datenträgern ändert der Modus nichts; beide klonen
gleich.
`live` ist der Normalfall. Die Maschine ist vollständig abgeschottet: Sie kann
nichts erreichen und niemanden stören, auch wenn sie dieselbe IP-Adresse
konfiguriert hat wie das noch laufende Original. Daten kommen über ein
[Austauschlaufwerk](transfer.md) heraus.
`recovery` ist der Ernstfall — und gefährlich, solange das Original läuft.
Details unter [Mit Netzwerk](netzwerk.md).
---
## Warum das schnell geht
Die Datenträger werden nicht kopiert, sondern **geklont**. Auf Ceph/RBD, ZFS und
LVM-thin ist das ein Copy-on-Write-Klon: fertig in Sekunden, egal wie groß die
Platte ist, und anfangs ohne zusätzlichen Platzbedarf.
```
Klon ~1 s egal ob 32 GB oder 2 TB
Start Sekunden mit RAM-Zustand sofort im laufenden Zustand
Arbeiten ab jetzt voll beschreibbar
```
Eine 32-GB-Platte war im Test nach **0,8 Sekunden** geklont.
Dafür wird `PVE::Storage::vdisk_clone` benutzt — dieselbe Funktion, die auch
Proxmox selbst für Klone verwendet, samt Cluster-Sperre auf dem Storage. Es
wird nichts nachgebaut, was Proxmox schon kann.
!!! info "Kein „Live-Restore"— besser"
Beim Live-Restore eines Backup-Servers liegt die Sicherung auf einem anderen
Medium: Die VM startet zwar sofort, holt die Blöcke aber im Hintergrund
übers Netz nach und läuft bis dahin gebremst.
Hier wird **gar nichts nachgeladen**. Der Klon liegt im selben Pool und ist
aus Sicht des Gastes von Sekunde eins an vollständig — bei voller
Geschwindigkeit. Bei einer 2-TB-VM wird also nicht stundenlang kopiert und
dann gestartet; gearbeitet wird ab der ersten Sekunde.
!!! warning "Nicht auf jedem Storage"
Auf Datei-Storages (`dir`, `nfs`, `cifs`) geht das nicht — aus einem
qcow2-internen Snapshot lässt sich kein Klon ziehen. Für diese Storages
bleibt der Weg über den [Explorer](../holen/explorer.md).
Was welches Storage kann, steht unter
[Speicherarten](../nachschlagen/speicher.md).
---
## Der Arbeitsspeicher kommt mit
Enthält der Snapshot den Arbeitsspeicher (`vmstate = yes` in der Gruppe), wird
er mitgenommen. Die Maschine **bootet dann nicht**, sondern läuft genau dort
weiter, wo sie beim Snapshot stand:
* kein Crash-Recovery, kein `fsck`, kein Journal-Rollback
* Datenbanken sind bereits offen und konsistent
* die Uhr im Gast steht auf dem Snapshot-Zeitpunkt
Im Test war das gut zu sehen: Die Konsole zeigte 10:38 — die Uhrzeit des
Snapshots — während auf dem Host längst 11:24 war.
Damit das klappt, muss die Geräteausstattung **exakt** zum gespeicherten Zustand
passen. `pvesnap-recovery` sorgt selbst dafür:
| | |
|---|---|
| `vmgenid`, `smbios1` | bleiben erhalten — fehlen sie, bricht das Laden mit `Unknown savevm section or instance 'vmgenid'` ab |
| `runningmachine`, `runningcpu` | werden aus dem Snapshot übernommen |
| Netzwerkkarte | bleibt **vorhanden**, wird aber abgeklemmt (`link_down=1`) statt entfernt |
| Austauschlaufwerke | werden erst **nach** dem Fortsetzen angesteckt (Hotplug) — der Gast wacht in einem Zustand auf, in dem es die Platte noch nicht gab |
!!! note "Ein Detail, das Proxmox nicht selbst meldet"
Schlägt das Laden des Arbeitsspeichers fehl, quittiert Proxmox den Start
trotzdem mit `TASK OK` und lässt die Maschine angehalten stehen.
`pvesnap-recovery` liest das Task-Protokoll mit, setzt die Maschine fort und
sagt deutlich, wenn statt des RAM-Standes kalt gebootet wurde.
Mit `--no-resume` lässt sich der Arbeitsspeicher bewusst weglassen — etwa, wenn
[SPICE](dongle.md) gebraucht wird, das sich damit ausschließt.
---
## Der übliche Ablauf
<div class="ablauf" markdown>
**1.** [Austauschlaufwerk vorbereiten](transfer.md) — *vorher*, in Ruhe
**2.** [Maschine einrichten](einrichten.md) — Snapshot wählen, Optionen prüfen, anlegen
**3.** [Damit arbeiten](arbeiten.md) — noVNC-Konsole, Dump ziehen, Laufwerk auswerfen
**4.** [Verwerfen](verwerfen.md) — oder [dauerhaft übernehmen](loesen.md)
</div>
Bedient wird alles entweder über die ncurses-Oberfläche:
```bash
pvesnap-recovery
```
… oder direkt über Parameter:
```bash
pvesnap-recovery live 101 # neuester Snapshot, abgeschottet
pvesnap-recovery live 101 auto-stuendlich-20260809-100000
pvesnap-recovery recover 101 --newid 9101 # mit Netzwerk
pvesnap-recovery list
pvesnap-recovery destroy 9101
```
Vollständig unter [Alle Befehle](../nachschlagen/befehle.md#pvesnap-recovery).
---
## Was in der Übersicht steht
| Spalte | |
|---|---|
| **Maschine** | die neue VMID |
| **Zustand** | `running`, `stopped` — oder `weg`, wenn sie außerhalb von pvesnap entfernt wurde |
| **Node** | auf welchem Cluster-Knoten sie läuft |
| **Herkunft** | Original-VMID und Snapshot, aus dem sie stammt |
| **Modus** | `live` oder `recover` |
| Taste | |
|---|---|
| ++enter++ | [Detailansicht](arbeiten.md) |
| ++n++ | [neue Wiederherstellung](einrichten.md) |
| ++s++ / ++h++ | starten / herunterfahren |
| ++x++ | [verwerfen](verwerfen.md) |
| ++v++ | [Austauschlaufwerke](transfer.md) |
| ++r++ | Liste neu einlesen |
| ++q++ | Ende |
!!! tip "Wiederhergestellte Maschinen tragen einen Tag"
Jede von `pvesnap-recovery` angelegte Maschine bekommt in Proxmox den Tag
**`pvesnap-recovery`**. Daran erkennt man sie in der Weboberfläche — und
daran erkennt `destroy`, dass es sie anfassen darf. Eine von Hand angelegte
VM lässt sich damit nicht versehentlich löschen.
Der Tag ist auch der Grund, warum
`exclude_tags = nosnap, pvesnap-recovery` in jede `all`-Gruppe gehört, siehe
[Auswahl](../sichern/auswahl.md#ausschlusse).
+194
View File
@@ -0,0 +1,194 @@
# Vom Snapshot lösen
Ein Linked Clone hängt für immer am Quell-Snapshot. Fürs Hineinschauen ist das
ideal — es kostet nichts und geht in Sekunden. Für eine **dauerhaft übernommene**
Maschine ist es ein Problem:
* der Quell-Snapshot lässt sich nicht mehr löschen — die
[Vorhaltezeit](../sichern/vorhaltezeit.md) scheitert mit
`snapshot is protected`
* die Original-VM muss mitsamt ihrer Platte bestehen bleiben
* der Klon wächst ohnehin mit jedem Schreibvorgang
Die Detailansicht sagt, woran man ist:
![Linked Clone in der Detailansicht](../bilder/recovery-detail.svg)
```
Datentraeger: Linked Clone - haengt am Quell-Snapshot
Belegt nur, was seither geschrieben wurde. Der Quell-Snapshot ist dafuer unloeschbar.
Fuer den Dauerbetrieb mit f loesen (kopiert die Daten wirklich).
```
---
## Lösen
Taste ++f++ in der Detailansicht — oder:
```bash
pvesnap-recovery flatten 9101
```
Zuerst wird gemessen, wie viel kopiert werden muss:
![Der Lösen-Dialog mit der Größe](../bilder/recovery-loesen.svg)
Dahinter steckt `rbd flatten`: Die Daten aus dem Elternteil werden jetzt
wirklich in den Klon kopiert. Danach hängt die Maschine an nichts mehr, und der
Schutz der Quell-Snapshots wird **automatisch aufgehoben** — die Vorhaltezeit
kommt wieder durch.
Schnell ist es, solange Platz da ist: 13 GB waren im Test in rund
**70 Sekunden** kopiert.
---
## Die Maschine läuft dabei weiter
!!! success "`rbd flatten` ist eine Online-Operation"
Im Test blieb die VM über den gesamten Vorgang auf `qmpstatus: running`, der
QEMU-Monitor antwortete durchgehend, und die Uhr auf der Konsole lief weiter
— inklusive geladenem RAM-Zustand.
Auch Daten, die **während** des Abkoppelns geschrieben werden, überleben es.
Gemessen an einem Container, der durchgehend schrieb:
| | |
|---|---|
| vor dem Flatten geschrieben | 64 MB Zufallsdaten, SHA-256 danach **OK** |
| während des Flatten geschrieben | 192 MB Zufallsdaten, SHA-256 danach **OK** |
| Schreibvorgänge im Zeitfenster | 12, von 19:35:48 bis 19:36:10 (Flatten: 19:35:4819:36:11) |
| Zustand des Gastes | durchgehend `running` |
Der Grund: `rbd flatten` füllt nur die Blöcke auf, die der Klon noch **nicht**
selbst besitzt. Was der Gast bereits geschrieben hat, gehört ihm — und wird
nicht überschrieben.
`flatten` betrifft also ausschließlich die *Abhängigkeit* vom Quell-Snapshot,
nie die Verfügbarkeit.
---
## Der Platzbedarf — und warum `rbd du` täuscht
!!! warning "Nicht von der Snapshot-Zeile täuschen lassen"
Die Zeile eines Snapshots in `rbd du` zeigt nur dessen **Zuwachs**, nicht
seinen Inhalt.
Im Test stand `@handtest01` mit `USED 56 MiB` da — kopiert wurden beim
Flatten trotzdem über 8 GB. Denn sichtbar ist an dieser Stelle der gesamte
Inhalt der Kette (`<TOTAL> 14 GiB`).
Deshalb misst `pvesnap-recovery flatten` das vorher richtig und nennt die
Größe, bevor es losgeht.
### Es wird geprüft, ob der Platz reicht
!!! danger "Ein volllaufender Ceph-Pool blockiert alles"
Läuft der Pool während des Kopierens voll, blockiert er **jeden**
Schreibvorgang. Dann steht nicht nur das Flatten, sondern jede VM auf diesem
Storage — und selbst das Aufräumen wird schwierig, weil Löschen ebenfalls
ein Schreibvorgang ist.
`flatten` bricht deshalb ab, wenn weniger als das **1,15-fache** des Bedarfs
frei ist. `--force` setzt sich darüber hinweg.
Vorher selbst nachsehen:
```bash
ceph df # MAX AVAIL beachten, nicht % RAW USED
```
`% RAW USED` täuscht, weil es die Replikation nicht berücksichtigt: Bei
dreifacher Replikation kosten 13 GB Nutzdaten 39 GB im Pool.
---
## Nachsehen, was los ist
```bash
rbd -p <pool> ls -l | grep -E 'NAME|vm-9101' # Spalte PARENT
rbd -p <pool> du | grep -E 'NAME|vm-9101' # PROVISIONED gegen USED
```
Eine frisch geklonte 32-GB-Platte steht dort mit `PROVISIONED 32 GiB` und
`USED 80 MiB` — die 80 MiB sind alles, was der laufende Gast seither geschrieben
hat.
Nach dem Flatten ist die Spalte `PARENT` leer, und `USED` entspricht dem
tatsächlichen Inhalt.
---
## Auf anderen Speicherarten
Das ganze Thema ist eine **Ceph-Eigenheit**. Andere Storages verhalten sich
anders:
| Storage | Hängt am Quell-Snapshot? | `flatten` |
|---|---|---|
| `rbd` (Ceph) | **ja** — Snapshot wird geschützt und unlöschbar | nötig für den Dauerbetrieb |
| `lvmthin` | **nein** | nicht nötig |
| `zfspool` | ja | nicht möglich |
### LVM-thin ist hier 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. `flatten` meldet
solche Datenträger entsprechend als „nicht nötig".
Der Preis liegt woanders: `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.
!!! warning "Auch Thin-Pools laufen voll"
Und dann stehen alle Volumes darin. Überprovisionierung im Blick behalten:
```bash
lvs -o lv_name,data_percent,metadata_percent
```
### Bei ZFS geht es nicht
Der Klon hängt wie bei Ceph am Snapshot. `zfs promote` würde die Abhängigkeit
nur **umdrehen** statt auflösen — danach hinge das Original am Klon, was das
Problem nicht löst, sondern verschiebt.
Echte Unabhängigkeit ginge nur über eine Vollkopie per `zfs send | zfs recv`.
Das macht `flatten` nicht, und es sagt das auch so:
```
nicht moeglich - ein ZFS-Klon haengt am Snapshot. Loesen ginge nur
ueber 'zfs send | zfs recv' in einen neuen Datentraeger
```
---
## Wann lösen, wann nicht?
| Lage | |
|---|---|
| Kurz hineinschauen, Dump ziehen, verwerfen | **nicht lösen.** Kostet nur Platz und Zeit. |
| Die Maschine bleibt ein paar Tage stehen | **lösen**, sonst blockiert sie die Vorhaltezeit des Originals |
| Die Maschine übernimmt dauerhaft | **lösen** — und zwar vor dem Löschen des Originals |
| Auf LVM-thin | nichts zu tun |
+167
View File
@@ -0,0 +1,167 @@
# Mit Netzwerk
```bash
pvesnap-recovery recover 101 auto-stuendlich-20260809-100000 --newid 9101
```
Der Ernstfall: Die Maschine soll nicht nur untersucht, sondern **wieder in
Betrieb genommen** werden.
Dabei bleibt alles erhalten, was die Maschine ausmacht:
* SMBIOS-UUID und `vmgenid`
* MAC-Adressen aller Netzwerkkarten
* bei Containern der Hostname
Für alles im Netz ist sie damit **dieselbe Maschine** — inklusive
Lizenzbindungen, AD-Mitgliedschaft und DHCP-Reservierungen. Genau das will man
im Ernstfall.
---
## Genau deshalb ist sie gefährlich
!!! danger "Zwei Maschinen, eine Identität"
Dieselbe MAC und dieselbe IP zweimal im selben Netz geben Chaos: ARP-Tabellen
schlagen um, Verbindungen brechen sporadisch ab, ein Verzeichnisdienst
bekommt widersprüchliche Anmeldungen, und die Datenbank wird von zwei Seiten
beschrieben.
Und das Unangenehme daran: Es fällt nicht sofort auf. Es fällt eine halbe
Stunde später auf, wenn niemand mehr weiß, was zuletzt geändert wurde.
`pvesnap-recovery` prüft deshalb vorher, ob das Original noch läuft — und lässt
sich in dem Fall **nicht beiläufig durchwinken**:
| Situation | Verhalten |
|---|---|
| ncurses-Oberfläche | Zusammenfassung nur mit ++j++, danach eine **zweite** Rückfrage, die den Grund beim Namen nennt |
| `recover …` im Terminal | das Wort **`ja`** muss ausgeschrieben werden — ++j++ reicht nicht |
| `recover … -y` | **wird abgewiesen** (Exit-Code 2) |
| `recover … --force` | läuft durch — die bewusste Entscheidung |
!!! quote "Warum `-y` nicht reicht"
`-y` heißt „keine Routinefragen", nicht „frag mich auch dann nicht, wenn es
weh tut". Ein Skript, das mit `-y` läuft, soll nicht versehentlich eine
zweite Domänencontroller-Identität ins Netz stellen.
Wer es wirklich will, schreibt `--force` — und hat es dann bewusst getan.
---
## Der sichere Weg
<div class="ablauf" markdown>
**Erste Wahl:** Das Original **vorher stoppen**.
```bash
qm stop 101
pvesnap-recovery recover 101 --newid 9101
```
**Zweite Wahl:** Mit abgeklemmter Leitung starten und die Karte erst zuschalten,
wenn klar ist, dass die Luft rein ist.
```bash
pvesnap-recovery recover 101 --newid 9101 --net down
# ... in der Konsole prüfen, ob es die richtige Maschine ist ...
qm set 9101 --net0 virtio=BC:24:11:00:65:40,bridge=vmbr0
```
</div>
Der zweite Weg hat einen praktischen Vorteil: Man sieht der Maschine über die
noVNC-Konsole an, ob es wirklich der richtige Stand ist — **bevor** sie
irgendjemand im Netz erreicht.
---
## Die drei Netzwerkzustände
| `--net` | |
|---|---|
| `none` | **keine Netzwerkkarte.** Die Maschine hat gar kein Netz. Vorgabe bei `live`. |
| `down` | **Karte vorhanden, Leitung abgeklemmt** (`link_down=1`). Der Gast sieht seine Karte, aber nichts kommt durch. |
| `on` | **voll am Netz**, mit den MAC-Adressen des Originals. Vorgabe bei `recover`. |
Der Unterschied zwischen `none` und `down` ist wichtiger, als er aussieht:
!!! info "Mit geladenem Arbeitsspeicher wird aus `none` automatisch `down`"
Ein gespeicherter RAM-Zustand lässt sich nur in eine Maschine mit **exakt
derselben Geräteausstattung** laden. Eine entfernte Netzwerkkarte wäre eine
Änderung — das Laden würde scheitern.
Deshalb bleibt die Karte in diesem Fall vorhanden und wird stattdessen
abgeklemmt. In der Zusammenfassung steht dazu ein Hinweis; man muss also
nicht selbst daran denken.
---
## Was danach zu tun ist
Eine Wiederherstellung, die dauerhaft bleiben soll, ist noch nicht fertig:
<div class="ablauf" markdown>
**1.** [Vom Quell-Snapshot lösen](loesen.md) — sonst hängt sie für immer daran,
und die Vorhaltezeit kommt an den Snapshots des Originals nicht mehr vorbei
**2.** Den Tag `pvesnap-recovery` entfernen, wenn sie zur regulären Maschine
werden soll:
```bash
qm set 9101 --tags produktion,datenbank
```
**3.** Aus der Merkliste nehmen — nach dem Entfernen des Tags taucht sie in
`pvesnap-recovery list` ohnehin nicht mehr als lebend auf
**4.** `onboot` wieder setzen, falls sie beim Hostneustart hochkommen soll:
```bash
qm set 9101 --onboot 1
```
</div>
!!! warning "Schritt 1 ist keine Kür"
Solange die Maschine ein Linked Clone ist, kann der Quell-Snapshot nicht
gelöscht werden — und das Original muss mitsamt seiner Platte bestehen
bleiben. Im Protokoll des Dienstes stünde dauerhaft
`snapshot is protected`.
Siehe [Vom Snapshot lösen](loesen.md).
!!! note "`onboot` und `startup` werden bewusst weggelassen"
Eine frisch angelegte Wiederherstellung soll nicht beim nächsten
Hostneustart von selbst hochkommen — schon gar nicht eine mit den
MAC-Adressen einer noch laufenden Maschine. Wer sie behält, setzt das selbst.
---
## Und das Original?
Wenn die Wiederherstellung übernimmt, will das Original meistens weg. Aber
**nicht sofort**:
!!! danger "Erst lösen, dann löschen"
Solange die Wiederherstellung ein Linked Clone ist, hängt sie an den
Datenträgern des Originals. Das Original zu löschen, würde die
Wiederherstellung mitnehmen.
Reihenfolge:
1. `pvesnap-recovery flatten 9101` — abwarten, bis es durch ist
2. prüfen: Die Detailansicht muss *„eigenständig — hängt an keinem Snapshot"*
zeigen
3. erst dann das Original entfernen
Auf LVM-thin entfällt das — dort sind die Klone von sich aus unabhängig. Siehe
[Speicherarten](../nachschlagen/speicher.md).
+228
View File
@@ -0,0 +1,228 @@
# Austauschlaufwerke
Eine abgeschottete Wiederherstellung hat **kein Netzwerk**. Kein SCP, kein
Netzlaufwerk, kein Cloud-Speicher. Wie kommt der Datenbank-Dump dann heraus?
Über ein **Austauschlaufwerk**: eine benannte Abbilddatei unter
`/var/lib/pvesnap/transfer/`, die sich am Host ganz normal einhängen lässt und
einem Gast als zusätzliche Platte angehängt wird.
```
/var/lib/pvesnap/transfer/dumps.img 20G "Datenbank-Dumps und Exporte"
/var/lib/pvesnap/transfer/werkzeuge.img 4G "Skripte, Treiber, Installer"
```
!!! success "Der eigentliche Sinn: vorher, nicht mittendrin"
Ein Austauschlaufwerk gehört zu keiner Maschine und überlebt jede
Wiederherstellung. Man legt es **einmal in Ruhe an**, packt hinein, was man
im Notfall braucht — und wählt es im Ernstfall nur noch aus einer Liste aus.
Im Notfall ist keine Zeit, sich Werkzeuge zusammenzusuchen. Und die
Maschine, in der man sie bräuchte, kommt nicht ins Netz.
---
## Die Übersicht
Taste ++v++ — erreichbar aus der Übersicht, aus der
[Optionsmaske](einrichten.md#die-optionsmaske) und aus der
[Detailansicht](arbeiten.md):
![Übersicht der Austauschlaufwerke](../bilder/transfer-uebersicht.svg)
| Taste | |
|---|---|
| ++enter++ | am Host einhängen und im Commander öffnen |
| ++n++ | neues Laufwerk anlegen |
| ++u++ | am Host aushängen |
| ++l++ | löschen |
| ++r++ | neu einlesen |
| ++q++ | zurück |
Die Spalte **Zustand** ist die wichtigste. Sie kennt drei Werte:
| Zustand | Farbe | bedeutet |
|---|---|---|
| `frei` | — | gehört niemandem, kann angehängt oder eingehängt werden |
| `am Host eingehängt` | grün | liegt gerade unter `/run/pvesnap/transfer/<name>` |
| `in Benutzung von 9101` | gelb | steckt in dieser Maschine |
---
## Ein Laufwerk anlegen
Taste ++n++, dann drei Fragen:
![Ein neues Laufwerk anlegen](../bilder/transfer-neu.svg)
| Frage | |
|---|---|
| **Name** | kurz, klein geschrieben, z. B. `dumps`. Wird zur Bezeichnung im Gast (`DUMPS`). |
| **Größe** | `20G`, `500M`, `2T` |
| **Wofür** | freier Text, taucht in der Übersicht auf |
Dabei passiert:
1. Abbilddatei anlegen (dünn besetzt — sie belegt anfangs nichts)
2. GPT-Partitionstabelle mit einer Partition, Typ `msftdata`
3. exFAT-Dateisystem, Bezeichnung = Name in Großbuchstaben
4. Rechte `0777` und eine `LIESMICH.txt` hineinlegen
!!! note "Die Größe ist eine Obergrenze, keine Reservierung"
Die Datei ist dünn besetzt: Ein 20-GB-Laufwerk mit 3 GB Inhalt belegt 3 GB
auf dem Host. In der Übersicht stehen beide Zahlen nebeneinander —
**Größe** und **belegt**.
---
## Befüllen und auslesen
++enter++ hängt das Laufwerk am Host ein und öffnet den
[Commander](../holen/explorer.md) darauf:
![Der Commander auf einem Austauschlaufwerk](../bilder/transfer-commander.svg)
Links das Laufwerk, rechts der Host. **Beide Seiten sind beschreibbar** — anders
als beim Snapshot-Explorer, wo links Schreibschutz herrscht. Kopiert wird mit
++f5++ in beide Richtungen.
Beim Verlassen mit ++q++ wird das Laufwerk automatisch wieder ausgehängt.
!!! tip "Was hineingehört"
Alles, was man im Gast bräuchte und dort nicht herunterladen kann:
* ein passendes `pg_dump` / `mysqldump` in der richtigen Version
* ein Packprogramm (`7z.exe` für Windows-Gäste)
* eigene Auswerteskripte
* bei Windows die virtio-Treiber, falls die Maschine sie braucht
* ein leeres Verzeichnis `ausgang/` für das, was herauskommen soll
---
## Host oder Gast, nie beides
Zwei unabhängige Einhängungen desselben Blockgeräts zerlegen das Dateisystem.
Nicht „vielleicht" — zuverlässig, weil beide Seiten Puffer halten, von denen
die andere nichts weiß.
Die Verwaltung lässt das deshalb nicht zu:
| Versuch | |
|---|---|
| Anhängen, während der Host es hält | abgelehnt |
| Aushängen, während ein Gast darauf arbeitet | abgelehnt |
| Löschen, solange es überhaupt in Benutzung ist | abgelehnt |
Woher sie das weiß: Sie liest die **Gast-Konfigurationen** unter
`/etc/pve/nodes/*/`. Dort steht der Eintrag auch dann, wenn die Maschine gerade
nicht läuft — was die Wahrheit besser trifft als jede Merkliste.
!!! info "Bei Containern ist es anders"
Dort ist es kein Blockgerät, sondern ein durchgereichtes Verzeichnis
(Bind-Mount). Host und Container sehen dieselben Dateien **gleichzeitig**
es ist dasselbe Dateisystem, nicht zweimal eingehängt.
Dort gibt es also nichts auszuwerfen und nichts abzuwarten.
---
## Im laufenden Betrieb wechseln
Taste ++e++ in der [Detailansicht](arbeiten.md) zieht das Laufwerk bei laufender
Maschine ab und gibt es wieder hinein — beliebig oft:
<div class="ablauf" markdown>
**auswerfen** → gehört wieder dem Host: einhängen, auslesen, neu befüllen
**einklinken** → zurück in denselben Steckplatz, die VM läuft durchgehend
</div>
Das ist der Weg, ohne die Maschine anzufassen an Zwischenergebnisse zu kommen —
Dump ziehen, abholen, weitermachen.
!!! warning "Im Gast vorher aushängen"
Unter Linux `umount`, unter Windows „Auswerfen" im Explorer.
Proxmox meldet das Gerät zwar ordentlich ab, aber ein Dateisystem, auf das
gerade geschrieben wird, nimmt das übel. Hält der Gast es fest, schlägt das
Auswerfen mit einer entsprechenden Meldung fehl — dann erst im Gast
aushängen und noch einmal.
Die Detailansicht zeigt jederzeit, was steckt:
```
Transfer-Laufwerke:
dumps eingesteckt als scsi1
e = auswerfen / einklinken, im laufenden Betrieb
```
---
## Warum exFAT
| | |
|---|---|
| **Windows und Linux** lesen und schreiben es von Haus aus | kein Treiber, keine Nachinstallation im Gast |
| **Keine 4-GB-Grenze** je Datei | ein 30-GB-Dump passt |
| **Keine Besitzrechte auf der Platte** | keine Rechteprobleme zwischen Host und Gast, keine `chown`-Runden |
Die Bezeichnung darf höchstens **11 Zeichen** haben — daher der Rat, den Namen
kurz zu halten. Eingehängt wird mit `umask=0000`, damit auch ein
unprivilegierter Container hineinschreiben kann (dessen `root` ist auf dem Host
die UID 100000).
### Warum eine Datei und kein Proxmox-Volume?
!!! danger "Ein Volume würde mitgelöscht"
Beim Entfernen der Maschine — auch aus der Proxmox-Weboberfläche heraus.
Die mühsam vorbereitete Werkzeugsammlung wäre weg, und zwar genau in dem
Moment, in dem jemand aufräumt.
Pfade überspringt PVE beim Zerstören ausdrücklich:
`return if $volid =~ m|^/|`. Deshalb eine Datei.
Angehängt wird über ein **Loop-Gerät**, weil Proxmox als Pfad nur `/dev/…`
akzeptiert — eine Abbilddatei direkt einzutragen lehnt es ab
(`unable to associate path to any storage`).
Die GPT-Partition trägt bewusst den Typ `msftdata`. Ohne diese Angabe vergibt
`parted` den Typ „Linux filesystem", und Windows gibt dem Laufwerk dann keinen
Buchstaben.
---
## Auf der Kommandozeile
```bash
pvesnap-recovery live 101 --transfer werkzeuge --transfer dumps
```
Mehrfach möglich. Die Laufwerke müssen frei sein, sonst bricht das Anlegen mit
einer Meldung ab.
Angelegt und verwaltet werden sie nur über die Oberfläche — dafür gibt es
bewusst keine Parameter. Das ist eine Vorbereitungsaufgabe, keine, die man
unter Zeitdruck tippt.
---
## Wo sie liegen
```
/var/lib/pvesnap/transfer/index.json Verzeichnis der Laufwerke
/var/lib/pvesnap/transfer/dumps.img die Abbilddatei
/run/pvesnap/transfer/dumps Einhängepunkt am Host
```
!!! warning "`uninstall.sh --purge` nimmt sie mit"
Ohne `--purge` bleibt alles liegen. Mit `--purge` ist auch der Inhalt der
Austauschlaufwerke weg. Siehe [Installation](../installation.md#deinstallation).
+146
View File
@@ -0,0 +1,146 @@
# Verwerfen und aufräumen
## Verwerfen
Taste ++x++ in der Übersicht oder in der Detailansicht:
![Sicherheitsabfrage vor dem Verwerfen](../bilder/recovery-verwerfen.svg)
Oder auf der Kommandozeile:
```bash
pvesnap-recovery destroy 9101
pvesnap-recovery destroy 9101 -y # ohne Rückfrage
```
`destroy` räumt vollständig auf:
<div class="ablauf" markdown>
**1.** Maschine stoppen
**2.** Gast samt aller Klone entfernen
**3.** Austauschlaufwerke freigeben (der **Inhalt bleibt** — sie gehören zu
keiner Maschine)
**4.** Den **Schutz der Quell-Snapshots wieder aufheben**
**5.** Eintrag aus der Merkliste nehmen
</div>
---
### Schritt 4 ist der wichtige
!!! info "Warum der Snapshot-Schutz wieder weg muss"
Ceph verlangt für einen Klon, dass der Quell-Snapshot geschützt ist
(`rbd snap protect`), und Proxmox setzt das beim Klonen selbst.
Bliebe der Schutz stehen, könnte die [Vorhaltezeit](../sichern/vorhaltezeit.md)
diesen Snapshot später nicht mehr löschen — im Protokoll stünde dann immer
wieder:
```
snapshot is protected
```
Solange eine Wiederherstellung existiert, ist ihr Quell-Snapshot also
**bewusst** unlöschbar. Danach nicht mehr.
---
### Was `destroy` nicht anfasst
!!! success "Nur Maschinen mit dem Tag `pvesnap-recovery`"
Eine von Hand angelegte VM lässt sich damit nicht versehentlich löschen —
auch dann nicht, wenn sie zufällig eine VMID hat, die einmal einer
Wiederherstellung gehörte.
**Austauschlaufwerke bleiben.** Sie gehören zu keiner Maschine; ihr Inhalt
überlebt jede Wiederherstellung. Sie werden nur wieder als `frei` markiert.
**Das Original bleibt.** Selbstverständlich — `destroy` fasst ausschließlich die
Wiederherstellung an.
---
## `cleanup`
Wird eine Wiederherstellung in der **Proxmox-Oberfläche** entfernt statt mit
`destroy`, bleibt etwas liegen:
* der Klon selbst kann übrigbleiben
* der Quell-Snapshot bleibt **geschützt** — die Vorhaltezeit scheitert dann
dauerhaft
```bash
pvesnap-recovery cleanup # zeigt Gefundenes, fragt, räumt auf
pvesnap-recovery cleanup --all # auch Datenträger ohne Elternteil
pvesnap-recovery cleanup -y # ohne Rückfrage
```
### Wonach gesucht wird
Nach Datenträgern der Form `vm-<id>-disk-N`, zu deren VMID es **keine
Konfigurationsdatei** unter `/etc/pve/nodes/*/` mehr gibt.
!!! info "Bewusst nicht über `/cluster/resources`"
Ein Gast auf einem abgemeldeten Node taucht dort unter Umständen nicht auf
— und dann würden die Platten einer **lebenden** Maschine als verwaist
gelten.
Die Konfigurationsdateien im Cluster-Dateisystem sind die verlässlichere
Quelle.
Zwei weitere Vorsichtsmaßnahmen:
| | |
|---|---|
| **Ohne Elternteil** wird nichts von selbst entfernt | dafür braucht es `--all` |
| **Der Schutz eines Snapshots wird nur gelöst**, wenn wirklich kein Klon mehr daran hängt | sonst würde ein noch lebender Klon seine Grundlage verlieren |
### Wann man es braucht
* Jemand hat eine Wiederherstellung in der Weboberfläche gelöscht
* Ein `destroy` ist mittendrin abgebrochen (Host neu gestartet, Netz weg)
* Im Protokoll des Dienstes taucht wiederholt `snapshot is protected` auf
Der letzte Fall ist der häufigste — und der, den man sonst lange sucht.
---
## Aufräumen prüfen
```bash
pvesnap-recovery list # sind noch welche eingetragen?
qm list | grep -i recovery # laufen noch welche?
rbd -p <pool> ls | grep -E 'vm-9[0-9]{3}-' # liegen noch Klone herum?
rbd -p <pool> snap ls vm-101-disk-0 # steht noch ein Schutz?
```
Bei der letzten Ausgabe steht in der Spalte `PROTECTED` ein `yes`, solange ein
Snapshot geschützt ist. Ohne zugehörigen Klon ist das ein Fall für `cleanup`.
---
## Vor der Deinstallation
`uninstall.sh` warnt, wenn noch Wiederherstellungen offen sind. Der Grund ist
derselbe:
!!! warning "Erst verwerfen, dann deinstallieren"
Nach dem Entfernen von pvesnap gibt es kein `destroy` und kein `cleanup`
mehr. Die Klone müsste man dann von Hand aufspüren, und den Snapshot-Schutz
von Hand lösen:
```bash
rbd -p <pool> snap unprotect vm-101-disk-0@auto-stuendlich-20260809-100000
```
Mit Werkzeug ist das ein Tastendruck. Ohne eine Stunde Sucherei.
+121
View File
@@ -0,0 +1,121 @@
site_name: pvesnap — Handbuch
site_description: >-
Automatische Snapshots für Proxmox VE: einrichten, Dateien zurückholen und
einen Snapshot als laufende Maschine starten.
site_author: pvesnap
copyright: Handbuch zu pvesnap
docs_dir: docs
site_dir: site
# Das Handbuch muss offline funktionieren - im Notfall steht vielleicht das
# halbe Netz. Deshalb: keine absolute site_url, keine Schriften vom CDN,
# Suchindex liegt neben den Seiten.
use_directory_urls: true
theme:
name: material
language: de
font: false # keine Google-Fonts, sonst braucht es Netz
# Bewusst nicht unter bilder/ - dort raeumt werkstatt/aufnehmen.sh auf.
favicon: stil/favicon.svg
icon:
logo: material/camera-timer
palette:
- media: "(prefers-color-scheme: light)"
scheme: default
primary: blue grey
accent: teal
toggle:
icon: material/weather-night
name: Zu dunkler Darstellung wechseln
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: blue grey
accent: teal
toggle:
icon: material/weather-sunny
name: Zu heller Darstellung wechseln
features:
- navigation.instant
- navigation.tracking
- navigation.sections
- navigation.indexes
- navigation.top
- toc.follow
- search.highlight
- search.suggest
- content.code.copy
- content.tooltips
plugins:
- search:
lang: de
separator: '[\s\-\.\_/]+'
markdown_extensions:
- abbr
- admonition
- attr_list
- def_list
- footnotes
- md_in_html
- tables
- toc:
permalink: true
permalink_title: Verweis auf diesen Abschnitt
toc_depth: 3
- pymdownx.details
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
- pymdownx.inlinehilite
- pymdownx.keys
- pymdownx.snippets
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.tasklist:
custom_checkbox: true
extra_css:
- stil/handbuch.css
extra:
generator: false
nav:
- Start: index.md
- Schnellstart: schnellstart.md
- Installation: installation.md
- Sichern:
- sichern/index.md
- Der Konfigurationseditor: sichern/editor.md
- Zeitpläne: sichern/zeitplan.md
- Welche Gäste?: sichern/auswahl.md
- Vorhaltezeit: sichern/vorhaltezeit.md
- Im Betrieb: sichern/betrieb.md
- Daten zurückholen:
- holen/index.md
- Der Explorer: holen/explorer.md
- Die Web-Oberfläche: holen/web.md
- Wiederherstellen:
- wiederherstellen/index.md
- Eine Maschine einrichten: wiederherstellen/einrichten.md
- Damit arbeiten: wiederherstellen/arbeiten.md
- Austauschlaufwerke: wiederherstellen/transfer.md
- Dongle und USB: wiederherstellen/dongle.md
- Mit Netzwerk: wiederherstellen/netzwerk.md
- Vom Snapshot lösen: wiederherstellen/loesen.md
- Verwerfen und aufräumen: wiederherstellen/verwerfen.md
- Nachschlagen:
- nachschlagen/index.md
- Alle Befehle: nachschlagen/befehle.md
- Alle Einstellungen: nachschlagen/konfiguration.md
- Tastenkürzel: nachschlagen/tasten.md
- Speicherarten: nachschlagen/speicher.md
- Fehlersuche: nachschlagen/fehlersuche.md
+139
View File
@@ -0,0 +1,139 @@
# Die Bildschirmfoto-Werkstatt
Die Bilder im Handbuch sind keine abfotografierten Terminals, sondern werden
erzeugt — aus dem **echten** Programmcode, gegen einen erfundenen Proxmox-Host.
```bash
./aufnehmen.sh # alle Bilder neu
./aufnehmen.sh explorer # nur die, deren Name "explorer" enthält
```
Ergebnis: `handbuch/docs/bilder/*.svg`
Das hat drei Gründe:
* **Sie stimmen.** Es läuft wirklich `pvesnap/recovery_ui.py`, nicht eine
Nachbildung. Ändert sich die Tastenleiste, ändert sich das Bild.
* **Sie sind reproduzierbar.** Derselbe Aufruf ergibt dasselbe Bild — gleiche
Daten, gleiche Uhrzeiten, gleiche Snapshot-Namen.
* **Kein echter Host wird angefasst.** Weder Daten noch Namen aus einer echten
Umgebung landen im Handbuch.
---
## Wie es funktioniert
```
scenes.py Liste aller Aufnahmen: Name, Szene, Tastenfolge, Titel
└─ probe.py startet eine Szene in einem Pseudo-Terminal
└─ stage.sh hängt die Kulisse an die richtigen Pfade (Namensraum)
└─ run.py biegt die Pfade im Programmcode um und ruft die Oberfläche auf
└─ pvesnap/… der echte Programmcode
└─ shot.py baut den Bildschirm nach und schreibt ihn als SVG
```
### `demo.py` — die Kulisse
Baut unter `werkstatt/demo/` ein erfundenes Rechenzentrum auf: zwei Nodes, zehn
Gäste, ein paar hundert Snapshots, zwei laufende Wiederherstellungen, drei
Austauschlaufwerke und einen Dateibaum für den Explorer.
Ein **fester Zeitpunkt** (`NOW`) sorgt dafür, dass zwei Aufnahmen derselben
Liste gleich aussehen — sonst würde die Beschreibung im Handbuch nicht mehr
zum Bild passen.
### `bin/pvesh`, `bin/perl`, `bin/rbd` — die Attrappen
pvesnap spricht mit Proxmox über genau drei Programme. Alle drei liegen hier als
kleine Python-Skripte, die aus `demo/state.json` antworten. Sie stehen im `PATH`
vor den echten.
Nichts davon verändert etwas — die schreibenden Aufrufe geben brav eine UPID
zurück und tun sonst nichts.
### `run.py` — die Pfade umbiegen
Setzt vor dem Start die Konstanten um, die sonst auf einen echten Host zeigen:
```python
recovery.REGISTRY = demo/lib/recovery.json
transfer.INDEX = demo/lib/transfer/index.json
transfer.CONF_ROOT = demo/pve/nodes
```
Der Programmcode selbst wird nicht angefasst. Die Bilder zeigen also wirklich
das, was auf dem Host auch zu sehen ist.
### `stage.sh` — die Pfade, die im Bild stehen
Der Explorer zeigt Pfade an. Im Handbuch sollen das die Pfade eines echten
Hosts sein (`/run/pvesnap/mnt/…`, `/root`, `/etc/pvesnap.conf`) und nicht die
Arbeitsverzeichnisse dieser Werkstatt.
Deshalb läuft jede Aufnahme in einem eigenen **Mount-Namensraum**
(`unshare -r -m`): Dort dürfen wir uns die Pfade hinbiegen, ohne am System
etwas zu verändern. `/etc` bekommt eine Overlay-Schicht, damit die
Konfiguration dort liegt, wo sie hingehört.
### `shot.py` — der Bildschirmnachbau
Startet das Programm in einem Pseudo-Terminal fester Größe (118 × 32), spielt
Tastendrücke ein, lässt [pyte](https://pypi.org/project/pyte/) den Bildschirm
nachbilden und schreibt das Ergebnis als SVG.
Zwei Feinheiten, die Zeit gekostet haben:
**`textLength` auf jedem Textstück.** Das SVG bettet keine Schrift ein — es
soll ja klein bleiben und offline funktionieren. Damit das Zeichenraster
trotzdem in jedem Browser sitzt, wird jede Zeichenkette fest vermessen.
**`NCURSES_NO_UTF8_ACS=1`.** ncurses zeichnet Rahmen sonst über den
Ersatzzeichensatz (`ESC ( 0`). pyte ignoriert das im UTF-8-Betrieb, und aus den
Linien würden Buchstaben (`lqqqk` statt `┌───┐`). Mit der Variablen liefert
ncurses echte Unicode-Rahmenzeichen.
---
## Eine Szene nachsehen
Zum Ausprobieren, ohne ein Bild zu schreiben — gibt den Bildschirm als Text aus:
```bash
../.venv/bin/python probe.py recovery
../.venv/bin/python probe.py recovery enter p
COLS=140 ROWS=40 ../.venv/bin/python probe.py explorer ab ab enter
```
Tastennamen: `ab auf links rechts enter tab esc space f2 f3 f5 f7 f9`,
alles andere wird als Zeichen geschickt.
Verfügbare Szenen stehen in `run.py` unter `SZENEN`.
---
## Eine Aufnahme hinzufügen
In `scenes.py` eine Zeile ergänzen:
```python
("recovery-neuerkram", "recovery", ["enter", "p"],
"root@pve1 — pvesnap-recovery"),
```
Fünftes Feld optional: Wartezeit in Sekunden, für Bildschirme, hinter denen
noch etwas arbeitet (sonst hält der Nachbau den letzten Stand fest, während im
Hintergrund noch ein Proxmox-Task läuft).
Braucht es dafür eine neue Szene, kommt sie in `run.py` dazu.
---
## Wenn etwas nicht klappt
| Symptom | Ursache |
|---|---|
| Bild ist leer oder zeigt einen Traceback | `scenes.py` meldet das selbst — mit `probe.py` nachsehen |
| Rahmen bestehen aus `lqqqk` | `NCURSES_NO_UTF8_ACS=1` fehlt in `probe.umgebung()` |
| Pfade zeigen ins Scratchpad | `unshare -r` ging nicht; `aufnehmen.sh` prüft das vorher |
| Pfeiltasten bewegen nichts | Es muss `\x1bOB` sein, nicht `\x1b[B` — curses schaltet auf den Anwendungsmodus |
| Ein Bildschirm hängt bei „bitte warten" | Eine Antwort der Attrappe fehlt; mit `log_level=DEBUG` sieht man den Aufruf |
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# ---------------------------------------------------------------------
# Alle Bildschirmfotos fuers Handbuch neu aufnehmen.
#
# ./aufnehmen.sh alle
# ./aufnehmen.sh explorer nur die, deren Name das enthaelt
#
# Ergebnis: handbuch/docs/bilder/*.svg
# Wie das funktioniert, steht in LIESMICH.md.
# ---------------------------------------------------------------------
set -euo pipefail
HIER="$(cd "$(dirname "$0")" && pwd)"
HANDBUCH="$(dirname "${HIER}")"
VENV="${HANDBUCH}/.venv"
info() { printf '\033[36m%s\033[0m\n' "$*"; }
rot() { printf '\033[31m%s\033[0m\n' "$*" >&2; }
# unshare -r braucht Benutzer-Namensraeume; ohne sie stehen im Explorer die
# Arbeitspfade dieser Werkstatt statt der Pfade eines echten Hosts.
if ! unshare -r -m --propagation private true 2>/dev/null; then
rot "Benutzer-Namensraeume sind nicht verfuegbar (unshare -r schlaegt fehl)."
rot "Ohne sie stimmen die Pfade in den Explorer-Bildern nicht."
exit 1
fi
if [ ! -x "${VENV}/bin/python" ]; then
info "Richte Bau-Umgebung in ${VENV} ein ..."
python3 -m venv "${VENV}"
"${VENV}/bin/pip" install --quiet --upgrade pip
fi
if ! "${VENV}/bin/python" -c "import pyte" 2>/dev/null; then
info "Installiere pyte (Bildschirm-Nachbau) ..."
"${VENV}/bin/pip" install --quiet pyte
fi
info "Baue die Demo-Kulisse ..."
"${VENV}/bin/python" "${HIER}/demo.py" >/dev/null
info "Nehme auf ..."
"${VENV}/bin/python" "${HIER}/scenes.py" "$@"
echo
info "Fertig: ${HANDBUCH}/docs/bilder/"
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Attrappe der Storage-Schicht.
pvesnap ruft `perl -e "<Skript>" <op> <args>` auf, um an PVE::Storage zu
kommen. Hier wird nur der Teil beantwortet, den die Bildschirmfotos brauchen -
Groessen und Pfade. Nichts wird angelegt oder geloescht.
"""
import sys
# Groessen der Datentraeger in der Kulisse, in Byte.
GROESSEN = {
"vm-100-disk-0": 60, "vm-101-disk-0": 400, "vm-102-disk-0": 2048,
"vm-105-disk-0": 250, "vm-130-disk-0": 80, "vm-120-disk-0": 120,
"subvol-110-disk-0": 16, "subvol-111-disk-0": 64,
"vm-9101-disk-0": 400, "vm-9102-disk-0": 250,
}
RAM = 32 * 1024 ** 3 # der gespeicherte Arbeitsspeicher von db01
def main(argv):
# argv: -e <skript> <op> <rest...>
rest = argv[2:] if len(argv) > 2 and argv[0] == "-e" else argv
if not rest:
return 1
op, args = rest[0], rest[1:]
if op == "size":
volid = args[0] if args else ""
name = volid.split(":", 1)[-1]
if "-state-" in name:
print("RESULT %d raw" % RAM)
else:
gigabyte = GROESSEN.get(name, 32)
print("RESULT %d raw" % (gigabyte * 1024 ** 3))
return 0
if op == "path":
volid = args[0] if args else ""
storage, _, name = volid.partition(":")
print("RESULT /dev/rbd/%s/%s" % (storage, name))
return 0
if op in ("activate", "deactivate", "free"):
print("RESULT ok")
return 0
if op == "clone":
volid = args[0] if args else ""
storage = volid.split(":", 1)[0]
print("RESULT %s:vm-%s-disk-0" % (storage, args[1] if len(args) > 1 else "0"))
return 0
if op == "alloc":
print("RESULT %s:vm-%s-disk-9" % (args[0], args[1]))
return 0
print("unbekannte Operation: %s" % op, file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env python3
"""Attrappe von `pvesh` - beantwortet Abfragen aus demo/state.json.
Nur so viel, wie die Oberflaechen fuer die Bildschirmfotos brauchen. Alles,
was etwas veraendern wuerde, gibt brav eine UPID zurueck und tut nichts.
"""
import json
import os
import sys
STATE = os.environ.get("PVESNAP_DEMO_STATE", "")
def load():
with open(STATE, "r", encoding="utf-8") as handle:
return json.load(handle)
def main(argv):
if len(argv) < 2:
return 1
verb, path = argv[0], argv[1]
args = {}
rest = argv[2:]
index = 0
while index < len(rest):
if rest[index].startswith("--"):
key = rest[index][2:]
value = rest[index + 1] if index + 1 < len(rest) else ""
args[key] = value
index += 2
else:
index += 1
data = load()
parts = [p for p in path.strip("/").split("/") if p]
if verb == "get":
result = handle_get(parts, args, data)
if result is _MISS:
print("no such resource '%s'" % path, file=sys.stderr)
return 2
if args.get("output-format") == "json":
print(json.dumps(result))
else:
print(result)
return 0
# Alles Veraendernde: Proxmox liefert eine UPID, der Aufrufer wartet darauf.
node = parts[1] if len(parts) > 1 and parts[0] == "nodes" else "pve1"
if parts[-1] == "spiceproxy":
print(json.dumps({
"type": "spice", "host": "pvespiceproxy:63f4a2b1:100:pve1::abc",
"proxy": "http://10.20.0.11", "tls-port": 61000,
"password": "aG9jaGdlaGVpbQ==", "ca": "-----BEGIN CERTIFICATE-----\\n"
"MIIFxTCCA62gAwIBAgIB...\\n-----END CERTIFICATE-----\\n",
"host-subject": "OU=PVE Cluster Node,O=Proxmox Virtual Environment,"
"CN=pve1.hausnetz.lan",
"delete-this-file": 1, "secure-attention": "Ctrl+Alt+Ins",
"toggle-fullscreen": "Shift+F11", "release-cursor": "Ctrl+Alt+R",
"title": "VM 9102 - warenwirtschaft-w",
}))
return 0
print("UPID:%s:00001A2B:0BC3D4E5:68970000:qmsnapshot:100:root@pam:" % node)
return 0
_MISS = object()
def handle_get(parts, args, data):
if parts == ["cluster", "resources"]:
return data["resources"]
if len(parts) == 2 and parts[0] == "storage":
return {"ceph-vm": {"type": "rbd", "pool": "vmdaten", "shared": 1,
"content": "images", "storage": "ceph-vm",
"krbd": 0, "monhost": "10.20.0.11 10.20.0.12"},
"ceph-ct": {"type": "rbd", "pool": "ctdaten", "shared": 1,
"content": "rootdir", "storage": "ceph-ct"},
"local": {"type": "dir", "shared": 0, "storage": "local",
"content": "iso,vztmpl,backup"},
}.get(parts[1], _MISS)
if parts == ["storage"]:
return [{"storage": "ceph-vm", "type": "rbd", "shared": 1},
{"storage": "ceph-ct", "type": "rbd", "shared": 1},
{"storage": "local", "type": "dir", "shared": 0}]
if parts == ["cluster", "nextid"]:
wanted = args.get("vmid")
if wanted:
taken = {str(r["vmid"]) for r in data["resources"]}
if str(wanted) in taken:
print("VM %s already exists" % wanted, file=sys.stderr)
sys.exit(2)
return int(wanted)
return data["nextid"]
# Muss vor dem allgemeinen /nodes-Zweig stehen: sonst wird "tasks" fuer
# eine Gastart gehalten und /status liefert einen leeren Datensatz - die
# Warteschleife auf den Task laeuft dann bis zum Zeitlimit.
if len(parts) >= 3 and parts[0] == "nodes" and parts[2] == "tasks":
if parts[-1] == "log":
return [{"n": 1, "t": "TASK OK"}]
return {"status": "stopped", "exitstatus": "OK", "type": "qmconfig",
"upid": parts[3] if len(parts) > 3 else "", "node": parts[1]}
if len(parts) >= 4 and parts[0] == "nodes":
node, kind, vmid = parts[1], parts[2], parts[3]
key = "%s/%s/%s" % (node, kind, vmid)
tail = parts[4:]
if tail == ["snapshot"]:
entries = list(data["snapshots"].get(key, []))
entries.append({"name": "current", "digest": "0" * 40,
"description": "You are here!",
"parent": entries[-1]["name"] if entries else ""})
return entries
if tail == ["config"]:
return _conf(data["configs"].get(key, ""))
if len(tail) == 3 and tail[0] == "snapshot" and tail[2] == "config":
base = _conf(data["configs"].get(key, ""))
base["parent"] = tail[1]
base["snaptime"] = next(
(s["snaptime"] for s in data["snapshots"].get(key, [])
if s["name"] == tail[1]), 0)
# Ein Snapshot "mit RAM" traegt den Arbeitsspeicher als eigenes
# Volume; genau daran erkennt pvesnap, dass es warm gehen kann.
if tail[1].startswith("auto-stuendlich"):
base["vmstate"] = "ceph-vm:vm-%s-state-%s" % (vmid, tail[1])
base["runningmachine"] = "pc-i440fx-9.0+pve0"
base["runningcpu"] = "x86-64-v2-AES,enforce"
return base
if tail == ["status", "current"]:
return data["status"].get(key, {})
if tail and tail[0] == "status":
return data["status"].get(key, {})
return _MISS
def _conf(text):
result = {}
for line in text.splitlines():
if ":" in line and not line.startswith("#"):
key, _, value = line.partition(":")
result[key.strip()] = value.strip()
for key in ("cores", "memory", "sockets", "numa", "agent"):
if key in result:
try:
result[key] = int(result[key])
except ValueError:
pass
return result
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Attrappe von `rbd` - nur lesende Auskuenfte fuer die Bildschirmfotos.
VM 9101 ist ein Linked Clone (haengt am Quell-Snapshot), VM 9102 ist bereits
geloest. Genau diese beiden Zustaende soll das Handbuch zeigen.
"""
import sys
VERBUNDEN = "vm-9101-disk-0" # haengt noch am Snapshot
QUELLE = "vmdaten/vm-101-disk-0@auto-stuendlich-20260809-100000"
# Optionen, hinter denen noch ein Wert steht - sonst haelt man "-m <monhost>"
# fuer den Befehl.
MIT_WERT = {"-m", "--id", "--keyring", "-c", "--conf", "-p", "--pool",
"--namespace", "-n", "--name"}
def zerlege(argv):
"""(Befehl, Abbild) aus der Kommandozeile."""
rest = []
index = 0
while index < len(argv):
wort = argv[index]
if wort in MIT_WERT:
index += 2
continue
if wort.startswith("-"):
index += 1
continue
rest.append(wort)
index += 1
return (rest[0] if rest else ""), (rest[1] if len(rest) > 1 else "")
def main(argv):
if not argv:
return 1
befehl, ziel = zerlege(argv)
name = ziel.split("/")[-1].split("@")[0]
if befehl == "info":
print("rbd image '%s':" % name)
print("\tsize 400 GiB in 102400 objects")
print("\torder 22 (4 MiB objects)")
print("\tsnapshot_count: 0")
print("\tid: 1f5c3d9a7b2e")
print("\tblock_name_prefix: rbd_data.1f5c3d9a7b2e")
print("\tformat: 2")
print("\tfeatures: layering, exclusive-lock")
print("\top_features:")
print("\tflags:")
if name == VERBUNDEN:
print("\tparent: %s" % QUELLE)
print("\toverlap: 400 GiB")
return 0
if befehl == "du":
print("NAME PROVISIONED USED")
print("%-19s 400 GiB 2.4 GiB" % name)
return 0
if befehl in ("flatten", "snap"):
return 0
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+478
View File
@@ -0,0 +1,478 @@
#!/usr/bin/env python3
"""Ein erfundenes Rechenzentrum fuer die Bildschirmfotos im Handbuch.
Baut unter DEMO/ eine vollstaendige Kulisse auf: Antworten fuer ein
Attrappen-pvesh, Gast-Konfigurationen, die Merkliste der Wiederherstellungen,
die Transfer-Laufwerke und einen Dateibaum fuer den Explorer.
Nichts davon fasst einen echten Proxmox-Host an.
"""
from __future__ import annotations
import json
import os
import shutil
from datetime import datetime, timedelta
HERE = os.path.dirname(os.path.abspath(__file__))
DEMO = os.path.join(HERE, "demo")
# Ein fester Zeitpunkt - sonst sehen zwei Bildschirmfotos derselben Liste
# unterschiedlich aus, und im Handbuch passt die Beschreibung nicht mehr.
NOW = datetime(2026, 8, 9, 11, 42, 0)
NODES = {"pve1": "10.20.0.11", "pve2": "10.20.0.12"}
GUESTS = [
# vmid, name, typ, node, zustand, tags, pool
(100, "web01", "qemu", "pve1", "running", "produktion;stuendlich", "Hausnetz"),
(101, "db01", "qemu", "pve1", "running", "produktion;datenbank;stuendlich", "Hausnetz"),
(102, "fileserver", "qemu", "pve2", "running", "produktion", "Hausnetz"),
(105, "warenwirtschaft", "qemu", "pve2", "running", "produktion;dongle", "Hausnetz"),
(110, "mailgw", "lxc", "pve1", "running", "produktion", "Hausnetz"),
(111, "gitlab", "lxc", "pve2", "running", "entwicklung", ""),
(120, "build-test", "qemu", "pve2", "stopped", "nosnap", ""),
(130, "dc01", "qemu", "pve1", "running", "produktion", "Hausnetz"),
(9101, "db01-live", "qemu", "pve1", "running", "pvesnap-recovery", ""),
(9102, "warenwirtschaft-w", "qemu", "pve2", "stopped", "pvesnap-recovery", ""),
]
# Wie viele Snapshots welcher Gruppe je Gast - so, wie es nach ein paar Wochen
# Betrieb tatsaechlich aussieht.
PLAN = {
100: [("stuendlich", 24, 3600), ("taeglich", 8, 86400)],
101: [("stuendlich", 24, 3600), ("taeglich", 14, 86400), ("monatlich", 3, 2592000)],
102: [("taeglich", 9, 86400)],
105: [("taeglich", 12, 86400), ("monatlich", 2, 2592000)],
110: [("taeglich", 7, 86400)],
111: [("taeglich", 4, 86400)],
130: [("taeglich", 11, 86400)],
}
# Von Hand angelegte Snapshots - die pvesnap niemals anfasst.
HANDMADE = {
101: [("vor-update-14.2", "Vor dem Update auf PostgreSQL 14.2", 6 * 86400)],
105: [("golden", "Frisch eingerichtet, Lizenz aktiviert", 40 * 86400)],
}
DESCRIPTION = ("pvesnap | Gruppe: %s | erstellt: %s | Vorhaltezeit: %s | max: %d")
KEEP = {"stuendlich": ("2 Tage", 24), "taeglich": ("21 Tage", 14),
"monatlich": ("400 Tage", 6)}
QEMU_CONF = """\
agent: 1
boot: order=scsi0
cores: %(cores)d
cpu: x86-64-v2-AES
memory: %(memory)d
meta: creation-qemu=9.0.2
name: %(name)s
net0: virtio=%(mac)s,bridge=vmbr0,firewall=1
numa: 0
ostype: %(ostype)s
scsi0: ceph-vm:vm-%(vmid)d-disk-0,discard=on,iothread=1,size=%(size)dG
scsihw: virtio-scsi-single
smbios1: uuid=%(uuid)s
sockets: 1
vmgenid: %(genid)s
"""
LXC_CONF = """\
arch: amd64
cores: 2
features: nesting=1
hostname: %(name)s
memory: 2048
net0: name=eth0,bridge=vmbr0,firewall=1,hwaddr=%(mac)s,ip=dhcp,type=veth
ostype: debian
rootfs: ceph-ct:subvol-%(vmid)d-disk-0,size=%(size)dG
swap: 512
unprivileged: 1
"""
HARDWARE = {
100: dict(cores=4, memory=8192, size=60, ostype="l26"),
101: dict(cores=8, memory=32768, size=400, ostype="l26"),
102: dict(cores=4, memory=8192, size=2048, ostype="l26"),
105: dict(cores=6, memory=16384, size=250, ostype="win11"),
120: dict(cores=8, memory=16384, size=120, ostype="l26"),
130: dict(cores=4, memory=8192, size=80, ostype="win11"),
110: dict(size=16),
111: dict(size=64),
9101: dict(cores=8, memory=32768, size=400, ostype="l26"),
9102: dict(cores=6, memory=16384, size=250, ostype="win11"),
}
def _mac(vmid, index=0):
return "BC:24:11:%02X:%02X:%02X" % (vmid // 256, vmid % 256, 0x40 + index)
def _uuid(vmid):
return "8f3c%04d-11ee-4a7b-9c2d-%012d" % (vmid, vmid * 7919)
def _genid(vmid):
return "b2e1%04d-7f45-4c18-a3d9-%012d" % (vmid, vmid * 104729)
# ---------------------------------------------------------------------------
# Snapshots
# ---------------------------------------------------------------------------
def _snapshots_for(vmid):
"""Alle Snapshots eines Gastes, aeltester zuerst, mit Elternkette."""
entries = []
for slug, count, step in PLAN.get(vmid, []):
keep_time, keep_count = KEEP[slug]
for index in range(count):
moment = NOW - timedelta(seconds=step * (index + 1))
if slug == "taeglich":
moment = moment.replace(hour=2, minute=30, second=0)
elif slug == "monatlich":
moment = moment.replace(day=1, hour=4, minute=0, second=0)
else:
moment = moment.replace(minute=0, second=0)
entries.append({
"name": "auto-%s-%s" % (slug, moment.strftime("%Y%m%d-%H%M%S")),
"snaptime": int(moment.timestamp()),
"description": DESCRIPTION % (slug, moment.strftime("%Y-%m-%d %H:%M:%S"),
keep_time, keep_count),
})
for name, note, age in HANDMADE.get(vmid, []):
moment = NOW - timedelta(seconds=age)
entries.append({"name": name, "snaptime": int(moment.timestamp()),
"description": note})
entries.sort(key=lambda e: e["snaptime"])
parent = ""
for entry in entries:
entry["parent"] = parent
parent = entry["name"]
return entries
# ---------------------------------------------------------------------------
# Kulisse aufbauen
# ---------------------------------------------------------------------------
def _write(path, text):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as handle:
handle.write(text)
def _guest_config(vmid, name, kind):
values = dict(HARDWARE.get(vmid, {}))
values.update(vmid=vmid, name=name, mac=_mac(vmid),
uuid=_uuid(vmid), genid=_genid(vmid))
values.setdefault("cores", 2)
values.setdefault("memory", 2048)
values.setdefault("size", 32)
values.setdefault("ostype", "l26")
return (LXC_CONF if kind == "lxc" else QEMU_CONF) % values
def build():
if os.path.isdir(DEMO):
shutil.rmtree(DEMO)
resources, snapshots, configs, status = [], {}, {}, {}
for vmid, name, kind, node, state, tags, pool in GUESTS:
resources.append({
"vmid": vmid, "name": name, "type": kind, "node": node,
"status": state, "tags": tags, "pool": pool,
"maxmem": HARDWARE.get(vmid, {}).get("memory", 2048) * 1024 * 1024,
"maxdisk": HARDWARE.get(vmid, {}).get("size", 32) * 1024 ** 3,
"uptime": 486231 if state == "running" else 0,
})
key = "%s/%s/%d" % (node, kind, vmid)
snapshots[key] = _snapshots_for(vmid)
configs[key] = _guest_config(vmid, name, kind)
status[key] = {
"status": state, "vmid": vmid, "name": name,
"qmpstatus": "running" if state == "running" else "stopped",
"uptime": 486231 if state == "running" else 0,
"maxmem": HARDWARE.get(vmid, {}).get("memory", 2048) * 1024 * 1024,
"cpus": HARDWARE.get(vmid, {}).get("cores", 2),
"spice": 1 if vmid == 9102 else 0,
}
# Die beiden Wiederherstellungen haben eine eigene Konfiguration: kein Netz
# bzw. Netz mit den MAC-Adressen des Originals, dazu das Transfer-Laufwerk.
live = _guest_config(9101, "db01-live", "qemu")
live = live.replace("net0: virtio=%s,bridge=vmbr0,firewall=1\n" % _mac(9101), "")
live = live.replace("scsi0: ceph-vm:vm-9101-disk-0",
"scsi0: ceph-vm:vm-9101-disk-0")
live += "scsi1: /dev/loop3,backup=0,replicate=0\n"
live += "tags: pvesnap-recovery\n"
configs["pve1/qemu/9101"] = live
warm = _guest_config(9102, "warenwirtschaft-w", "qemu")
warm = warm.replace("net0: virtio=%s" % _mac(9102), "net0: virtio=%s" % _mac(105))
warm = warm.replace("smbios1: uuid=%s" % _uuid(9102),
"smbios1: uuid=%s" % _uuid(105))
warm += "tags: pvesnap-recovery\nusb0: spice\nusb1: spice\nvga: qxl\n"
configs["pve2/qemu/9102"] = warm
_write(os.path.join(DEMO, "state.json"), json.dumps({
"resources": resources, "snapshots": snapshots,
"configs": configs, "status": status, "nextid": 9103,
"nodes": NODES,
}, indent=1))
# /etc/pve nachbilden - daraus liest pvesnap die Belegung der Laufwerke.
for vmid, name, kind, node, _s, _t, _p in GUESTS:
sub = "lxc" if kind == "lxc" else "qemu-server"
_write(os.path.join(DEMO, "pve", "nodes", node, sub, "%d.conf" % vmid),
configs["%s/%s/%d" % (node, kind, vmid)])
_write(os.path.join(DEMO, "pve", ".members"),
json.dumps({"nodename": "pve1", "version": 8,
"nodelist": {n: {"id": i + 1, "online": 1, "ip": ip}
for i, (n, ip) in enumerate(NODES.items())}},
indent=1))
_build_registry()
_build_transfers()
_build_config()
_build_tree()
return DEMO
def _build_registry():
created_live = int((NOW - timedelta(minutes=38)).timestamp())
created_warm = int((NOW - timedelta(hours=5, minutes=12)).timestamp())
entries = [
{"vmid": 9101, "type": "qemu", "node": "pve1", "name": "db01-live",
"source": 101, "source_node": "pve1",
"snapshot": "auto-stuendlich-20260809-100000", "mode": "live",
"created": created_live,
"volumes": ["ceph-vm:vm-9101-disk-0"],
"protected": [["ceph-vm:vm-101-disk-0", "auto-stuendlich-20260809-100000"]],
"transfers": [{"name": "dumps", "key": "scsi1",
"drive": "/dev/loop3,backup=0,replicate=0",
"kind": "disk", "pending": False}],
"resumed": True},
{"vmid": 9102, "type": "qemu", "node": "pve2", "name": "warenwirtschaft-w",
"source": 105, "source_node": "pve2",
"snapshot": "auto-taeglich-20260809-023000", "mode": "recover",
"created": created_warm,
"volumes": ["ceph-vm:vm-9102-disk-0"],
"protected": [],
"transfers": [], "resumed": False},
]
_write(os.path.join(DEMO, "lib", "recovery.json"), json.dumps(entries, indent=1))
def _build_transfers():
volumes = [
{"name": "dumps", "image": os.path.join(DEMO, "lib", "transfer", "dumps.img"),
"size": 20 * 1024 ** 3, "fs": "exfat", "label": "DUMPS",
"note": "Datenbank-Dumps und Exporte",
"created": int((NOW - timedelta(days=41)).timestamp()), "partitioned": True},
{"name": "werkzeuge",
"image": os.path.join(DEMO, "lib", "transfer", "werkzeuge.img"),
"size": 4 * 1024 ** 3, "fs": "exfat", "label": "WERKZEUGE",
"note": "Skripte, Treiber, Installer",
"created": int((NOW - timedelta(days=41)).timestamp()), "partitioned": True},
{"name": "austausch",
"image": os.path.join(DEMO, "lib", "transfer", "austausch.img"),
"size": 8 * 1024 ** 3, "fs": "exfat", "label": "AUSTAUSCH",
"note": "Alles andere",
"created": int((NOW - timedelta(days=9)).timestamp()), "partitioned": True},
]
_write(os.path.join(DEMO, "lib", "transfer", "index.json"),
json.dumps(volumes, indent=1))
# Duenn besetzte Abbilddateien: sie kosten keinen Plattenplatz, aber
# Volume.used_bytes findet echte Werte vor.
for volume, used in zip(volumes, (3_400_000_000, 780_000_000, 0)):
with open(volume["image"], "wb") as handle:
handle.truncate(volume["size"])
if used:
handle.seek(0)
handle.write(b"\0" * min(used, 4 * 1024 ** 2))
os.truncate(volume["image"], volume["size"])
def _build_config():
_write(os.path.join(DEMO, "pvesnap.conf"), """\
[global]
prefix = auto
check_interval = 60s
state_file = /var/lib/pvesnap/state.json
log_level = INFO
task_timeout = 15m
retries = 2
retry_delay = 60s
pause_between = 0s
run_on_start = no
dry_run = no
description = pvesnap | Gruppe: {group} | erstellt: {datetime} | Vorhaltezeit: {keep_time} | max: {keep_count}
[defaults]
enabled = yes
skip_stopped = no
vmstate = no
[group:stuendlich]
interval = 1h
align = yes
keep_count = 24
keep_time = 2d
tags = stuendlich
skip_stopped = yes
[group:taeglich]
schedule = daily
at = 02:30
keep_count = 14
keep_time = 21d
tags = produktion
exclude_tags = nosnap
[group:monatlich]
schedule = monthly
day_of_month = 1
at = 04:00
keep_count = 6
keep_time = 400d
all = yes
exclude_tags = nosnap, pvesnap-recovery
description = Monatssicherung {name} ({vmid}) vom {date}
""")
FSTAB = """\
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a device; this may
# be used with UUID= as a more robust way to name devices that works even if
# disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
UUID=4c1a9f3e-2b77-4d18-9a5c-7e3f0d1b8a26 / ext4 errors=remount-ro 0 1
UUID=9f2c-31AD /boot/efi vfat umask=0077 0 1
/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi1 /srv/dumps xfs defaults 0 2
/swap.img none swap sw 0 0
"""
SICHERUNG = """\
#!/bin/bash
# Naechtlicher Dump - laeuft aus der Crontab um 01:50, also vor dem Snapshot.
set -euo pipefail
ZIEL=/srv/dumps
STAMPE=$(date +%Y%m%d-%H%M)
for DB in kunden auftraege artikel; do
pg_dump -Fc "$DB" | gzip -1 > "$ZIEL/${DB}-${STAMPE}.sql.gz"
done
find "$ZIEL" -name '*.sql.gz' -mtime +14 -delete
"""
LIESMICH = """\
Transfer-Laufwerk dumps
=======================
Angelegt mit pvesnap-recovery (Taste v in der Uebersicht).
Dieses Laufwerk gehoert zu keiner Maschine. Es wird beim Start einer
Wiederherstellung angehaengt und danach wieder freigegeben - der Inhalt
bleibt erhalten.
Immer nur an einer Stelle benutzen: entweder am Host eingehaengt oder in
einem Gast. Beides gleichzeitig zerlegt das Dateisystem.
"""
TREE = {
"etc": {
"postgresql": {"14": {"main": {
"postgresql.conf": 28_412, "pg_hba.conf": 5_137, "pg_ident.conf": 1_636}}},
"nginx": {"nginx.conf": 1_482, "sites-enabled": {"default": 2_416}},
"fstab": FSTAB, "hostname": "db01\n", "hosts": 274, "passwd": 2_143,
"shadow": 1_309, "ssh": {"sshd_config": 3_290},
},
"home": {"stefan": {"notizen.txt": 1_204, ".bashrc": 3_771}},
"root": {".bash_history": 8_214, "sicherung.sh": SICHERUNG},
"srv": {"dumps": {
"db01-20260809-0200.sql.gz": 4_183_244_800,
"db01-20260808-0200.sql.gz": 4_106_112_512,
"kunden-export.csv": 88_412_160}},
"var": {"log": {"syslog": 12_884_901, "auth.log": 940_233,
"postgresql": {"postgresql-14-main.log": 3_402_118}},
"lib": {"postgresql": {"14": {"main": {"PG_VERSION": 3,
"postgresql.auto.conf": 88}}}}},
}
# Alles, was frisch angelegt wird, traegt sonst das Datum von heute - im
# Bildschirmfoto sieht ein Serverdateisystem dann aus wie eben ausgepackt.
MTIMES = {
"srv/dumps/db01-20260809-0200.sql.gz": NOW - timedelta(hours=9, minutes=12),
"srv/dumps/db01-20260808-0200.sql.gz": NOW - timedelta(days=1, hours=9),
"srv/dumps/kunden-export.csv": NOW - timedelta(days=2, hours=4),
"srv/dumps": NOW - timedelta(hours=9),
"var/log/syslog": NOW - timedelta(minutes=3),
"var/log/auth.log": NOW - timedelta(minutes=41),
"var/log": NOW - timedelta(minutes=3),
"root/.bash_history": NOW - timedelta(days=1, hours=2),
"root/sicherung.sh": NOW - timedelta(days=96),
"home/stefan/notizen.txt": NOW - timedelta(days=12),
"etc/postgresql/14/main/postgresql.conf": NOW - timedelta(days=214),
"etc/nginx/nginx.conf": NOW - timedelta(days=402),
"etc/fstab": NOW - timedelta(days=611),
"etc/hostname": NOW - timedelta(days=611),
}
GRUNDALTER = timedelta(days=611) # der Tag, an dem der Server aufgesetzt wurde
def _build_tree():
"""Ein glaubhaftes Linux-Wurzelverzeichnis fuer den Explorer."""
def make(base, spec, prefix=""):
for name, value in spec.items():
path = os.path.join(base, name)
relativ = "%s/%s" % (prefix, name) if prefix else name
if isinstance(value, dict):
os.makedirs(path, exist_ok=True)
make(path, value, relativ)
elif isinstance(value, str):
# Echter Inhalt - damit die Dateivorschau (F3) etwas zu zeigen hat.
with open(path, "w", encoding="utf-8") as handle:
handle.write(value)
else:
with open(path, "wb") as handle:
handle.truncate(value)
moment = MTIMES.get(relativ, NOW - GRUNDALTER)
stamp = moment.timestamp()
os.utime(path, (stamp, stamp))
root = os.path.join(DEMO, "snapshot")
os.makedirs(root, exist_ok=True)
make(root, TREE)
lokal = os.path.join(DEMO, "lokal")
os.makedirs(lokal, exist_ok=True)
make(lokal, {"werkzeuge": {"pg_repack.deb": 412_160, "pruefen.sh": 2_048},
"holen": {}, "notizen.md": 3_190})
# Was auf einem Transfer-Laufwerk liegt, wenn man es vorbereitet hat.
transfer = os.path.join(DEMO, "transfer-dumps")
os.makedirs(transfer, exist_ok=True)
make(transfer, {"LIESMICH.txt": LIESMICH,
"werkzeuge": {"pg_dump-14": 1_284_096, "7z.exe": 1_140_224},
"eingang": {}, "ausgang": {}})
for basis, alter in ((lokal, timedelta(days=3)), (transfer, timedelta(days=41))):
stamp = (NOW - alter).timestamp()
for wurzel, ordner, dateien in os.walk(basis):
for name in list(ordner) + list(dateien):
os.utime(os.path.join(wurzel, name), (stamp, stamp))
os.utime(wurzel, (stamp, stamp))
if __name__ == "__main__":
print(build())
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Eine Szene starten und den Bildschirm als Text zeigen - zum Nachsehen."""
import os
import sys
import time
HERE = os.path.dirname(os.path.abspath(__file__)) # handbuch/werkstatt
HANDBUCH = os.path.dirname(HERE) # handbuch
REPO = os.path.dirname(HANDBUCH) # Projektwurzel
sys.path.insert(0, HERE)
from shot import Terminal # noqa: E402
TASTEN = {"ab": "\x1bOB", "auf": "\x1bOA", "rechts": "\x1bOC", "links": "\x1bOD",
"enter": "\r", "tab": "\t", "esc": "\x1b", "f2": "\x1bOQ", "f3": "\x1bOR",
"f5": "\x1b[15~", "f7": "\x1b[18~", "f9": "\x1b[20~", "space": " "}
def umgebung():
env = dict(os.environ)
env["PATH"] = os.path.join(HERE, "bin") + ":" + env["PATH"]
env["PVESNAP_DEMO_STATE"] = os.path.join(HERE, "demo", "state.json")
# ncurses zeichnet Rahmen sonst ueber den Ersatzzeichensatz (ESC ( 0). Der
# Bildschirmnachbau ignoriert das im UTF-8-Betrieb, und aus den Linien
# wuerden Buchstaben. So kommen echte Unicode-Rahmenzeichen heraus.
env["NCURSES_NO_UTF8_ACS"] = "1"
env["PVESNAP_SRC"] = os.environ.get("PVESNAP_SRC", REPO)
env["PYTHONPATH"] = env["PVESNAP_SRC"]
return env
# Dieselbe Umgebung, die auch bauen.sh benutzt - dort wird pyte nachinstalliert.
VENV = os.path.join(HANDBUCH, ".venv", "bin", "python")
def befehl(szene):
"""Die Aufnahme laeuft in einem eigenen Mount-Namensraum - siehe stage.sh."""
return ["unshare", "-r", "-m", "--propagation", "private",
os.path.join(HERE, "stage.sh"), szene]
def starte(szene, cols=100, rows=30):
env = umgebung()
env["DOKU"] = HERE
env["PY"] = VENV
return Terminal(befehl(szene), cols=cols, rows=rows, env=env, cwd=HERE)
if __name__ == "__main__":
szene = sys.argv[1]
tasten = [TASTEN.get(t, t) for t in sys.argv[2:]]
term = starte(szene, cols=int(os.environ.get("COLS", 100)),
rows=int(os.environ.get("ROWS", 30)))
try:
time.sleep(1.2)
term.settle()
term.send(tasten, pause=0.3)
time.sleep(0.3)
term.settle()
for line in term.screen.display:
print("|" + line.rstrip() + "|")
finally:
term.close()
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env python3
"""Startet eine Oberflaeche von pvesnap gegen die Demo-Kulisse.
Aufruf: run.py <szene> [argumente]
Der eigentliche Trick steckt in _patch(): alle Pfade, die sonst auf einen
echten Proxmox-Host zeigen, werden auf demo/ umgebogen. Der Programmcode
selbst wird nicht angefasst - die Bildschirmfotos zeigen also wirklich das,
was auf dem Host auch zu sehen ist.
"""
from __future__ import annotations
import curses
import json
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
DEMO = os.path.join(HERE, "demo")
sys.path.insert(0, os.environ.get("PVESNAP_SRC", ""))
# Wo die Kulisse haengt, wenn stage.sh sie eingehaengt hat. Ohne Namensraum
# (beim schnellen Nachsehen) wird auf die Verzeichnisse selbst zurueckgefallen.
SNAP_MNT = "/run/pvesnap/mnt/101-auto-taeglich-20260809-023000"
TRANSFER_MNT = "/run/pvesnap/transfer/dumps"
LOKAL = "/root"
def _oder(pfad, ersatz):
return pfad if os.path.isdir(pfad) and os.listdir(pfad) else ersatz
def _patch():
from pvesnap import preflight, recovery, transfer, tui
recovery.STATE_DIR = os.path.join(DEMO, "lib")
recovery.REGISTRY = os.path.join(DEMO, "lib", "recovery.json")
recovery.CONF_ROOT = os.path.join(DEMO, "pve", "nodes")
transfer.TRANSFER_DIR = os.path.join(DEMO, "lib", "transfer")
transfer.INDEX = os.path.join(DEMO, "lib", "transfer", "index.json")
transfer.MOUNT_ROOT = "/run/pvesnap/transfer"
transfer.CONF_ROOT = recovery.CONF_ROOT
# Loop-Geraete und Einhaengepunkte gibt es hier nicht - also erzaehlen wir
# sie. "dumps" haengt in der Maschine, "austausch" gerade am Host.
abbild = lambda name: os.path.join(transfer.TRANSFER_DIR, name + ".img")
transfer._loop_devices = lambda: {abbild("dumps"): "/dev/loop3",
abbild("austausch"): "/dev/loop5"}
transfer._mounts = lambda: {"/dev/loop5p1": os.path.join(transfer.MOUNT_ROOT,
"austausch")}
with open(os.path.join(DEMO, "pve", ".members"), encoding="utf-8") as handle:
mitglieder = json.load(handle)
recovery._ADDRESSES.update({n: e["ip"] for n, e
in mitglieder["nodelist"].items()})
preflight.check_environment = lambda require_pve=True: []
tui.service_state = lambda: (True, "active")
tui.service_text = lambda: "Dienst: laeuft"
def _explorer_panes(links_pfad, links_titel, untertitel, links_schreibbar=False):
from pvesnap.explorer import Explorer, Pane
links = Pane(path=links_pfad, root=links_pfad,
readonly=not links_schreibbar, title=links_titel)
rechts = Pane(path=_oder(LOKAL, os.path.join(DEMO, "lokal")), root="/",
readonly=False, title="Lokaler Rechner")
return Explorer(links, rechts, subtitle=untertitel)
# ---------------------------------------------------------------------------
# Szenen
# ---------------------------------------------------------------------------
def szene_config(stdscr):
from pvesnap.tui import Editor
pfad = "/etc/pvesnap.conf"
editor = Editor(pfad if os.path.exists(pfad)
else os.path.join(DEMO, "pvesnap.conf"))
editor.load()
editor.run(stdscr)
def szene_recovery(stdscr):
from pvesnap.proxmox import Proxmox
from pvesnap.recovery_ui import _overview
_overview(stdscr, Proxmox())
def szene_transfer(stdscr):
from pvesnap import transfer_ui
from pvesnap.recovery_ui import _browse_transfer
transfer_ui.screen(stdscr, browse=_browse_transfer)
def szene_explorer(stdscr):
_explorer_panes(_oder(SNAP_MNT, os.path.join(DEMO, "snapshot")),
"VM 101 (db01) @ auto-taeglich-20260809-023000",
"VM 101 (db01) - Snapshot auto-taeglich-20260809-023000"
).run(stdscr)
def szene_explorer_transfer(stdscr):
_explorer_panes(_oder(TRANSFER_MNT, os.path.join(DEMO, "transfer-dumps")),
"Transfer: dumps (DUMPS)",
"Transfer-Laufwerk dumps - beide Seiten beschreibbar",
links_schreibbar=True).run(stdscr)
def szene_gastauswahl(stdscr):
from pvesnap.explorer import _pick_guest
from pvesnap.proxmox import Proxmox
_pick_guest(stdscr, Proxmox().inventory())
def szene_snapshotauswahl(stdscr):
from pvesnap.explorer import _pick_snapshot
from pvesnap.proxmox import Proxmox
from pvesnap.snapfs import list_snapshots
proxmox = Proxmox()
gast = next(g for g in proxmox.inventory() if g.vmid == 101)
_pick_snapshot(stdscr, [s for s in list_snapshots(proxmox, gast) if s.complete])
def szene_neu(stdscr):
"""Die Optionsmaske des Assistenten - ohne etwas anzulegen."""
from pvesnap.proxmox import Proxmox
from pvesnap.recovery_ui import _options
from pvesnap.recovery import Spec
proxmox = Proxmox()
gast = next(g for g in proxmox.inventory() if g.vmid == 101)
spec = Spec(mode="live")
spec.snapshot = "auto-stuendlich-20260809-100000"
spec.newid = 9103
spec.node = "pve1"
spec.transfers = ["werkzeuge"]
_options(stdscr, gast, spec)
def _vorhaben(stdscr, vmid, snapshot, **werte):
from pvesnap.curses_util import init_colors
from pvesnap.proxmox import Proxmox
from pvesnap.recovery import Spec, plan
from pvesnap.recovery_ui import _pages, summary_lines
init_colors()
proxmox = Proxmox()
gast = next(g for g in proxmox.inventory() if g.vmid == vmid)
spec = Spec(mode=werte.pop("mode", "live"))
for schluessel, wert in werte.items():
setattr(spec, schluessel, wert)
_pages(stdscr, summary_lines(plan(proxmox, gast, snapshot, spec)),
"Vorhaben pruefen", "j = anlegen | n = abbrechen")
def szene_vorhaben(stdscr):
"""Der Normalfall: abgeschottet hineinschauen, mit Arbeitsspeicher."""
_vorhaben(stdscr, 101, "auto-stuendlich-20260809-100000",
newid=9103, transfers=["werkzeuge"])
def szene_vorhaben_dongle(stdscr):
"""Die Windows-Maschine mit SPICE und USB - fuer das Schutzmodul."""
_vorhaben(stdscr, 105, "auto-taeglich-20260809-023000",
mode="live", newid=9104, transfers=["werkzeuge"], spice=True, usb=2)
def szene_laufwerkswahl(stdscr):
from pvesnap import transfer_ui
transfer_ui.choose(stdscr, preselected=["werkzeuge"])
SZENEN = {
"config": szene_config,
"recovery": szene_recovery,
"transfer": szene_transfer,
"explorer": szene_explorer,
"explorer-transfer": szene_explorer_transfer,
"gastauswahl": szene_gastauswahl,
"snapshotauswahl": szene_snapshotauswahl,
"neu": szene_neu,
"vorhaben": szene_vorhaben,
"vorhaben-dongle": szene_vorhaben_dongle,
"laufwerkswahl": szene_laufwerkswahl,
}
def main():
if len(sys.argv) < 2 or sys.argv[1] not in SZENEN:
print("Szenen: %s" % ", ".join(sorted(SZENEN)), file=sys.stderr)
return 1
_patch()
from pvesnap.curses_util import init_colors
szene = SZENEN[sys.argv[1]]
def start(stdscr):
curses.curs_set(0)
if hasattr(curses, "set_escdelay"):
curses.set_escdelay(25)
init_colors()
stdscr.keypad(True)
return szene(stdscr)
try:
curses.wrapper(start)
except Exception: # beim Bauen will man den Grund sehen
import traceback
traceback.print_exc()
return 2
return 0
if __name__ == "__main__":
sys.exit(main())
+145
View File
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
"""Alle Bildschirmfotos fuers Handbuch aufnehmen.
./scenes.py alle
./scenes.py explorer nur die, deren Name das enthaelt
"""
from __future__ import annotations
import os
import sys
import time
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
from probe import TASTEN, VENV, befehl, umgebung # noqa: E402
from shot import Terminal # noqa: E402
ZIEL = os.path.join(os.path.dirname(HERE), "docs", "bilder")
BREIT, HOCH = 118, 32
# (Dateiname, Szene, Tasten, Fenstertitel)
AUFNAHMEN = [
# -- Sicherung einrichten: der Konfigurationseditor --------------------
("config-gruppen", "config", [],
"root@pve1 — pvesnap config"),
("config-gruppe", "config", ["ab", "enter"],
"root@pve1 — pvesnap config → Gruppe taeglich"),
("config-vms", "config", ["ab", "enter", "v"],
"root@pve1 — pvesnap config → VMs waehlen"),
("config-global", "config", ["g"],
"root@pve1 — pvesnap config → Globales"),
("config-uebersicht", "config", ["v"],
"root@pve1 — pvesnap config → Uebersicht"),
# -- Dateien holen: der Explorer ---------------------------------------
("explorer-gastauswahl", "gastauswahl", [],
"root@pve1 — pvesnap-explorer"),
("explorer-snapshotauswahl", "snapshotauswahl", [],
"root@pve1 — pvesnap-explorer"),
("explorer-fenster", "explorer", ["ab", "ab", "ab", "enter", "ab", "enter"],
"root@pve1 — pvesnap-explorer 101"),
("explorer-markiert", "explorer",
["ab", "ab", "ab", "enter", "ab", "enter", "ab", "space", "space"],
"root@pve1 — pvesnap-explorer 101"),
("explorer-kopieren", "explorer",
["ab", "ab", "ab", "enter", "ab", "enter", "ab", "space", "f5"],
"root@pve1 — pvesnap-explorer 101"),
("explorer-ansehen", "explorer",
["enter", "ab", "ab", "ab", "ab", "f3"],
"root@pve1 — pvesnap-explorer 101"),
("explorer-hilfe", "explorer", ["?"],
"root@pve1 — pvesnap-explorer 101"),
# -- Wiederherstellen ---------------------------------------------------
("recovery-uebersicht", "recovery", [],
"root@pve1 — pvesnap-recovery"),
("recovery-optionen", "neu", [],
"root@pve1 — pvesnap-recovery → einrichten"),
("recovery-betriebsart", "neu", ["enter"],
"root@pve1 — pvesnap-recovery → einrichten"),
("recovery-vorhaben", "vorhaben", [],
"root@pve1 — pvesnap-recovery → einrichten"),
("recovery-detail", "recovery", ["enter"],
"root@pve1 — pvesnap-recovery"),
("recovery-loesen", "recovery", ["enter", "f"],
"root@pve1 — pvesnap-recovery"),
("recovery-spice", "recovery", ["enter", "p"],
"root@pve1 — pvesnap-recovery"),
("recovery-usb", "recovery", ["enter", "p", "enter"],
"root@pve1 — pvesnap-recovery"),
("recovery-neustart", "recovery",
["enter", "p", "enter", "ab", "ab", "enter"],
"root@pve1 — pvesnap-recovery"),
("recovery-neustartwahl", "recovery",
["enter", "p", "enter", "ab", "ab", "enter", "j"],
"root@pve1 — pvesnap-recovery", 4.0),
("recovery-verwerfen", "recovery", ["x"],
"root@pve1 — pvesnap-recovery"),
# -- Austauschlaufwerke -------------------------------------------------
("transfer-uebersicht", "transfer", [],
"root@pve1 — pvesnap-recovery → Laufwerke"),
("transfer-neu", "transfer", ["n"],
"root@pve1 — pvesnap-recovery → Laufwerke"),
("transfer-commander", "explorer-transfer", [],
"root@pve1 — pvesnap-recovery → dumps"),
("transfer-auswahl", "laufwerkswahl", [],
"root@pve1 — pvesnap-recovery → einrichten"),
# -- Schutzmodul am SPICE-Anschluss -------------------------------------
("dongle-vorhaben", "vorhaben-dongle", [],
"root@pve2 — pvesnap-recovery → einrichten"),
]
def aufnehmen(name, szene, tasten, titel, nachlauf=0.3):
"""`nachlauf` fuer Bildschirme, hinter denen noch etwas arbeitet: der
Nachbau haelt sonst den letzten Stand fest, waehrend im Hintergrund noch
ein Proxmox-Task laeuft und danach der naechste Dialog aufgeht."""
env = umgebung()
env["DOKU"] = HERE
env["PY"] = VENV
term = Terminal(befehl(szene), cols=BREIT, rows=HOCH, env=env, cwd=HERE)
try:
time.sleep(1.3)
term.settle()
term.send([TASTEN.get(t, t) for t in tasten], pause=0.3)
time.sleep(nachlauf)
term.settle()
svg = term.to_svg(titel)
text = "\n".join(l.rstrip() for l in term.screen.display)
finally:
term.close()
os.makedirs(ZIEL, exist_ok=True)
with open(os.path.join(ZIEL, name + ".svg"), "w", encoding="utf-8") as handle:
handle.write(svg)
return text
def main():
filter_ = sys.argv[1] if len(sys.argv) > 1 else ""
fehler = []
for eintrag in AUFNAHMEN:
name = eintrag[0]
if filter_ and filter_ not in name:
continue
text = aufnehmen(*eintrag)
leer = not text.strip()
panne = "Traceback" in text
kennzeichen = "LEER" if leer else ("ABSTURZ" if panne else "ok")
print("%-28s %s" % (name, kennzeichen))
if leer or panne:
fehler.append(name)
print("\n".join(" " + l for l in text.splitlines() if l.strip())[:1500])
if fehler:
print("\nNachsehen: %s" % ", ".join(fehler))
return 1 if fehler else 0
if __name__ == "__main__":
sys.exit(main())
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/env python3
"""Bildschirmfotos aus ncurses-Oberflaechen.
Startet ein Programm in einem echten Pseudo-Terminal fester Groesse, spielt
Tastendruecke ein, laesst pyte den Bildschirm nachbilden und schreibt das
Ergebnis als SVG. Das SVG ist reiner Text - keine Schriftart eingebettet,
dafuer jede Zeichenkette mit `textLength` fest vermessen. Damit sitzt das
Raster in jedem Browser, egal welche Monospace-Schrift er waehlt.
"""
from __future__ import annotations
import fcntl
import os
import pty
import select
import signal
import struct
import subprocess
import sys
import termios
import time
from xml.sax.saxutils import escape
import pyte
CW = 8.6 # Zeichenbreite in px
LH = 18.0 # Zeilenhoehe in px
FS = 14.5 # Schriftgroesse
PAD = 14.0 # Rand um den Textbereich
CHROME = 30.0 # Hoehe der Titelleiste
# Tango - die Palette, die auch Debian im Terminal benutzt. Fett macht aus der
# Grundfarbe die helle Variante; genau das tut ein Terminal mit A_BOLD, und
# genau darauf beruht der Kommentar in curses_util.init_colors().
PALETTE = {
"black": "#2e3436", "red": "#cc0000", "green": "#4e9a06",
"brown": "#c4a000", "yellow": "#c4a000", "blue": "#3465a4",
"magenta": "#75507b", "cyan": "#06989a", "white": "#d3d7cf",
"brightblack": "#555753", "brightred": "#ef2929", "brightgreen": "#8ae234",
"brightbrown": "#fce94f", "brightyellow": "#fce94f", "brightblue": "#729fcf",
"brightmagenta": "#ad7fa8", "brightcyan": "#34e2e2", "brightwhite": "#eeeeec",
}
BG_DEFAULT = "#1b1e24"
FG_DEFAULT = "#d3d7cf"
BRIGHT = {"black": "brightblack", "red": "brightred", "green": "brightgreen",
"brown": "brightbrown", "yellow": "brightyellow", "blue": "brightblue",
"magenta": "brightmagenta", "cyan": "brightcyan", "white": "brightwhite"}
def _color(name, bold, default):
if name == "default":
# Fett faerbt den Vordergrund nicht um, wenn er gar keine Farbe hat.
return default
if bold:
name = BRIGHT.get(name, name)
if name in PALETTE:
return PALETTE[name]
if len(name) == 6:
try:
int(name, 16)
return "#" + name
except ValueError:
pass
return default
class Terminal:
"""Ein Programm in einem Pseudo-Terminal, dessen Bildschirm mitgelesen wird."""
def __init__(self, command, cols=100, rows=30, env=None, cwd=None):
self.cols, self.rows = cols, rows
self.screen = pyte.Screen(cols, rows)
self.stream = pyte.ByteStream(self.screen)
self.master, slave = pty.openpty()
fcntl.ioctl(slave, termios.TIOCSWINSZ,
struct.pack("HHHH", rows, cols, cols * 8, rows * 16))
environment = dict(os.environ)
environment.update({"TERM": "xterm-256color", "LINES": str(rows),
"COLUMNS": str(cols), "LANG": "de_DE.UTF-8",
"LC_ALL": "de_DE.UTF-8"})
environment.update(env or {})
self.process = subprocess.Popen(
command, stdin=slave, stdout=slave, stderr=slave, cwd=cwd,
env=environment, close_fds=True, start_new_session=True)
os.close(slave)
# -- lesen -------------------------------------------------------------
def _drain(self, timeout):
"""Alles lesen, was bis `timeout` kommt. True, wenn etwas kam."""
got = False
deadline = time.monotonic() + timeout
while True:
rest = deadline - time.monotonic()
if rest <= 0:
return got
ready, _, _ = select.select([self.master], [], [], rest)
if not ready:
return got
try:
data = os.read(self.master, 65536)
except OSError:
return got
if not data:
return got
self.stream.feed(data)
got = True
def settle(self, quiet=0.35, limit=8.0):
"""Warten, bis eine Weile nichts mehr nachkommt."""
deadline = time.monotonic() + limit
while time.monotonic() < deadline:
if not self._drain(quiet):
return
self._drain(0.1)
# -- schreiben ---------------------------------------------------------
def send(self, keys, pause=0.25):
for key in keys if isinstance(keys, (list, tuple)) else [keys]:
os.write(self.master, key.encode("utf-8"))
time.sleep(pause)
self.settle()
def close(self):
try:
self.process.send_signal(signal.SIGKILL)
self.process.wait(timeout=5)
except (ProcessLookupError, subprocess.TimeoutExpired, OSError):
pass
try:
os.close(self.master)
except OSError:
pass
# -- ausgeben ----------------------------------------------------------
def to_svg(self, title=""):
return render(self.screen, self.cols, self.rows, title)
def _runs(screen, y, cols):
"""Eine Zeile in Abschnitte gleicher Darstellung zerlegen."""
line = screen.buffer[y]
out, current = [], None
for x in range(cols):
char = line[x]
style = (char.fg, char.bg, char.bold, char.reverse)
if char.reverse:
style = (char.bg, char.fg, char.bold, False)
if current and current[0] == style:
current[1].append(char.data or " ")
else:
current = (style, [char.data or " "], x)
out.append(current)
return out
def render(screen, cols, rows, title=""):
width = cols * CW + 2 * PAD
height = rows * LH + 2 * PAD + (CHROME if title else 0)
top = PAD + (CHROME if title else 0)
parts = [
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %.1f %.1f" '
'width="%.0f" height="%.0f" font-family="ui-monospace, SFMono-Regular, '
'Menlo, Consolas, \'DejaVu Sans Mono\', monospace" font-size="%.1f">'
% (width, height, width, height, FS),
'<rect width="%.1f" height="%.1f" rx="7" fill="%s"/>' % (width, height, BG_DEFAULT),
]
if title:
parts.append('<path d="M0 7a7 7 0 0 1 7-7h%.1fa7 7 0 0 1 7 7v%.1fH0z" '
'fill="#2b2f38"/>' % (width - 14, CHROME - 7))
for index, colour in enumerate(("#ed6a5e", "#f4bf4f", "#61c554")):
parts.append('<circle cx="%.1f" cy="15" r="5" fill="%s"/>'
% (16 + index * 17, colour))
parts.append('<text x="%.1f" y="20" fill="#9aa3b2" font-size="12" '
'text-anchor="middle">%s</text>'
% (width / 2, escape(title)))
for y in range(rows):
for style, chars, x in _runs(screen, y, cols):
fg_name, bg_name, bold, _ = style
text = "".join(chars)
run_width = len(chars) * CW
bg = _color(bg_name, False, None)
if bg and bg != BG_DEFAULT:
parts.append('<rect x="%.1f" y="%.1f" width="%.1f" height="%.1f" '
'fill="%s"/>' % (PAD + x * CW, top + y * LH,
run_width + 0.4, LH + 0.4, bg))
if not text.strip():
continue
fg = _color(fg_name, bold, FG_DEFAULT)
weight = ' font-weight="bold"' if bold else ""
parts.append('<text x="%.1f" y="%.1f" fill="%s"%s textLength="%.1f" '
'lengthAdjust="spacing" xml:space="preserve">%s</text>'
% (PAD + x * CW, top + y * LH + FS * 0.92, fg, weight,
run_width, escape(text)))
parts.append("</svg>")
return "\n".join(parts)
def capture(target, command, keys=(), cols=100, rows=30, title="",
env=None, cwd=None, pause=0.3, warmup=1.2):
"""Ein Programm starten, Tasten schicken, Bildschirm als SVG ablegen."""
term = Terminal(command, cols=cols, rows=rows, env=env, cwd=cwd)
try:
time.sleep(warmup)
term.settle()
term.send(list(keys), pause=pause)
time.sleep(0.2)
term.settle()
svg = term.to_svg(title)
finally:
term.close()
os.makedirs(os.path.dirname(target), exist_ok=True)
with open(target, "w", encoding="utf-8") as handle:
handle.write(svg)
return svg
def text_of(svg_screen):
"""Nur zum Nachsehen beim Bauen: der Bildschirm als reiner Text."""
return "\n".join(svg_screen.display)
if __name__ == "__main__":
print("Wird von scenes.py benutzt.", file=sys.stderr)
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
# Die Kulisse an die richtigen Stellen haengen.
#
# Der Explorer zeigt Pfade an. Im Handbuch sollen das die Pfade sein, die auf
# einem echten Host stehen - /run/pvesnap/mnt/... und /root -, nicht die
# Arbeitsverzeichnisse dieser Werkstatt. Also laeuft die Aufnahme in einem
# eigenen Mount-Namensraum (unshare -r -m), in dem wir uns die Pfade
# hinbiegen duerfen, ohne irgendetwas am System zu veraendern.
set -e
D="$DOKU/demo"
mount -t tmpfs none /run
mkdir -p /run/pvesnap/mnt/101-auto-taeglich-20260809-023000
mkdir -p /run/pvesnap/transfer/dumps
mount --bind "$D/snapshot" /run/pvesnap/mnt/101-auto-taeglich-20260809-023000
mount --bind "$D/transfer-dumps" /run/pvesnap/transfer/dumps
mount --bind "$D/lokal" /root
# /etc bleibt vollstaendig, bekommt aber eine Schreibschicht obendrauf - damit
# die Konfiguration dort liegt, wo sie auf dem Host liegt.
mkdir -p /run/overlay/oben /run/overlay/arbeit
mount -t overlay overlay -o lowerdir=/etc,upperdir=/run/overlay/oben,workdir=/run/overlay/arbeit /etc
cp "$D/pvesnap.conf" /etc/pvesnap.conf
cd /root
exec "$PY" "$DOKU/run.py" "$@"
+30 -2
View File
@@ -211,6 +211,22 @@ if [ -f "${SRC}/README.md" ]; then
install -m 0644 "${SRC}/README.md" "${DOC_DIR}/README.md" install -m 0644 "${SRC}/README.md" "${DOC_DIR}/README.md"
fi fi
# --- Handbuch ---------------------------------------------------------
# Nur kopieren, wenn es gebaut vorliegt (handbuch/bauen.sh). Auf dem Host
# selbst wird nichts gebaut - mkdocs gehoert nicht auf einen Hypervisor.
# Das gebaute Handbuch ist reines HTML samt Suche und braucht kein Netz.
if [ -d "${SRC}/handbuch/site" ]; then
info "Installiere Handbuch nach ${DOC_DIR}/handbuch"
rm -rf "${DOC_DIR}/handbuch"
install -d -m 0755 "${DOC_DIR}/handbuch"
cp -r "${SRC}/handbuch/site/." "${DOC_DIR}/handbuch/"
find "${DOC_DIR}/handbuch" -type d -exec chmod 0755 {} +
find "${DOC_DIR}/handbuch" -type f -exec chmod 0644 {} +
HANDBUCH=1
elif [ -d "${SRC}/handbuch" ]; then
info "Handbuch liegt nur als Quelle vor - bauen mit: handbuch/bauen.sh"
fi
# --- systemd ---------------------------------------------------------- # --- systemd ----------------------------------------------------------
info "Installiere systemd-Unit ${UNIT}" info "Installiere systemd-Unit ${UNIT}"
install -m 0644 "${SRC}/systemd/pvesnap.service" "${UNIT}" install -m 0644 "${SRC}/systemd/pvesnap.service" "${UNIT}"
@@ -288,10 +304,22 @@ Fertig.
Einen Snapshot als Maschine starten: Einen Snapshot als Maschine starten:
pvesnap-recovery Uebersicht und Assistent pvesnap-recovery Uebersicht und Assistent
pvesnap-recovery live 100 --exchange ja pvesnap-recovery live 100 --transfer dumps
abgeschottet ohne Netz, mit Austauschverzeichnis abgeschottet ohne Netz, mit Austauschlaufwerk
EOF EOF
if [ "${HANDBUCH:-0}" -eq 1 ]; then
cat <<EOF
Handbuch (funktioniert ohne Netz, mit Volltextsuche):
${DOC_DIR}/handbuch/index.html
Im Browser oeffnen, oder vom Arbeitsplatz aus holen:
scp -r root@\$(hostname -f):${DOC_DIR}/handbuch .
EOF
fi
if [ "$WEB" -eq 1 ]; then if [ "$WEB" -eq 1 ]; then
cat <<EOF cat <<EOF