update oci manager

This commit is contained in:
MacRimi
2026-03-12 22:13:56 +01:00
parent b4a2e5ee11
commit 6d4006fd93
4 changed files with 226 additions and 73 deletions

View File

@@ -420,6 +420,36 @@ def get_runtime():
}), 500
@oci_bp.route("/runtime/install-script", methods=["GET"])
@require_auth
def get_runtime_install_script():
"""
Get the path to the runtime installation script.
Returns:
Script path for installing Podman.
"""
import os
# Check possible paths for the install script
possible_paths = [
"/usr/local/share/proxmenux/scripts/oci/install_runtime.sh",
os.path.join(os.path.dirname(__file__), "..", "..", "Scripts", "oci", "install_runtime.sh"),
]
for script_path in possible_paths:
if os.path.exists(script_path):
return jsonify({
"success": True,
"script_path": os.path.abspath(script_path)
})
return jsonify({
"success": False,
"message": "Runtime installation script not found"
}), 404
@oci_bp.route("/status/<app_id>", methods=["GET"])
@require_auth
def get_app_status(app_id: str):

View File

@@ -1125,40 +1125,6 @@ class HealthMonitor:
except Exception:
pass
# Check disk_observations for active (non-dismissed) warnings
# This ensures disks with persistent observations appear in Health Monitor
# even if the error is not currently in the logs
try:
all_observations = health_persistence.get_disk_observations()
for obs in all_observations:
device_name = obs.get('device_name', '').replace('/dev/', '')
if not device_name:
continue
severity = (obs.get('severity') or 'warning').upper()
if severity in ('WARNING', 'CRITICAL') and not obs.get('dismissed'):
# Add to issues if not already present
obs_reason = obs.get('raw_message', f'{device_name}: Disk observation recorded')
obs_key = f'/dev/{device_name}'
if obs_key not in storage_details:
issues.append(obs_reason)
storage_details[obs_key] = {
'status': severity,
'reason': obs_reason,
'dismissable': True,
}
# Ensure disk is in disk_errors_by_device for consolidation
if device_name not in disk_errors_by_device:
disk_errors_by_device[device_name] = {
'status': severity,
'reason': obs_reason,
'error_type': obs.get('error_type', 'disk_observation'),
'serial': obs.get('serial', ''),
'model': obs.get('model', ''),
'dismissable': True,
}
except Exception:
pass
# Build checks dict from storage_details
# We consolidate disk error entries (like /Dev/Sda) into physical disk entries
# and only show disks with problems (not healthy ones).
@@ -1252,6 +1218,39 @@ class HealthMonitor:
except Exception:
pass
# Check disk_observations for active (non-dismissed) warnings
# This ensures disks with persistent observations appear in Health Monitor
# even if the error is not currently in the logs
try:
all_observations = health_persistence.get_disk_observations()
for obs in all_observations:
device_name = obs.get('device_name', '').replace('/dev/', '')
if not device_name:
continue
severity = (obs.get('severity') or 'warning').upper()
# Only include if WARNING/CRITICAL and not already dismissed
if severity in ('WARNING', 'CRITICAL') and not obs.get('dismissed'):
# Check if there's a corresponding acknowledged error in the errors table
# If so, skip this observation (it was dismissed via Health Monitor)
error_key = f"disk_smart_{device_name}"
error_record = health_persistence.get_error_by_key(error_key)
if error_record and error_record.get('acknowledged'):
continue # Skip - this was dismissed
# Add to disk_errors_by_device if not already present
if device_name not in disk_errors_by_device:
obs_reason = obs.get('raw_message', f'{device_name}: Disk observation recorded')
disk_errors_by_device[device_name] = {
'status': severity,
'reason': obs_reason,
'error_type': obs.get('error_type', 'disk_observation'),
'serial': obs.get('serial', ''),
'model': obs.get('model', ''),
'dismissable': True,
}
except Exception:
pass
# Add consolidated disk entries (only for disks with errors)
for device_name, error_info in disk_errors_by_device.items():
# Try to find this disk in physical_disks for enriched info

View File

