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>
346 lines
13 KiB
Bash
Executable File
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
|