Files
proxmox-snapshot-service/install.sh
T
duffyduckandClaude Opus 5 59e7224297 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>
2026-08-09 12:39:30 +02:00

346 lines
13 KiB
Bash
Executable File

#!/bin/bash
# ---------------------------------------------------------------------
# pvesnap - Installation
#
# Aufruf: ./install.sh [Optionen]
#
# --with-webexplorer Web-Oberflaeche zusaetzlich als Dienst
# einrichten (pvesnap-web.service).
# Erfordert --port.
# --port <nummer> Port fuer die Web-Oberflaeche, z.B. 8823
# --bind <adresse> Adresse, auf der sie lauscht
# (Vorgabe: 0.0.0.0 = alle)
# --no-start installieren, aber Dienste nicht starten
# --no-install-deps fehlende Werkzeuge (parted, exfatprogs) nicht
# nachinstallieren
# --force auch ohne erkanntes Proxmox VE installieren
# -h, --help diese Hilfe
#
# Beispiele:
# ./install.sh
# ./install.sh --with-webexplorer --port 8823
# ./install.sh --with-webexplorer --port 8443 --bind 10.0.0.5
#
# Die Web-Oberflaeche meldet Benutzer gegen Proxmox VE an - dieselben
# Zugangsdaten und Realms wie im Proxmox-Webinterface.
# ---------------------------------------------------------------------
set -euo pipefail
LIB_DIR="/usr/lib/pvesnap"
BIN="/usr/local/bin/pvesnap"
CONF_DIR="/etc/pvesnap"
CONF="${CONF_DIR}/pvesnap.conf"
STATE_DIR="/var/lib/pvesnap"
DOC_DIR="/usr/share/doc/pvesnap"
UNIT="/etc/systemd/system/pvesnap.service"
WEB_UNIT="/etc/systemd/system/pvesnap-web.service"
WEB_CONF="${CONF_DIR}/web.conf"
SRC="$(cd "$(dirname "$0")" && pwd)"
START=1
FORCE=0
DEPS=1
WEB_RUNNING=0
WEB=0
WEB_PORT=""
WEB_BIND="0.0.0.0"
while [ $# -gt 0 ]; do
case "$1" in
--with-webexplorer) WEB=1 ;;
--port) WEB_PORT="${2:-}"; shift ;;
--port=*) WEB_PORT="${1#*=}" ;;
--bind) WEB_BIND="${2:-}"; shift ;;
--bind=*) WEB_BIND="${1#*=}" ;;
--no-start) START=0 ;;
--no-install-deps) DEPS=0 ;;
--force) FORCE=1 ;;
-h|--help) sed -n '2,27p' "$0" | sed 's/^#[[:space:]]\{0,1\}//'; exit 0 ;;
*) echo "Unbekannte Option: $1" >&2
echo "Hilfe mit: $0 --help" >&2; exit 1 ;;
esac
shift
done
info() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m==>\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31m==>\033[0m %s\n' "$*" >&2; exit 1; }
# --- Web-Oberflaeche: Angaben pruefen ---------------------------------
if [ "$WEB" -eq 1 ]; then
if [ -z "$WEB_PORT" ]; then
fail "--with-webexplorer benoetigt einen Port.
Beispiel: ./install.sh --with-webexplorer --port 8823
Hilfe mit: $0 --help"
fi
case "$WEB_PORT" in
''|*[!0-9]*) fail "--port erwartet eine Zahl, bekommen: '$WEB_PORT'" ;;
esac
if [ "$WEB_PORT" -lt 1 ] || [ "$WEB_PORT" -gt 65535 ]; then
fail "--port muss zwischen 1 und 65535 liegen (bekommen: $WEB_PORT)"
fi
if [ "$WEB_PORT" -eq 8006 ]; then
fail "Port 8006 gehoert dem Proxmox-Webinterface - bitte einen anderen waehlen."
fi
# Laeuft unsere eigene Web-Oberflaeche schon, gehoert ihr der Port auch -
# das ist kein Konflikt, sondern der Normalfall bei einer Aktualisierung.
# Sie wird weiter unten gestoppt, bevor der Code ausgetauscht wird.
if systemctl is-active --quiet pvesnap-web.service 2>/dev/null; then
WEB_RUNNING=1
elif ss -ltn 2>/dev/null | grep -qE "[:.]${WEB_PORT}[[:space:]]"; then
warn "Auf Port ${WEB_PORT} lauscht bereits etwas anderes - der Dienst"
warn "wird sich moeglicherweise nicht starten lassen."
ss -ltnp 2>/dev/null | grep -E "[:.]${WEB_PORT}[[:space:]]" | sed 's/^/ /' >&2 || true
fi
[ -n "$WEB_BIND" ] || fail "--bind erwartet eine Adresse"
elif [ -n "$WEB_PORT" ]; then
fail "--port wirkt nur zusammen mit --with-webexplorer."
fi
[ "$(id -u)" -eq 0 ] || fail "Bitte als root ausfuehren (sudo ./install.sh)."
command -v python3 >/dev/null 2>&1 || fail "python3 wird benoetigt."
python3 - <<'PY' || fail "Python 3.7 oder neuer wird benoetigt."
import sys
sys.exit(0 if sys.version_info >= (3, 7) else 1)
PY
if ! command -v pvesh >/dev/null 2>&1; then
if [ "$FORCE" -eq 1 ]; then
warn "pvesh nicht gefunden - installiere trotzdem (--force)."
START=0
else
fail "pvesh nicht gefunden. pvesnap gehoert auf einen Proxmox-VE-Host.
Mit --force laesst sich die Installation trotzdem erzwingen."
fi
fi
command -v systemctl >/dev/null 2>&1 || fail "systemd wird benoetigt."
[ -d "${SRC}/pvesnap" ] || fail "Verzeichnis 'pvesnap' nicht gefunden (Aufruf aus dem Projektordner?)."
# --- Werkzeuge fuer Transfer-Laufwerke -------------------------------------
# Transfer-Laufwerke sind Abbilddateien, die auf dem Host formatiert und
# eingehaengt werden. Dafuer braucht es eine Partitionstabelle (parted) und
# einen Formatierer je Dateisystem.
have_tool() {
command -v "$1" >/dev/null 2>&1 && return 0
for d in /usr/local/sbin /usr/sbin /sbin; do
[ -x "$d/$1" ] && return 0
done
return 1
}
if [ "$DEPS" -eq 1 ]; then
MISSING=""
have_tool parted || MISSING="${MISSING} parted"
have_tool mkfs.exfat || MISSING="${MISSING} exfatprogs"
if [ -n "$MISSING" ]; then
info "Fehlende Werkzeuge fuer Transfer-Laufwerke:${MISSING}"
if command -v apt-get >/dev/null 2>&1; then
info "Installiere:${MISSING}"
# shellcheck disable=SC2086
if DEBIAN_FRONTEND=noninteractive apt-get install -y ${MISSING}; then
info "Werkzeuge installiert"
else
warn "Installation fehlgeschlagen - Transfer-Laufwerke lassen sich"
warn "dann nur mit den vorhandenen Dateisystemen anlegen."
fi
else
warn "apt-get nicht gefunden - bitte von Hand:${MISSING}"
fi
fi
fi
# --- Laufendes anhalten -----------------------------------------------
# Der Code wird gleich ersetzt. Die Web-Oberflaeche kann dabei noch Snapshots
# eingebunden haben - sie bekommt beim Stoppen ihr SIGTERM und haengt sauber
# aus. Wuerde sie einfach weiterlaufen, blieben rbd-Zuordnungen und
# Mountpunkte einer Fassung stehen, die es nicht mehr gibt.
if [ "$WEB_RUNNING" -eq 1 ]; then
info "Web-Oberflaeche laeuft - halte sie fuer die Aktualisierung an"
if systemctl stop pvesnap-web.service; then
info " angehalten, Port ${WEB_PORT} ist frei"
else
warn " liess sich nicht anhalten - bitte pruefen:"
warn " systemctl status pvesnap-web"
fi
fi
# Bewusst kein "pvesnap-explorer --cleanup" an dieser Stelle: das wuerde auch
# die Einbindungen einer Sitzung loesen, die jemand gerade in einem anderen
# Terminal offen hat. Wer nach einem Absturz aufraeumen will, ruft es selbst.
# --- Programm ---------------------------------------------------------
info "Installiere Programm nach ${LIB_DIR}"
rm -rf "${LIB_DIR}/pvesnap"
install -d -m 0755 "${LIB_DIR}"
cp -r "${SRC}/pvesnap" "${LIB_DIR}/pvesnap"
find "${LIB_DIR}/pvesnap" -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null || true
chmod -R go-w "${LIB_DIR}/pvesnap"
info "Installiere Startbefehle"
install_command() { # $1 = Befehlsname, $2 = Python-Modul
cat > "/usr/local/bin/$1" <<EOF
#!/bin/sh
# von install.sh erzeugt
PYTHONPATH="${LIB_DIR}\${PYTHONPATH:+:\$PYTHONPATH}" exec /usr/bin/python3 -m $2 "\$@"
EOF
chmod 0755 "/usr/local/bin/$1"
info " /usr/local/bin/$1"
}
install_command pvesnap pvesnap
install_command pvesnap-explorer pvesnap.explorer
install_command pvesnap-web pvesnap.web
install_command pvesnap-recovery pvesnap.recovery_ui
# --- Konfiguration ----------------------------------------------------
install -d -m 0755 "${CONF_DIR}" "${STATE_DIR}" "${DOC_DIR}"
if [ -f "${CONF}" ]; then
info "Vorhandene Konfiguration bleibt unveraendert: ${CONF}"
install -m 0644 "${SRC}/config/pvesnap.conf.example" "${CONF_DIR}/pvesnap.conf.example"
else
info "Lege Beispielkonfiguration an: ${CONF}"
install -m 0640 "${SRC}/config/pvesnap.conf.example" "${CONF}"
install -m 0644 "${SRC}/config/pvesnap.conf.example" "${CONF_DIR}/pvesnap.conf.example"
NEW_CONFIG=1
fi
if [ -f "${SRC}/README.md" ]; then
install -m 0644 "${SRC}/README.md" "${DOC_DIR}/README.md"
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 ----------------------------------------------------------
info "Installiere systemd-Unit ${UNIT}"
install -m 0644 "${SRC}/systemd/pvesnap.service" "${UNIT}"
systemctl daemon-reload
info "Pruefe Konfiguration"
if ! "${BIN}" --config "${CONF}" check; then
warn "Die Konfiguration ist noch nicht vollstaendig - Dienst wird nicht gestartet."
START=0
fi
if systemctl enable pvesnap.service >/dev/null 2>&1; then
info "Dienst beim Systemstart aktiviert"
else
warn "Dienst konnte nicht aktiviert werden - bitte 'systemctl enable pvesnap' pruefen."
fi
# --- Web-Oberflaeche als Dienst ---------------------------------------
if [ "$WEB" -eq 1 ]; then
info "Richte Web-Oberflaeche als Dienst ein (Port ${WEB_PORT})"
cat > "${WEB_CONF}" <<EOF
# Aufrufparameter von pvesnap-web.service
# Nach Aenderungen: systemctl restart pvesnap-web
#
# --auth pve Anmeldung mit den Benutzern von Proxmox VE (empfohlen)
# --auth token Zugangsschluessel in der Adresse
# --auth none ohne jede Anmeldung - nur in vertrauenswuerdigen Netzen
#
# Sichtbar ist ein Gast nur fuer Benutzer, die darauf das Recht aus
# --require-privilege besitzen (Vorgabe: VM.Snapshot). Mit --allow-user
# lassen sich einzelne Benutzer generell zulassen.
PVESNAP_WEB_ARGS=--bind ${WEB_BIND} --port ${WEB_PORT} --auth pve
EOF
chmod 0640 "${WEB_CONF}"
install -m 0644 "${SRC}/systemd/pvesnap-web.service" "${WEB_UNIT}"
systemctl daemon-reload
if systemctl enable pvesnap-web.service >/dev/null 2>&1; then
info "Web-Oberflaeche beim Systemstart aktiviert"
else
warn "pvesnap-web konnte nicht aktiviert werden."
fi
fi
if [ "$START" -eq 1 ]; then
info "Starte Dienst"
systemctl restart pvesnap.service
sleep 1
systemctl --no-pager --lines=5 status pvesnap.service || true
if [ "$WEB" -eq 1 ]; then
info "Starte Web-Oberflaeche"
systemctl restart pvesnap-web.service
sleep 1
systemctl --no-pager --lines=3 status pvesnap-web.service || true
fi
else
warn "Dienste wurden nicht gestartet."
fi
cat <<EOF
Fertig.
Konfiguration bearbeiten : pvesnap config (ncurses-Editor)
nano ${CONF}
Konfiguration pruefen : pvesnap check
Uebersicht : pvesnap status
VMs und Gruppen : pvesnap vms
Testlauf ohne Aenderung : pvesnap run --force --dry-run
Nach Aenderungen : systemctl reload pvesnap
Protokoll : journalctl -u pvesnap -f
Dateien aus einem Snapshot holen:
pvesnap-explorer zwei Fenster wie im Midnight Commander
pvesnap-web im Browser durchsehen und herunterladen
Einen Snapshot als Maschine starten:
pvesnap-recovery Uebersicht und Assistent
pvesnap-recovery live 100 --transfer dumps
abgeschottet ohne Netz, mit Austauschlaufwerk
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
cat <<EOF
Web-Oberflaeche laeuft als Dienst:
Adresse : http://<IP-dieses-Hosts>:${WEB_PORT}/
Anmeldung : Benutzer von Proxmox VE (gleiche Zugangsdaten wie dort)
Einstellungen : ${WEB_CONF}
Status : systemctl status pvesnap-web
Protokoll : journalctl -u pvesnap-web -f
Sichtbar ist ein Gast nur fuer Benutzer mit dem Recht VM.Snapshot darauf;
root@pam sieht alles. Anzupassen in ${WEB_CONF}.
EOF
fi
if [ "${NEW_CONFIG:-0}" -eq 1 ]; then
cat <<EOF
Hinweis: In der Beispielkonfiguration sind alle Gruppen mit "enabled = no"
angelegt. Erst nach dem Aktivieren einer Gruppe werden Snapshots erstellt.
EOF
fi