feat: add CentOS Stream support to deployment and maintenance scripts

This commit is contained in:
2026-07-26 00:16:39 +02:00
parent 64e5b32514
commit 4c675cd634
5 changed files with 51 additions and 34 deletions
+7 -7
View File
@@ -13,7 +13,7 @@ ships with `--dry-run` where applicable.
|---------|--------| |---------|--------|
| Language | Python 3.12+ | | Language | Python 3.12+ |
| Package manager | None — stdlib only | | Package manager | None — stdlib only |
| Target platforms | Fedora Server, openEuler; FreeBSD 13+ (`network-diag.py` only) | | Target platforms | Fedora Server, CentOS Stream, openEuler; FreeBSD 13+ (`network-diag.py` only) |
| Lint / format | Ruff (line length 100 — see `ruff.toml`) | | Lint / format | Ruff (line length 100 — see `ruff.toml`) |
| Licence | MIT | | Licence | MIT |
@@ -32,16 +32,16 @@ ships with `--dry-run` where applicable.
| Script | Version | Purpose | | Script | Version | Purpose |
|--------|---------|---------| |--------|---------|---------|
| `server-setup.py` | 1.2.0 | Server initial setup: packages, firewall (SSH verified before start), SELinux, Podman, automatic updates | | `server-setup.py` | 1.3.0 | Server initial setup: packages, firewall (SSH verified before start), SELinux, Podman, automatic updates |
| `workstation-setup.py` | 4.1.0 | Fedora desktop setup: Brave, dev tools (Bun, Zed, uv, Rust), Flatpak apps, SSH + Git | | `workstation-setup.py` | 4.1.0 | Fedora desktop setup: Brave, dev tools (Bun, Zed, uv, Rust), Flatpak apps, SSH + Git |
| `vllm-deploy.py` | 1.1.0 | vLLM inference server behind nginx with HTTPS and API-key auth — installs ROCm on AMD GPUs | | `vllm-deploy.py` | 1.2.0 | vLLM inference server behind nginx with HTTPS and API-key auth — installs ROCm on AMD GPUs |
| `zabbix-deploy.py` | 1.0.0 | Zabbix 7.4 monitoring stack: PostgreSQL 18, nginx with HTTPS on :443, PHP-FPM, SELinux | | `zabbix-deploy.py` | 1.1.0 | Zabbix 7.4 monitoring stack: PostgreSQL 18, nginx with HTTPS on :443, PHP-FPM, SELinux |
### Maintenance ### Maintenance
| Script | Version | Purpose | | Script | Version | Purpose |
|--------|---------|---------| |--------|---------|---------|
| `system-optimise.py` | 1.3.1 | System cleanup: old kernels (running + one fallback always kept), journals, temp files, core dumps, package audit. Refuses rpm-ostree systems | | `system-optimise.py` | 1.4.0 | System cleanup: old kernels (running + one fallback always kept), journals, temp files, core dumps, package audit. Refuses rpm-ostree systems |
--- ---
@@ -107,8 +107,8 @@ flags to skip individual sections.
it falls back to the legacy `ping6` binary where available. it falls back to the legacy `ping6` binary where available.
- `system-diag.py` is Linux-only (it reads `/proc` and `/sys`) and says - `system-diag.py` is Linux-only (it reads `/proc` and `/sys`) and says
so clearly on other platforms. so clearly on other platforms.
- The deployment and maintenance scripts target Fedora and openEuler - The deployment and maintenance scripts target Fedora, CentOS Stream and
(dnf4 and dnf5 are both handled). openEuler (dnf4 and dnf5 are both handled).
--- ---
+6 -5
View File
@@ -2,7 +2,7 @@
# Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org) # Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""Idempotent server setup for Fedora Server and openEuler. """Idempotent server setup for Fedora Server, CentOS Stream and openEuler.
No external dependencies — stdlib and system tools only. Every operation No external dependencies — stdlib and system tools only. Every operation
checks current state before acting; running the script repeatedly produces checks current state before acting; running the script repeatedly produces
@@ -38,7 +38,7 @@ GREEN = "\033[32m"
YELLOW = "\033[33m" YELLOW = "\033[33m"
DIM = "\033[2m" DIM = "\033[2m"
RESET = "\033[0m" RESET = "\033[0m"
VERSION = "1.2.0" VERSION = "1.3.0"
# Timeout in seconds for dnf operations — metadata downloads and package # Timeout in seconds for dnf operations — metadata downloads and package
# transactions on a fresh or slow system routinely exceed the 60 s default # transactions on a fresh or slow system routinely exceed the 60 s default
@@ -48,6 +48,7 @@ DNF_TIMEOUT = 1800
# Map os-release ID to display name — only these two are supported. # Map os-release ID to display name — only these two are supported.
SUPPORTED_OS: dict[str, str] = { SUPPORTED_OS: dict[str, str] = {
"fedora": "Fedora", "fedora": "Fedora",
"centos": "CentOS Stream",
"openeuler": "openEuler", "openeuler": "openEuler",
} }
@@ -850,9 +851,9 @@ def setup_auto_updates(os_id: str, dry_run: bool = False) -> dict[str, Any]:
"failed": False, "failed": False,
} }
if os_id == "fedora": if os_id in ("fedora", "centos"):
# --- Install dnf-automatic (dnf4 name; provided virtually on dnf5) --- # --- Install dnf-automatic (dnf4 name; provided virtually on dnf5) ---
_status("Checking dnf-automatic (Fedora)") _status("Checking dnf-automatic (Fedora / CentOS Stream)")
pkg_present = _rpm_installed("dnf-automatic") or _rpm_installed("dnf5-plugin-automatic") pkg_present = _rpm_installed("dnf-automatic") or _rpm_installed("dnf5-plugin-automatic")
if not pkg_present: if not pkg_present:
_status_done("not installed") _status_done("not installed")
@@ -1221,7 +1222,7 @@ def print_summary(
def parse_args(argv: list[str] | None = None) -> argparse.Namespace: def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
"""Parse command-line arguments.""" """Parse command-line arguments."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Idempotent server setup for Fedora Server and openEuler", description="Idempotent server setup for Fedora Server, CentOS Stream and openEuler",
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
) )
parser.add_argument( parser.add_argument(
+4 -3
View File
@@ -2,7 +2,7 @@
# Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org) # Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
"""Idempotent system cleanup and optimisation for Fedora and openEuler. """Idempotent system cleanup and optimisation for Fedora, CentOS Stream and openEuler.
Cleans old kernels, DNF caches, systemd journals, temp files, and core dumps. Cleans old kernels, DNF caches, systemd journals, temp files, and core dumps.
Every operation checks current state before acting; running the script Every operation checks current state before acting; running the script
@@ -37,10 +37,11 @@ GREEN = "\033[32m"
YELLOW = "\033[33m" YELLOW = "\033[33m"
DIM = "\033[2m" DIM = "\033[2m"
RESET = "\033[0m" RESET = "\033[0m"
VERSION = "1.3.1" VERSION = "1.4.0"
SUPPORTED_OS: dict[str, str] = { SUPPORTED_OS: dict[str, str] = {
"fedora": "Fedora", "fedora": "Fedora",
"centos": "CentOS Stream",
"openeuler": "openEuler", "openeuler": "openEuler",
} }
@@ -1112,7 +1113,7 @@ def print_summary(
def parse_args(argv: list[str] | None = None) -> argparse.Namespace: def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
"""Parse command-line arguments.""" """Parse command-line arguments."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Idempotent system cleanup and optimisation for Fedora and openEuler", description="Idempotent system cleanup and optimisation for Fedora, CentOS Stream and openEuler",
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
) )
parser.add_argument( parser.add_argument(
+25 -14
View File
@@ -4,13 +4,13 @@
"""Idempotent vLLM deployment script for AMD ROCm GPUs. """Idempotent vLLM deployment script for AMD ROCm GPUs.
Deploys vLLM behind nginx with self-signed TLS on Fedora or openEuler. Deploys vLLM behind nginx with self-signed TLS on Fedora, CentOS Stream, or openEuler.
vLLM binds to ::1 (IPv4 loopback fallback) on port 8000, internal only; vLLM binds to ::1 (IPv4 loopback fallback) on port 8000, internal only;
nginx proxies :443 → the loopback upstream with streaming (SSE) support. nginx proxies :443 → the loopback upstream with streaming (SSE) support.
The endpoint requires an API key, delivered to the service via a 0600 The endpoint requires an API key, delivered to the service via a 0600
EnvironmentFile; nginx→vLLM proxying is allowed through SELinux on EnvironmentFile; nginx→vLLM proxying is allowed through SELinux on
enforcing systems. Fedora gets ROCm from its native repositories; enforcing systems. Fedora gets ROCm from its native repositories;
openEuler falls back to AMD's amdgpu-install (unsupported by AMD). CentOS Stream and openEuler fall back to AMD's amdgpu-install (unsupported by AMD).
No external dependencies — stdlib and system tools only. Every operation No external dependencies — stdlib and system tools only. Every operation
checks current state before acting; running the script repeatedly produces checks current state before acting; running the script repeatedly produces
@@ -46,10 +46,11 @@ GREEN = "\033[32m"
YELLOW = "\033[33m" YELLOW = "\033[33m"
DIM = "\033[2m" DIM = "\033[2m"
RESET = "\033[0m" RESET = "\033[0m"
VERSION = "1.1.0" VERSION = "1.2.0"
SUPPORTED_OS: dict[str, str] = { SUPPORTED_OS: dict[str, str] = {
"fedora": "Fedora", "fedora": "Fedora",
"centos": "CentOS Stream",
"openeuler": "openEuler", "openeuler": "openEuler",
} }
@@ -297,8 +298,17 @@ def _systemctl_is_enabled(service: str) -> bool:
return result.returncode == 0 return result.returncode == 0
def _map_to_el_major(version_id: str) -> str: def _map_to_el_major(os_id: str, version_id: str) -> str:
"""Map an openEuler version to the closest RHEL major ("8"/"9") for AMDGPU repos.""" """Map an OS version to the closest RHEL major ("8"/"9") for AMDGPU repos.
CentOS Stream VERSION_ID is already the RHEL major (e.g. "9", "10");
openEuler uses its own numbering (≥24 → "9", otherwise "8").
"""
if os_id.startswith("centos"):
try:
return str(int(version_id.split(".")[0]))
except (ValueError, IndexError):
return "9"
try: try:
major_ver = int(version_id.split(".")[0]) major_ver = int(version_id.split(".")[0])
except (ValueError, IndexError): except (ValueError, IndexError):
@@ -556,19 +566,20 @@ def _install_rocm_fedora(dry_run: bool) -> None:
_status_done("ok") _status_done("ok")
def _install_rocm_openeuler(version_id: str, dry_run: bool) -> None: def _install_rocm_openeuler(os_id: str, version_id: str, dry_run: bool) -> None:
"""Install ROCm on openEuler via AMD's amdgpu-install script. """Install ROCm on CentOS Stream / openEuler via AMD's amdgpu-install script.
AMD does not list openEuler as a supported ROCm platform — the RHEL AMD does not list CentOS Stream or openEuler as supported ROCm platforms
packages are installed strictly on a best-effort basis and may fail. the RHEL packages are installed strictly on a best-effort basis and may
The RHEL point-release directory and RPM filename are resolved fail. The RHEL point-release directory and RPM filename are resolved
dynamically from the repository (nothing is hardcoded). dynamically from the repository (nothing is hardcoded).
""" """
_warn( _warn(
"AMD does not support ROCm on openEuler — amdgpu-install targets RHEL. " "AMD does not support ROCm on CentOS Stream / openEuler — "
"Continuing best-effort; this may fail or produce a broken install." "amdgpu-install targets RHEL. Continuing best-effort; this may fail "
"or produce a broken install."
) )
el_major = _map_to_el_major(version_id) el_major = _map_to_el_major(os_id, version_id)
_status(f"Resolving latest amdgpu-install release for el{el_major}") _status(f"Resolving latest amdgpu-install release for el{el_major}")
listing = _fetch_text(f"{AMDGPU_INSTALL_URL}/") listing = _fetch_text(f"{AMDGPU_INSTALL_URL}/")
@@ -695,7 +706,7 @@ def preflight_checks(os_id: str, version_id: str, dry_run: bool = False) -> dict
if os_id == "fedora": if os_id == "fedora":
_install_rocm_fedora(dry_run) _install_rocm_fedora(dry_run)
else: else:
_install_rocm_openeuler(version_id, dry_run) _install_rocm_openeuler(os_id, version_id, dry_run)
results["rocm"] = True results["rocm"] = True
# GPU verification via rocm-smi. # GPU verification via rocm-smi.
+9 -5
View File
@@ -4,8 +4,8 @@
"""Idempotent Zabbix 7.4 deployment with PostgreSQL 18, nginx, PHP-FPM. """Idempotent Zabbix 7.4 deployment with PostgreSQL 18, nginx, PHP-FPM.
Deploys a full Zabbix monitoring stack with TLS-secured frontend on Fedora Deploys a full Zabbix monitoring stack with TLS-secured frontend on Fedora,
or openEuler. Every operation checks current state before acting; running CentOS Stream or openEuler. Every operation checks current state before acting; running
the script repeatedly produces the same result with no errors and no the script repeatedly produces the same result with no errors and no
duplicate work. duplicate work.
@@ -45,10 +45,11 @@ GREEN = "\033[32m"
YELLOW = "\033[33m" YELLOW = "\033[33m"
DIM = "\033[2m" DIM = "\033[2m"
RESET = "\033[0m" RESET = "\033[0m"
VERSION = "1.0.0" VERSION = "1.1.0"
SUPPORTED_OS: dict[str, str] = { SUPPORTED_OS: dict[str, str] = {
"fedora": "Fedora", "fedora": "Fedora",
"centos": "CentOS Stream",
"openeuler": "openEuler", "openeuler": "openEuler",
} }
@@ -371,6 +372,9 @@ def _map_to_rhel_version(os_id: str, version_id: str) -> tuple[str, str]:
openEuler 24+ -> el9 openEuler 24+ -> el9
openEuler 22 -> el8 openEuler 22 -> el8
""" """
if os_id == "centos":
return (version_id, version_id)
if os_id == "fedora": if os_id == "fedora":
return ("9", "9") return ("9", "9")
@@ -462,7 +466,7 @@ def setup_postgresql(
"""Install and configure PostgreSQL 18. Idempotent.""" """Install and configure PostgreSQL 18. Idempotent."""
results: dict[str, Any] = {} results: dict[str, Any] = {}
# Map to EL version for repo URLs (used for openEuler; Fedora has native # Map to EL version for repo URLs (used for non-Fedora; Fedora has native
# PGDG reporpms). # PGDG reporpms).
el_major, _ = _map_to_rhel_version(os_id, version_id) el_major, _ = _map_to_rhel_version(os_id, version_id)
@@ -515,7 +519,7 @@ def setup_postgresql(
_status_done("not present") _status_done("not present")
else: else:
_status("PostgreSQL module disable") _status("PostgreSQL module disable")
_status_done("skipped (openEuler)") _status_done("skipped (non-Fedora)")
# 2c. Install PostgreSQL 18 packages. # 2c. Install PostgreSQL 18 packages.
pg_packages = ["postgresql18-server", "postgresql18"] pg_packages = ["postgresql18-server", "postgresql18"]