116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
#!/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-cron"
|
||
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()
|