Als Dienst schlug jeder Snapshot mit "cfs-lock 'storage-data' error: got lock request timeout" fehl, waehrend derselbe Aufruf in der Shell funktionierte. Ursache war ProtectSystem=full in unserer eigenen Unit: das haengt /etc im Namespace des Dienstes schreibgeschuetzt ein. pvesh fuehrt die Proxmox-API im eigenen Prozess aus, der Snapshot-Task ist also ein Kindprozess von pvesnap und erbt diese Einschraenkung. pmxcfs legt seine Sperren aber 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. ProtectHome=yes war aus demselben Grund schaedlich: es blendet /root aus, womit die SSH-Schluessel fuer die uebrigen Cluster-Nodes fehlen. * Unit enthaelt keine Sandbox-Optionen mehr, mit Kommentar, warum nicht. * Neu: pvesnap/preflight.py prueft root-Rechte, Schreibzugriff auf /etc/pve und Sichtbarkeit von /root. Der Dienst schreibt das beim Start ins Journal, "pvesnap check" zeigt es ebenfalls an - damit faellt so etwas sofort auf, statt sich als Lock-Timeout zu tarnen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
"""Prueft, ob pvesnap ueberhaupt arbeiten kann.
|
|
|
|
Hintergrund: pvesh fuehrt die Proxmox-API im eigenen Prozess aus. Der
|
|
Snapshot-Task ist damit ein Kindprozess von pvesnap und erbt dessen Umgebung -
|
|
also auch Einschraenkungen aus der systemd-Unit. Eine schreibgeschuetzte /etc
|
|
faellt dabei nicht als Rechtefehler auf, sondern als
|
|
"cfs-lock 'storage-XXX' error: got lock request timeout", weil pmxcfs seine
|
|
Sperren als Verzeichnisse unter /etc/pve/priv/lock/ anlegt.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from .proxmox import pvesh_available
|
|
|
|
PMXCFS = "/etc/pve"
|
|
PMXCFS_LOCKDIR = "/etc/pve/priv/lock"
|
|
|
|
|
|
def _read_only(path):
|
|
"""True, wenn `path` auf einem schreibgeschuetzt eingehaengten Dateisystem liegt."""
|
|
try:
|
|
stats = os.statvfs(path)
|
|
except OSError:
|
|
return False
|
|
return bool(stats.f_flag & getattr(os, "ST_RDONLY", 1))
|
|
|
|
|
|
def check_environment(require_pve=True):
|
|
"""Liste von Problemen; leer heisst: alles in Ordnung."""
|
|
problems = []
|
|
|
|
if os.geteuid() != 0:
|
|
problems.append("pvesnap muss als root laufen (pvesh benoetigt root-Rechte).")
|
|
|
|
if require_pve and not pvesh_available():
|
|
problems.append("'pvesh' nicht gefunden - laeuft pvesnap auf einem "
|
|
"Proxmox-VE-Host?")
|
|
|
|
if not os.path.isdir(PMXCFS):
|
|
if require_pve:
|
|
problems.append("%s nicht vorhanden - laeuft pve-cluster?" % PMXCFS)
|
|
return problems
|
|
|
|
if _read_only(PMXCFS) or not os.access(PMXCFS, os.W_OK):
|
|
problems.append(
|
|
"%s ist nicht beschreibbar. Snapshots scheitern dann mit "
|
|
"\"cfs-lock 'storage-...' error: got lock request timeout\", weil "
|
|
"Proxmox seine Sperren unter %s anlegt. Ursache ist meist eine "
|
|
"Sandbox-Option in der systemd-Unit (ProtectSystem=, ReadOnlyPaths=) "
|
|
"- pruefen mit: systemctl show pvesnap -p ProtectSystem -p ProtectHome"
|
|
% (PMXCFS, PMXCFS_LOCKDIR))
|
|
|
|
# ProtectHome=yes ersetzt /root durch ein leeres, unlesbares Verzeichnis.
|
|
try:
|
|
empty_root = os.path.isdir("/root") and not os.listdir("/root")
|
|
except OSError:
|
|
empty_root = True
|
|
if empty_root:
|
|
problems.append("/root ist leer oder nicht lesbar (ProtectHome= in der "
|
|
"systemd-Unit?). Proxmox erreicht andere Nodes im Cluster "
|
|
"ueber die SSH-Schluessel in /root/.ssh.")
|
|
|
|
return problems
|
|
|
|
|
|
def warn_about_environment(log, require_pve=True):
|
|
"""Probleme protokollieren; gibt True zurueck, wenn alles passt."""
|
|
problems = check_environment(require_pve=require_pve)
|
|
for problem in problems:
|
|
log.error("Umgebung: %s", problem)
|
|
return not problems
|