From 51550b91047b940dcb1a80240dc95fc77c76161c Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 15 Aug 2025 16:51:11 +0200 Subject: [PATCH] =?UTF-8?q?pve-update.py=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pve-update.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 pve-update.py diff --git a/pve-update.py b/pve-update.py new file mode 100644 index 0000000..a798e3d --- /dev/null +++ b/pve-update.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +import subprocess +import logging +import datetime +import os +import sys +import re + +# ========== Einstellungen ========== +ZFS_DATASETS = ["rpool/ROOT", "rpool/pveconf"] +SNAPSHOT_TAG = "pve-update-via-rmm" +MAX_SNAPSHOTS = 5 +LOGFILE = "/var/log/proxmox_update.log" +REBOOT_FLAG = os.environ.get("REBOOT", "NO").upper() +TIMESTAMP = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + +# ========== Logging Setup ========== +logging.basicConfig( + filename=LOGFILE, + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", +) + +def run(cmd, check=True): + """Kommando ausführen""" + logging.debug(f"Running: {cmd}") + try: + result = subprocess.run( + cmd, shell=True, check=check, + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + return result.stdout.decode().strip() + except subprocess.CalledProcessError as e: + logging.error(f"Fehler bei Befehl: {cmd}") + logging.error(e.stderr.decode().strip()) + if check: + raise + return "" + +def has_updates(): + """apt update ausführen & prüfen, ob Updates vorhanden""" + try: + output = run("apt update") + if "upgradable" in output.lower(): + logging.info("Updates verfügbar.") + return True + logging.info("Keine Updates verfügbar.") + return False + except Exception: + logging.error("apt update fehlgeschlagen.") + sys.exit(2) + +def is_kernel_update_available(): + """Prüfen, ob ein Kernel-Update verfügbar ist""" + output = run("apt list --upgradable", check=False) + return any("linux-image-" in line for line in output.splitlines()) + +def create_snapshot(dataset): + """Snapshot mit Tag erstellen""" + snapshot_name = f"{dataset}@{SNAPSHOT_TAG}-{TIMESTAMP}" + logging.info(f"Erstelle Snapshot: {snapshot_name}") + run(f"zfs snapshot {snapshot_name}") + +def cleanup_snapshots(dataset): + """Alte Snapshots mit dem definierten Tag löschen""" + logging.info(f"Bereinige alte Snapshots in {dataset}") + snap_list = run(f"zfs list -t snapshot -o name -s creation | grep '^{dataset}@{SNAPSHOT_TAG}-'", check=False).splitlines() + if len(snap_list) > MAX_SNAPSHOTS: + for snap in snap_list[:-MAX_SNAPSHOTS]: + logging.info(f"Lösche Snapshot: {snap}") + run(f"zfs destroy {snap}") + +def upgrade_system(): + """Upgrade durchführen""" + try: + logging.info("Starte apt dist-upgrade …") + run("apt dist-upgrade -y") + logging.info("Führe apt autoremove aus …") + run("apt autoremove -y") + except Exception: + logging.error("Upgrade fehlgeschlagen.") + sys.exit(2) + +def main(): + logging.info("===== Proxmox Update & Snapshot gestartet =====") + + if os.geteuid() != 0: + print("Dieses Skript muss als root ausgeführt werden.") + sys.exit(2) + + if not has_updates(): + logging.info("Beende – keine Updates.") + sys.exit(0) + + # Snapshots nur bei Update + for dataset in ZFS_DATASETS: + create_snapshot(dataset) + cleanup_snapshots(dataset) + + # Upgrade + upgrade_system() + + # Kernel-Update erkennen + if is_kernel_update_available(): + logging.warning("Kernel-Update erkannt – Neustart empfohlen.") + if REBOOT_FLAG == "YES": + logging.info("Reboot wird ausgeführt …") + run("reboot") + sys.exit(1) # Exit 1 = Neustart empfohlen + + logging.info("===== Update-Vorgang abgeschlossen =====") + sys.exit(0) + +if __name__ == "__main__": + main()