# 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 |