From 8577b23b0c6c1529fd810c19b61835df61ddbb54 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 31 Jul 2026 10:34:14 +0200 Subject: [PATCH] Fehlgeschlagene Container-Snapshots wurden als Erfolg gemeldet Bei Containern schreibt pvesh den Fortschritt vor die UPID, alles in eine Zeile: Creating snap: 100% complete...done."UPID:node:...:vzsnapshot:100:..." Die UPID wurde zeilenweise mit startswith() gesucht und deshalb nicht gefunden. Ohne UPID liess sich der Task nicht verfolgen - ein Fehlschlag blieb damit unbemerkt und pvesnap protokollierte "Snapshot angelegt", obwohl Proxmox nur einen halbfertigen Eintrag (snapstate "prepare") in der Konfiguration hinterlassen hatte. Genau so sind die beiden Leichen bei LXC 100 entstanden. * Die UPID wird jetzt per Suchmuster aus der gesamten Ausgabe geholt, egal woran sie haengt. * Zusaetzlich wird nach dem Anlegen nachgesehen, ob der Snapshot wirklich da und vollstaendig ist. Damit faellt ein stiller Fehlschlag auch dann auf, wenn weder Rueckgabewert noch Task ihn melden. Auf pvetest01 mit Container 100 geprueft: anlegen (Task wird verfolgt, Eintrag vollstaendig, rbd-Snapshot vorhanden), einbinden (rootfs als ext4, /etc/hostname liefert "unifi"), Explorer und Web-Oberflaeche inklusive ZIP, sowie loeschen ueber die Vorhaltezahl. Co-Authored-By: Claude Opus 5 (1M context) --- pvesnap/proxmox.py | 48 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/pvesnap/proxmox.py b/pvesnap/proxmox.py index 1bb4060..14300bd 100644 --- a/pvesnap/proxmox.py +++ b/pvesnap/proxmox.py @@ -8,6 +8,7 @@ from __future__ import annotations import json import logging +import re import shutil import subprocess import time @@ -17,6 +18,9 @@ log = logging.getLogger("pvesnap.proxmox") PVESH = "/usr/bin/pvesh" +# UPID:node:PID:PSTART:STARTTIME:TYPE:ID:USER: +_UPID_PATTERN = re.compile(r"UPID:[^\s\"']+") + class ProxmoxError(Exception): """Ein pvesh-Aufruf ist fehlgeschlagen.""" @@ -184,7 +188,12 @@ class Proxmox: except ProxmoxError: return False - return self._retry(what, lambda: self._task(guest, args, what), skip_retry=exists) + def attempt(): + result = self._task(guest, args, what) + self._verify_created(guest, name) + return result + + return self._retry(what, attempt, skip_retry=exists) def delete_snapshot(self, guest, name, force=False): if self.dry_run: @@ -319,8 +328,35 @@ class Proxmox: @staticmethod def _extract_upid(output): - for line in (output or "").splitlines(): - candidate = line.strip().strip('"') - if candidate.startswith("UPID:"): - return candidate - return None + """UPID aus der Antwort von pvesh fischen. + + Bei Containern schreibt pvesh den Fortschritt vor die UPID, alles in + einer Zeile: + + Creating snap: 100% complete...done."UPID:node:...:vzsnapshot:100:..." + + Zeilenweise auf den Anfang zu pruefen findet die UPID dort nicht - und + ohne UPID bliebe ein fehlgeschlagener Task unbemerkt. + """ + match = _UPID_PATTERN.search(output or "") + return match.group(0) if match else None + + def _verify_created(self, guest, name): + """Nachsehen, ob der Snapshot wirklich brauchbar entstanden ist. + + Zweite Sicherung fuer den Fall, dass ein Fehlschlag weder ueber den + Rueckgabewert noch ueber den Task sichtbar wird: Proxmox laesst dann + einen halbfertigen Eintrag ("snapstate") in der Konfiguration stehen. + """ + try: + snapshots = self.list_snapshots(guest) + except ProxmoxError: + return # nicht schlimmer machen als noetig + found = next((s for s in snapshots if s.name == name), None) + if found is None: + raise ProxmoxError("Der Snapshot '%s' ist nach dem Anlegen nicht " + "vorhanden." % name) + if not found.complete: + raise ProxmoxError("Der Snapshot '%s' ist unvollstaendig geblieben " + "(snapstate: %s) - er enthaelt keine Daten." + % (name, found.snapstate))