950 lines
18 KiB
Python
950 lines
18 KiB
Python
#!/usr/bin/env python3
|
||
|
||
import subprocess
|
||
import logging
|
||
import datetime
|
||
import os
|
||
import re
|
||
import sys
|
||
import shlex
|
||
|
||
|
||
# =========================
|
||
# Einstellungen
|
||
# =========================
|
||
|
||
ZFS_DATASETS = [
|
||
"rpool/ROOT/pve-1",
|
||
"rpool/pveconf"
|
||
]
|
||
|
||
SNAPSHOT_TAG = "pve-update-via-cron"
|
||
|
||
# Anzahl der ZFS-Snapshots, die je Dataset behalten werden.
|
||
MAX_SNAPSHOTS = 5
|
||
|
||
# Anzahl älterer Kernel, die zusätzlich zum aktuell
|
||
# laufenden Kernel als Fallback behalten werden.
|
||
#
|
||
# 0 = nur aktiven Kernel behalten
|
||
# 1 = aktiven + 1 Fallback
|
||
# 2 = aktiven + 2 Fallback-Kernel
|
||
KEEP_OLD_KERNELS = 1
|
||
|
||
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")
|
||
|
||
|
||
# Unterstützt sowohl historische pve-kernel-* Pakete
|
||
# als auch aktuelle proxmox-kernel-* Pakete.
|
||
KERNEL_PKG_PATTERN = re.compile(
|
||
r"^(?:pve-kernel|proxmox-kernel)-([0-9][^\s]*?)(?:-signed)?$"
|
||
)
|
||
|
||
|
||
# Cron-Jobs laufen mit einem minimalen PATH.
|
||
# Proxmox-Tools liegen teilweise in /usr/sbin bzw. /sbin.
|
||
os.environ["PATH"] = (
|
||
"/usr/sbin:/usr/bin:/sbin:/bin:"
|
||
+ os.environ.get("PATH", "")
|
||
)
|
||
|
||
|
||
# =========================
|
||
# Logging
|
||
# =========================
|
||
|
||
logging.basicConfig(
|
||
filename=LOGFILE,
|
||
level=logging.INFO,
|
||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||
)
|
||
|
||
|
||
def log(msg, level="info"):
|
||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
|
||
print(f"[{timestamp}] {msg}")
|
||
|
||
if level == "info":
|
||
logging.info(msg)
|
||
|
||
elif level == "warn":
|
||
logging.warning(msg)
|
||
|
||
elif level == "error":
|
||
logging.error(msg)
|
||
|
||
|
||
# =========================
|
||
# Kommando Helfer
|
||
# =========================
|
||
|
||
def run(cmd, check=True, quiet=False):
|
||
"""
|
||
Führt einen Befehl ohne Shell aus.
|
||
|
||
cmd kann eine Argumentliste oder ein String ohne
|
||
Shell-Metazeichen sein.
|
||
|
||
quiet=True unterdrückt das automatische Loggen
|
||
von stdout.
|
||
"""
|
||
|
||
if isinstance(cmd, str):
|
||
cmd = shlex.split(cmd)
|
||
|
||
log(
|
||
f"Ausführen: {' '.join(cmd)}"
|
||
)
|
||
|
||
try:
|
||
result = subprocess.run(
|
||
cmd,
|
||
check=check,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
)
|
||
|
||
if result.stdout.strip() and not quiet:
|
||
log(result.stdout.strip())
|
||
|
||
if result.stderr.strip() and not check:
|
||
log(result.stderr.strip(), "warn")
|
||
|
||
return result.stdout.strip()
|
||
|
||
except FileNotFoundError as e:
|
||
|
||
log(
|
||
f"FEHLER: Befehl nicht gefunden: "
|
||
f"{' '.join(cmd)} ({e})",
|
||
"error",
|
||
)
|
||
|
||
if check:
|
||
raise
|
||
|
||
return ""
|
||
|
||
except subprocess.CalledProcessError as e:
|
||
|
||
log(
|
||
f"FEHLER: {' '.join(cmd)}",
|
||
"error",
|
||
)
|
||
|
||
if e.stderr:
|
||
log(
|
||
e.stderr.strip(),
|
||
"error",
|
||
)
|
||
|
||
if check:
|
||
raise
|
||
|
||
return ""
|
||
|
||
|
||
# =========================
|
||
# Kernel Prüfung
|
||
# =========================
|
||
|
||
def get_running_kernel():
|
||
return run([
|
||
"uname",
|
||
"-r",
|
||
])
|
||
|
||
|
||
def version_key(version):
|
||
"""
|
||
Sortierbarer Schlüssel für Kernel-Versionen.
|
||
|
||
Beispiel:
|
||
6.14.8
|
||
6.14.10
|
||
6.14.11
|
||
"""
|
||
|
||
return [
|
||
int(part) if part.isdigit() else part
|
||
for part in re.split(r"[.\-]", version)
|
||
]
|
||
|
||
|
||
def _query_kernel_packages():
|
||
"""
|
||
Fragt dpkg nach beiden Proxmox-Kernel-Paketgruppen ab.
|
||
|
||
Zurückgegeben werden Tupel:
|
||
|
||
(status, paketname)
|
||
|
||
Beispiel:
|
||
|
||
("ii", "proxmox-kernel-6.14.11-1-pve-signed")
|
||
("rc", "proxmox-kernel-6.8.12-2-pve-signed")
|
||
|
||
Nur "ii" bedeutet tatsächlich installiert.
|
||
"""
|
||
|
||
output = run(
|
||
[
|
||
"dpkg-query",
|
||
"-W",
|
||
"-f=${db:Status-Abbrev} ${Package}\n",
|
||
"pve-kernel-*",
|
||
"proxmox-kernel-*",
|
||
],
|
||
check=False,
|
||
quiet=True,
|
||
)
|
||
|
||
packages = []
|
||
|
||
for line in output.splitlines():
|
||
|
||
parts = line.split(None, 1)
|
||
|
||
if len(parts) != 2:
|
||
continue
|
||
|
||
status, pkg = parts
|
||
|
||
packages.append(
|
||
(status, pkg)
|
||
)
|
||
|
||
return packages
|
||
|
||
|
||
def get_installed_kernel_versions():
|
||
"""
|
||
Liefert tatsächlich installierte Proxmox-Kernel.
|
||
|
||
Nur dpkg-Status "ii" wird berücksichtigt.
|
||
"""
|
||
|
||
packages = _query_kernel_packages()
|
||
|
||
kernels = []
|
||
|
||
for status, pkg in packages:
|
||
|
||
if status != "ii":
|
||
continue
|
||
|
||
match = KERNEL_PKG_PATTERN.match(pkg)
|
||
|
||
if not match:
|
||
continue
|
||
|
||
kernels.append({
|
||
"version": match.group(1),
|
||
"package": pkg,
|
||
})
|
||
|
||
return kernels
|
||
|
||
|
||
def get_installed_kernel():
|
||
"""
|
||
Ermittelt die höchste installierte Proxmox-Kernel-Version.
|
||
"""
|
||
|
||
kernels = get_installed_kernel_versions()
|
||
|
||
if not kernels:
|
||
return ""
|
||
|
||
versions = sorted(
|
||
{
|
||
kernel["version"]
|
||
for kernel in kernels
|
||
},
|
||
key=version_key,
|
||
)
|
||
|
||
log(
|
||
"Installierte Proxmox-Kernel: "
|
||
+ ", ".join(versions)
|
||
)
|
||
|
||
return versions[-1]
|
||
|
||
|
||
def cleanup_old_kernels():
|
||
"""
|
||
Entfernt alte installierte Proxmox-Kernel.
|
||
|
||
Sicherheitsregeln:
|
||
|
||
- Der aktuell laufende Kernel wird niemals gelöscht.
|
||
- Ist der laufende Kernel nicht eindeutig in der
|
||
installierten Kernel-Liste vorhanden, wird nichts gelöscht.
|
||
- KEEP_OLD_KERNELS ältere Kernel bleiben erhalten.
|
||
- Ein neuerer installierter Kernel als der laufende
|
||
wird ebenfalls nicht gelöscht, da er nach einem
|
||
Reboot benötigt werden kann.
|
||
- Nur Status "ii" wird hier berücksichtigt.
|
||
- Entfernen erfolgt über apt-get purge.
|
||
"""
|
||
|
||
running = get_running_kernel()
|
||
|
||
if not running:
|
||
log(
|
||
"⚠️ Aktiver Kernel konnte nicht ermittelt werden - "
|
||
"keine Kernel werden gelöscht",
|
||
"warn",
|
||
)
|
||
return
|
||
|
||
kernels = get_installed_kernel_versions()
|
||
|
||
if not kernels:
|
||
log(
|
||
"⚠️ Keine installierten Proxmox-Kernel erkannt - "
|
||
"keine Kernel werden gelöscht",
|
||
"warn",
|
||
)
|
||
return
|
||
|
||
# Version -> Pakete
|
||
version_packages = {}
|
||
|
||
for kernel in kernels:
|
||
|
||
version = kernel["version"]
|
||
package = kernel["package"]
|
||
|
||
version_packages.setdefault(
|
||
version,
|
||
[]
|
||
).append(package)
|
||
|
||
installed_versions = sorted(
|
||
version_packages.keys(),
|
||
key=version_key,
|
||
)
|
||
|
||
log(
|
||
f"🖥️ Aktiver Kernel: {running}"
|
||
)
|
||
|
||
log(
|
||
"📋 Installierte Proxmox-Kernel:"
|
||
)
|
||
|
||
for version in reversed(installed_versions):
|
||
|
||
marker = ""
|
||
|
||
if version == running:
|
||
marker = " ← AKTIV"
|
||
|
||
log(
|
||
f" {version}{marker}"
|
||
)
|
||
|
||
# Der laufende Kernel muss exakt als installierte
|
||
# Kernel-Version vorhanden sein.
|
||
active_matches = [
|
||
version
|
||
for version in installed_versions
|
||
if version == running
|
||
]
|
||
|
||
if len(active_matches) != 1:
|
||
|
||
log(
|
||
"⚠️ Aktiver Kernel wurde nicht eindeutig in den "
|
||
"installierten Kernel-Paketen gefunden - "
|
||
"keine Kernel werden gelöscht",
|
||
"warn",
|
||
)
|
||
|
||
return
|
||
|
||
active_index = installed_versions.index(running)
|
||
|
||
# Alles unterhalb des laufenden Kernels ist älter.
|
||
older_versions = installed_versions[:active_index]
|
||
|
||
# Die direkt älteren KEEP_OLD_KERNELS Versionen bleiben.
|
||
fallback_versions = older_versions[
|
||
max(0, len(older_versions) - KEEP_OLD_KERNELS):
|
||
]
|
||
|
||
for version in reversed(fallback_versions):
|
||
|
||
log(
|
||
f"🛡️ Fallback-Kernel bleibt erhalten: {version}"
|
||
)
|
||
|
||
# Sicherheitsregel:
|
||
#
|
||
# Versionen oberhalb des laufenden Kernels werden NICHT
|
||
# gelöscht. Das kann ein neuer Kernel sein, auf den nach
|
||
# dem Reboot gewechselt werden soll.
|
||
newer_versions = [
|
||
version
|
||
for version in installed_versions
|
||
if version_key(version) > version_key(running)
|
||
]
|
||
|
||
for version in newer_versions:
|
||
|
||
log(
|
||
f"🛡️ Neuerer installierter Kernel bleibt erhalten: "
|
||
f"{version} - Reboot kann erforderlich sein",
|
||
"warn",
|
||
)
|
||
|
||
protected_versions = {
|
||
running,
|
||
*fallback_versions,
|
||
*newer_versions,
|
||
}
|
||
|
||
remove_versions = [
|
||
version
|
||
for version in installed_versions
|
||
if version not in protected_versions
|
||
]
|
||
|
||
if not remove_versions:
|
||
|
||
log(
|
||
"OK: Keine alten Kernel zur Entfernung vorhanden"
|
||
)
|
||
|
||
return
|
||
|
||
for version in remove_versions:
|
||
|
||
packages_to_remove = version_packages.get(
|
||
version,
|
||
[],
|
||
)
|
||
|
||
for package in packages_to_remove:
|
||
|
||
log(
|
||
f"🗑️ Entferne alten Kernel: {package}"
|
||
)
|
||
|
||
result = subprocess.run(
|
||
[
|
||
"apt-get",
|
||
"purge",
|
||
"-y",
|
||
package,
|
||
],
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
|
||
log(
|
||
f"✅ Kernel entfernt: {package}"
|
||
)
|
||
|
||
else:
|
||
|
||
log(
|
||
f"❌ Fehler beim Entfernen von {package} "
|
||
f"(Exit-Code {result.returncode})",
|
||
"error",
|
||
)
|
||
|
||
if result.stderr.strip():
|
||
|
||
log(
|
||
result.stderr.strip(),
|
||
"error",
|
||
)
|
||
|
||
# Absichtlich kein raise:
|
||
# Fehler bei einem einzelnen Kernel sollen
|
||
# den restlichen Update-Lauf nicht abbrechen.
|
||
|
||
|
||
def cleanup_old_kernel_configs():
|
||
"""
|
||
Entfernt ausschließlich dpkg-Konfigurationsreste ("rc")
|
||
alter Proxmox-Kernel-Pakete.
|
||
"""
|
||
|
||
packages = _query_kernel_packages()
|
||
|
||
residual_pkgs = [
|
||
pkg
|
||
for status, pkg in packages
|
||
if status == "rc"
|
||
]
|
||
|
||
if not residual_pkgs:
|
||
|
||
log(
|
||
"OK: Keine Konfigurationsreste alter Kernel-Pakete gefunden"
|
||
)
|
||
|
||
return
|
||
|
||
residual_pkgs = sorted(residual_pkgs)
|
||
|
||
total = len(residual_pkgs)
|
||
|
||
log(
|
||
f"🧼 Entferne Konfigurationsreste von "
|
||
f"{total} alten Kernel-Paketen"
|
||
)
|
||
|
||
for index, pkg in enumerate(
|
||
residual_pkgs,
|
||
start=1,
|
||
):
|
||
|
||
log(
|
||
f"({index}/{total}) Lösche Reste von: {pkg}"
|
||
)
|
||
|
||
result = subprocess.run(
|
||
[
|
||
"apt-get",
|
||
"purge",
|
||
"-y",
|
||
"-qq",
|
||
pkg,
|
||
],
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
|
||
log(
|
||
f"✅ ({index}/{total}) Erledigt: {pkg}"
|
||
)
|
||
|
||
else:
|
||
|
||
log(
|
||
f"❌ ({index}/{total}) Fehler beim Entfernen "
|
||
f"der Reste: {pkg}",
|
||
"error",
|
||
)
|
||
|
||
if result.stderr.strip():
|
||
|
||
log(
|
||
result.stderr.strip(),
|
||
"error",
|
||
)
|
||
|
||
|
||
def reboot_required():
|
||
"""
|
||
Prüft getrennt von der Kernelbereinigung, ob ein Reboot
|
||
erforderlich ist.
|
||
|
||
Reboot erforderlich, wenn der laufende Kernel nicht
|
||
der höchsten installierten Kernel-Version entspricht.
|
||
"""
|
||
|
||
running = get_running_kernel()
|
||
installed = get_installed_kernel()
|
||
|
||
log(
|
||
f"Kernel aktiv: {running}"
|
||
)
|
||
|
||
log(
|
||
f"Kernel installiert: {installed}"
|
||
)
|
||
|
||
if not running or not installed:
|
||
|
||
log(
|
||
"⚠️ Kernel-Vergleich nicht möglich - "
|
||
"gehe sicherheitshalber von 'kein Reboot nötig' aus",
|
||
"warn",
|
||
)
|
||
|
||
return False
|
||
|
||
return running != installed
|
||
|
||
|
||
# =========================
|
||
# Boot Prüfung
|
||
# =========================
|
||
|
||
def proxmox_boot_ok():
|
||
|
||
try:
|
||
|
||
result = subprocess.run(
|
||
[
|
||
"proxmox-boot-tool",
|
||
"status",
|
||
],
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
)
|
||
|
||
except FileNotFoundError as e:
|
||
|
||
log(
|
||
f"FEHLER: proxmox-boot-tool nicht gefunden: {e}",
|
||
"error",
|
||
)
|
||
|
||
return False
|
||
|
||
if result.stdout.strip():
|
||
|
||
log(
|
||
result.stdout.strip()
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
|
||
return True
|
||
|
||
if result.stderr.strip():
|
||
|
||
log(
|
||
result.stderr.strip(),
|
||
"error",
|
||
)
|
||
|
||
else:
|
||
|
||
log(
|
||
"FEHLER: proxmox-boot-tool status "
|
||
f"lieferte Exit-Code {result.returncode}",
|
||
"error",
|
||
)
|
||
|
||
return False
|
||
|
||
|
||
def refresh_boot():
|
||
|
||
log(
|
||
"Aktualisiere Proxmox Bootloader"
|
||
)
|
||
|
||
run([
|
||
"proxmox-boot-tool",
|
||
"refresh",
|
||
])
|
||
|
||
|
||
# =========================
|
||
# Update Prüfung
|
||
# =========================
|
||
|
||
def has_updates():
|
||
|
||
run([
|
||
"apt-get",
|
||
"update",
|
||
])
|
||
|
||
output = run(
|
||
[
|
||
"apt-get",
|
||
"-s",
|
||
"dist-upgrade",
|
||
],
|
||
check=False,
|
||
)
|
||
|
||
updates = [
|
||
line
|
||
for line in output.splitlines()
|
||
if line.startswith("Inst ")
|
||
]
|
||
|
||
if updates:
|
||
|
||
log(
|
||
f"{len(updates)} Updates verfügbar"
|
||
)
|
||
|
||
return True
|
||
|
||
log(
|
||
"OK: Keine Updates verfügbar"
|
||
)
|
||
|
||
return False
|
||
|
||
|
||
# =========================
|
||
# ZFS Snapshot Funktionen
|
||
# =========================
|
||
|
||
def zfs_dataset_exists(dataset):
|
||
|
||
result = subprocess.run(
|
||
[
|
||
"zfs",
|
||
"list",
|
||
"-H",
|
||
"-o",
|
||
"name",
|
||
dataset,
|
||
],
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
)
|
||
|
||
return result.returncode == 0
|
||
|
||
|
||
def create_snapshot(dataset):
|
||
|
||
snapshot = (
|
||
f"{dataset}@{SNAPSHOT_TAG}-{TIMESTAMP}"
|
||
)
|
||
|
||
log(
|
||
f"Snapshot erstellen: {snapshot}"
|
||
)
|
||
|
||
run([
|
||
"zfs",
|
||
"snapshot",
|
||
snapshot,
|
||
])
|
||
|
||
|
||
def cleanup_snapshots(dataset):
|
||
|
||
output = run(
|
||
[
|
||
"zfs",
|
||
"list",
|
||
"-H",
|
||
"-t",
|
||
"snapshot",
|
||
"-o",
|
||
"name",
|
||
"-s",
|
||
"creation",
|
||
],
|
||
check=False,
|
||
quiet=True,
|
||
)
|
||
|
||
snapshots = []
|
||
|
||
prefix = (
|
||
f"{dataset}@{SNAPSHOT_TAG}-"
|
||
)
|
||
|
||
for line in output.splitlines():
|
||
|
||
if line.startswith(prefix):
|
||
|
||
snapshots.append(line)
|
||
|
||
if len(snapshots) <= MAX_SNAPSHOTS:
|
||
|
||
return
|
||
|
||
for snap in snapshots[:-MAX_SNAPSHOTS]:
|
||
|
||
log(
|
||
f"Lösche Snapshot: {snap}"
|
||
)
|
||
|
||
run([
|
||
"zfs",
|
||
"destroy",
|
||
"-r",
|
||
snap,
|
||
])
|
||
|
||
|
||
def create_update_snapshots():
|
||
|
||
for dataset in ZFS_DATASETS:
|
||
|
||
if not zfs_dataset_exists(dataset):
|
||
|
||
log(
|
||
f"⚠️ ZFS-Dataset nicht vorhanden: {dataset} - "
|
||
"Snapshot wird übersprungen, Update läuft weiter",
|
||
"warn",
|
||
)
|
||
|
||
continue
|
||
|
||
cleanup_snapshots(dataset)
|
||
|
||
create_snapshot(dataset)
|
||
|
||
|
||
# =========================
|
||
# Upgrade
|
||
# =========================
|
||
|
||
def upgrade_system():
|
||
|
||
log(
|
||
"⚙️ Starte dist-upgrade"
|
||
)
|
||
|
||
run([
|
||
"apt-get",
|
||
"dist-upgrade",
|
||
"-y",
|
||
])
|
||
|
||
log(
|
||
"🧼 Starte autoremove"
|
||
)
|
||
|
||
run([
|
||
"apt-get",
|
||
"autoremove",
|
||
"--purge",
|
||
"-y",
|
||
])
|
||
|
||
# Eigene Kernelbereinigung nach dem normalen
|
||
# Debian/Proxmox-Upgrade.
|
||
cleanup_old_kernels()
|
||
|
||
# Danach eventuell verbliebene rc-Reste bereinigen.
|
||
cleanup_old_kernel_configs()
|
||
|
||
|
||
# =========================
|
||
# Hauptprogramm
|
||
# =========================
|
||
|
||
def main():
|
||
|
||
log(
|
||
"===== Proxmox Update gestartet ====="
|
||
)
|
||
|
||
log(
|
||
f"REBOOT-Flag: {REBOOT_FLAG}"
|
||
)
|
||
|
||
# Exit-Code 2 für nicht-root beibehalten.
|
||
if os.geteuid() != 0:
|
||
|
||
log(
|
||
"FEHLER: Muss als root laufen",
|
||
"error",
|
||
)
|
||
|
||
sys.exit(2)
|
||
|
||
kernel_before = get_running_kernel()
|
||
|
||
log(
|
||
f"Kernel vor Update: {kernel_before}"
|
||
)
|
||
|
||
if has_updates():
|
||
|
||
# Snapshots VOR dem eigentlichen dist-upgrade.
|
||
create_update_snapshots()
|
||
|
||
upgrade_system()
|
||
|
||
# Nach Kernel-/Paket-Updates Bootloader aktualisieren.
|
||
refresh_boot()
|
||
|
||
else:
|
||
|
||
log(
|
||
"ℹ️ Keine Updates installiert"
|
||
)
|
||
|
||
# Auch ohne verfügbare Updates regelmäßig alte
|
||
# installierte Kernel bereinigen.
|
||
cleanup_old_kernels()
|
||
|
||
cleanup_old_kernel_configs()
|
||
|
||
# Reboot-Erkennung bleibt vollständig getrennt
|
||
# von der Kernelbereinigung.
|
||
if reboot_required():
|
||
|
||
log(
|
||
"⚠️ Neuer Kernel vorhanden - Reboot erforderlich",
|
||
"warn",
|
||
)
|
||
|
||
if REBOOT_FLAG == "YES":
|
||
|
||
log(
|
||
"Prüfe proxmox-boot-tool"
|
||
)
|
||
|
||
if proxmox_boot_ok():
|
||
|
||
log(
|
||
"OK: Bootloader OK"
|
||
)
|
||
|
||
log(
|
||
"REBOOT=YES erkannt - Neustart in 1 Minute"
|
||
)
|
||
|
||
run([
|
||
"shutdown",
|
||
"-r",
|
||
"+1",
|
||
"Proxmox-Update abgeschlossen",
|
||
])
|
||
|
||
else:
|
||
|
||
log(
|
||
"❌ Bootloader Prüfung fehlgeschlagen - "
|
||
"kein Neustart",
|
||
"error",
|
||
)
|
||
|
||
else:
|
||
|
||
log(
|
||
"REBOOT-Flag ist nicht 'YES' - "
|
||
"kein automatischer Neustart"
|
||
)
|
||
|
||
# Monitoring muss weiterhin erkennen,
|
||
# dass ein Reboot erforderlich ist.
|
||
sys.exit(1)
|
||
|
||
log(
|
||
"OK: Update erfolgreich abgeschlossen"
|
||
)
|
||
|
||
sys.exit(0)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|