@@ -70,6 +70,154 @@ def ensure_oci_directories():
CONTAINER_PREFIX = "proxmenux"
# =================================================================
# Runtime Installation
# =================================================================
def install_runtime(runtime: str = "podman") -> Dict[str, Any]:
"""
Install container runtime (podman or docker).
Args:
runtime: Runtime to install ('podman' or 'docker')
Returns:
Dict with success status and message
"""
result = {
"success": False,
"message": "",
"runtime": runtime
}
logger.info(f"Installing container runtime: {runtime}")
print(f"\n{'='*60}")
print(f" Installing {runtime.capitalize()}")
print(f"{'='*60}\n")
try:
# Detect distribution
distro = "debian" # Default
if os.path.exists("/etc/os-release"):
with open("/etc/os-release") as f:
content = f.read().lower()
if "alpine" in content:
distro = "alpine"
elif "arch" in content:
distro = "arch"
elif "fedora" in content or "rhel" in content or "centos" in content:
distro = "rhel"
# Install commands by distro
install_commands = {
"debian": {
"podman": ["apt-get", "update", "&&", "apt-get", "install", "-y", "podman"],
"docker": ["apt-get", "update", "&&", "apt-get", "install", "-y", "docker.io"]
},
"alpine": {
"podman": ["apk", "add", "--no-cache", "podman"],
"docker": ["apk", "add", "--no-cache", "docker"]
},
"arch": {
"podman": ["pacman", "-Sy", "--noconfirm", "podman"],
"docker": ["pacman", "-Sy", "--noconfirm", "docker"]
},
"rhel": {
"podman": ["dnf", "install", "-y", "podman"],
"docker": ["dnf", "install", "-y", "docker-ce"]
}
}
# Get install command
if distro == "debian":
# Use shell for && syntax
if runtime == "podman":
cmd = "apt-get update && apt-get install -y podman"
else:
cmd = "apt-get update && apt-get install -y docker.io"
print(f"[*] Running: {cmd}")
proc = subprocess.run(
cmd,
shell=True,
capture_output=False,
timeout=300
)
else:
cmd = install_commands.get(distro, {}).get(runtime, [])
if not cmd:
result["message"] = f"Unsupported distro: {distro}"
return result
print(f"[*] Running: {' '.join(cmd)}")
proc = subprocess.run(
cmd,
capture_output=False,
timeout=300
)
if proc.returncode != 0:
result["message"] = f"Failed to install {runtime}"
return result
# Configure podman registries if needed
if runtime == "podman":
registries_conf = "/etc/containers/registries.conf"
if os.path.exists("/etc/containers") and not os.path.exists(registries_conf):
try:
with open(registries_conf, 'w') as f:
f.write('unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"]\n')
print("[*] Configured container registries")
except Exception as e:
logger.warning(f"Could not configure registries: {e}")
# Verify installation
verify_cmd = shutil.which(runtime)
if verify_cmd:
print(f"\n[OK] {runtime.capitalize()} installed successfully!")
result["success"] = True
result["message"] = f"{runtime.capitalize()} installed successfully"
result["path"] = verify_cmd
else:
result["message"] = f"{runtime.capitalize()} installed but not found in PATH"
except subprocess.TimeoutExpired:
result["message"] = "Installation timed out"
except Exception as e:
logger.error(f"Failed to install runtime: {e}")
result["message"] = str(e)
return result
def ensure_runtime() -> Dict[str, Any]:
"""
Ensure a container runtime is available, installing if necessary.
Returns:
Dict with runtime info (same as detect_runtime)
"""
runtime_info = detect_runtime()
if runtime_info["available"]:
return runtime_info
# No runtime available, install podman
print("\n[!] No container runtime found. Installing Podman...")
install_result = install_runtime("podman")
if not install_result["success"]:
return {
"available": False,
"runtime": None,
"version": None,
"path": None,
"error": install_result["message"]
}
# Re-detect after installation
return detect_runtime()
# =================================================================
# Runtime Detection
# =================================================================
@@ -416,10 +564,10 @@ def deploy_app(app_id: str, config: Dict[str, Any], installed_by: str = "web") -
"app_id": app_id
}
# Check runtime
runtime_info = detect_runtime()
# Ensure runtime is available (install if necessary)
runtime_info = ensure_runtime()
if not runtime_info["available"]:
result["message"] = runtime_info.get("error", "No container runtime available")
result["message"] = runtime_info.get("error", "Failed to setup container runtime")
return result
runtime = runtime_info["runtime"]