Benannte, dauerhafte Austauschmedien zwischen Host und Gast - Abbilddateien
unter /var/lib/pvesnap/transfer, die zu keinem Gast gehoeren und jede
Wiederherstellung ueberleben.
Zwei Entwurfsentscheidungen, beide am Host geprueft:
Datei statt Proxmox-Volume. Ein Volume wuerde beim Entfernen der Maschine
mitgeloescht, auch aus der Weboberflaeche heraus - die vorbereitete
Werkzeugsammlung waere weg. Pfade ueberspringt destroy_vm ausdruecklich
("return if $volid =~ m|^/|"). Nachgestellt: VM mit --purge entfernt, das
Abbild lebte samt Inhalt weiter.
Loop-Geraet statt direktem Pfad. Eine Abbilddatei als scsi3 einzutragen
lehnt Proxmox ab ("unable to associate path to any storage"), /dev/... wird
dagegen durchgereicht. Ueber losetup -P bekommt der Gast eine Platte mit
Partitionstabelle - ohne die zeigt Windows nur "nicht initialisiert".
Verriegelung, am laufenden System geprueft: ein Laufwerk ist entweder am Host
eingehaengt oder an einem Gast, nie beides. Alle drei Wege wurden abgelehnt -
anhaengen waehrend Host-Mount, Host-Mount waehrend angehaengt, loeschen
waehrend in Benutzung. Der Gast-Zustand wird aus den Konfigurationsdateien
gelesen, gilt also auch fuer gestoppte Maschinen.
Installer: parted und exfatprogs werden bei Bedarf nachinstalliert
(--no-install-deps schaltet das ab). ntfs-3g bewusst nicht - apt entfernt
dafuer 'fuse', an dem pve-cluster (/etc/pve) und ceph-fuse haengen. Dazu gibt
es nur einen Hinweis samt Begruendung; fuer Windows ist exFAT ohnehin die
einfachere Wahl.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
309 lines
11 KiB
Bash
Executable File
309 lines
11 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=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
|
|
if ss -ltn 2>/dev/null | grep -qE "[:.]${WEB_PORT}[[:space:]]"; then
|
|
warn "Auf Port ${WEB_PORT} lauscht bereits etwas - der Dienst wird sich "
|
|
warn "moeglicherweise nicht starten lassen."
|
|
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
|
|
|
|
# NTFS bewusst NICHT automatisch: apt entfernt dafuer 'fuse', und daran
|
|
# haengen pve-cluster (/etc/pve) und ceph-fuse. Auf einem laufenden Node
|
|
# ist das kein Schritt, den ein Installer ungefragt gehen darf.
|
|
if ! have_tool mkfs.ntfs; then
|
|
cat <<'EOF'
|
|
|
|
Hinweis: NTFS-Transfer-Laufwerke lassen sich hier nicht anlegen, es fehlt
|
|
mkfs.ntfs aus dem Paket ntfs-3g. Die Installation wuerde 'fuse' entfernen,
|
|
an dem pve-cluster (/etc/pve) und ceph-fuse haengen - deshalb macht dieses
|
|
Skript das nicht von selbst.
|
|
|
|
Fuer Windows-Gaeste ist exFAT die einfachere Wahl (bereits eingerichtet).
|
|
Wer wirklich NTFS braucht: apt install ntfs-3g - vorher Cluster pruefen.
|
|
|
|
Lesen kann der Host NTFS ohnehin, ueber den ntfs3-Treiber im Kernel.
|
|
EOF
|
|
fi
|
|
fi
|
|
|
|
# --- 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
|
|
|
|
# --- 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 --exchange ja
|
|
abgeschottet ohne Netz, mit Austauschverzeichnis
|
|
EOF
|
|
|
|
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
|