Sandbox-Optionen aus der systemd-Unit entfernen

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>
This commit is contained in:
duffyduck
2026-07-31 02:16:12 +02:00
co-authored by Claude Opus 5
parent e26d966a8b
commit 1be13ad294
5 changed files with 126 additions and 12 deletions
+8
View File
@@ -13,6 +13,7 @@ from .config import DEFAULT_CONFIG_PATH, ConfigError, load_config
from .daemon import Daemon, SingleInstanceLock
from .engine import run_group, select_guests
from .naming import parse_name
from .preflight import check_environment
from .proxmox import Proxmox, ProxmoxError, pvesh_available
from .schedule import describe, next_due
from .state import State
@@ -331,6 +332,13 @@ def cmd_check(args):
for problem in problems:
print(" - %s" % problem, file=sys.stderr)
return 1
environment = check_environment(require_pve=pvesh_available())
if environment:
print("Hinweise zur Umgebung:", file=sys.stderr)
for problem in environment:
print(" - %s" % problem, file=sys.stderr)
print("", file=sys.stderr)
print("Konfiguration in Ordnung: %d Gruppe(n)." % len(config.groups))
for group in config.groups:
print(" - %s: %s, behalte %s / %s%s"
+5
View File
@@ -13,6 +13,7 @@ from datetime import datetime
from .config import ConfigError, load_config
from .engine import run_group
from .preflight import warn_about_environment
from .proxmox import Proxmox, ProxmoxError
from .schedule import next_due
from .state import State
@@ -137,6 +138,10 @@ class Daemon:
len(self.config.groups), globals_.prefix,
", TESTLAUF" if globals_.dry_run else "")
# Der Dienst laeuft trotzdem weiter - aber es soll gleich beim Start im
# Journal stehen, warum spaeter jeder Snapshot scheitert.
warn_about_environment(log)
daemon_lock = SingleInstanceLock(
globals_.lock_file + ".daemon",
busy_message="Der pvesnap-Dienst laeuft bereits (%s.daemon)" % globals_.lock_file)
+73
View File
@@ -0,0 +1,73 @@
"""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