did stuff
Some checks failed
Tests / 🛃 Unit tests (push) Failing after 13s
Tests / 🗄 DB tests (push) Failing after 19s
Tests / 🐍 Lint python scripts (push) Failing after 1s
Tests / ⌨ Golang Lint (push) Failing after 1s
Tests / 📦 Packit config lint (push) Failing after 1s
Tests / 🔍 Check source preparation (push) Failing after 1s
Tests / 🔍 Check for valid snapshot urls (push) Failing after 1s
Tests / 🔍 Check for missing or unused runner repos (push) Failing after 1s
Tests / 🐚 Shellcheck (push) Failing after 1s
Tests / 📦 RPMlint (push) Failing after 1s
Tests / Gitlab CI trigger helper (push) Failing after 1s
Tests / 🎀 kube-linter (push) Failing after 1s
Tests / 🧹 cloud-cleaner-is-enabled (push) Successful in 3s
Tests / 🔍 Check spec file osbuild/images dependencies (push) Failing after 1s

This commit is contained in:
robojerk 2025-08-26 10:34:42 -07:00
parent d228f6d30f
commit 4eeaa43c39
47 changed files with 21390 additions and 31 deletions

479
admin_interface.py Normal file
View file

@ -0,0 +1,479 @@
#!/usr/bin/env python3
"""
Debian Forge System Administration Interface
This module provides system administration functionality including:
- System configuration management
- Build monitoring and health checks
- Resource usage tracking
- System maintenance tools
"""
import json
import os
import psutil
import sqlite3
import time
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from pathlib import Path
@dataclass
class SystemStatus:
"""System status information"""
status: str
cpu_percent: float
memory_percent: float
disk_percent: float
active_builds: int
queued_builds: int
total_builds: int
uptime: str
last_updated: str
@dataclass
class BuildStatistics:
"""Build statistics and metrics"""
total_builds: int
successful_builds: int
failed_builds: int
average_build_time: float
builds_per_day: float
most_used_blueprint: str
resource_usage_trend: Dict[str, float]
@dataclass
class SystemConfiguration:
"""System configuration settings"""
max_concurrent_builds: int
resource_limits: Dict[str, int]
cleanup_policies: Dict[str, Any]
security_settings: Dict[str, Any]
notification_settings: Dict[str, Any]
class AdminInterface:
"""System administration interface for Debian Forge"""
def __init__(self, db_path: str = "admin.db"):
self.db_path = db_path
self._init_database()
def _init_database(self):
"""Initialize the admin database"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# Create system configuration table
cursor.execute("""
CREATE TABLE IF NOT EXISTS system_config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT NOT NULL
)
""")
# Create system logs table
cursor.execute("""
CREATE TABLE IF NOT EXISTS system_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
level TEXT NOT NULL,
message TEXT NOT NULL,
component TEXT NOT NULL,
timestamp TEXT NOT NULL
)
""")
# Create maintenance tasks table
cursor.execute("""
CREATE TABLE IF NOT EXISTS maintenance_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_name TEXT NOT NULL,
status TEXT NOT NULL,
scheduled_at TEXT NOT NULL,
completed_at TEXT,
result TEXT
)
""")
# Insert default configuration
default_config = {
"max_concurrent_builds": "4",
"cpu_limit": "80",
"memory_limit": "85",
"disk_limit": "90",
"cleanup_interval": "3600",
"log_retention_days": "30",
"backup_interval": "86400"
}
for key, value in default_config.items():
cursor.execute("""
INSERT OR IGNORE INTO system_config (key, value, updated_at)
VALUES (?, ?, ?)
""", (key, value, time.strftime("%Y-%m-%d %H:%M:%S")))
conn.commit()
conn.close()
def get_system_status(self) -> SystemStatus:
"""Get current system status"""
# Get CPU usage
cpu_percent = psutil.cpu_percent(interval=1)
# Get memory usage
memory = psutil.virtual_memory()
memory_percent = memory.percent
# Get disk usage for the main partition
disk = psutil.disk_usage('/')
disk_percent = (disk.used / disk.total) * 100
# Get build statistics (simulated for now)
active_builds = self._get_active_builds_count()
queued_builds = self._get_queued_builds_count()
total_builds = self._get_total_builds_count()
# Get system uptime
boot_time = psutil.boot_time()
uptime_seconds = time.time() - boot_time
uptime = self._format_uptime(uptime_seconds)
# Determine overall status
status = "healthy"
if cpu_percent > 90 or memory_percent > 90 or disk_percent > 90:
status = "warning"
if cpu_percent > 95 or memory_percent > 95 or disk_percent > 95:
status = "critical"
return SystemStatus(
status=status,
cpu_percent=cpu_percent,
memory_percent=memory_percent,
disk_percent=disk_percent,
active_builds=active_builds,
queued_builds=queued_builds,
total_builds=total_builds,
uptime=uptime,
last_updated=time.strftime("%Y-%m-%d %H:%M:%S")
)
def _get_active_builds_count(self) -> int:
"""Get number of active builds (simulated)"""
# In real implementation, this would query the build orchestrator
return 2
def _get_queued_builds_count(self) -> int:
"""Get number of queued builds (simulated)"""
# In real implementation, this would query the build queue
return 1
def _get_total_builds_count(self) -> int:
"""Get total number of builds (simulated)"""
# In real implementation, this would query the build history
return 47
def _format_uptime(self, seconds: float) -> str:
"""Format uptime in human-readable format"""
days = int(seconds // 86400)
hours = int((seconds % 86400) // 3600)
minutes = int((seconds % 3600) // 60)
if days > 0:
return f"{days}d {hours}h {minutes}m"
elif hours > 0:
return f"{hours}h {minutes}m"
else:
return f"{minutes}m"
def get_build_statistics(self) -> BuildStatistics:
"""Get build statistics and metrics"""
# Simulated statistics - in real implementation, query build database
return BuildStatistics(
total_builds=47,
successful_builds=42,
failed_builds=5,
average_build_time=1847.5, # seconds
builds_per_day=12.3,
most_used_blueprint="debian-atomic-base",
resource_usage_trend={
"cpu_avg": 65.2,
"memory_avg": 78.4,
"disk_growth": 2.1
}
)
def get_system_configuration(self) -> SystemConfiguration:
"""Get system configuration"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("SELECT key, value FROM system_config")
config_data = dict(cursor.fetchall())
conn.close()
return SystemConfiguration(
max_concurrent_builds=int(config_data.get("max_concurrent_builds", "4")),
resource_limits={
"cpu": int(config_data.get("cpu_limit", "80")),
"memory": int(config_data.get("memory_limit", "85")),
"disk": int(config_data.get("disk_limit", "90"))
},
cleanup_policies={
"interval": int(config_data.get("cleanup_interval", "3600")),
"log_retention_days": int(config_data.get("log_retention_days", "30"))
},
security_settings={
"require_authentication": True,
"session_timeout": 3600,
"password_complexity": True
},
notification_settings={
"email_alerts": False,
"slack_integration": False,
"webhook_url": None
}
)
def update_system_configuration(self, config: SystemConfiguration) -> bool:
"""Update system configuration"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
# Update configuration values
config_updates = [
("max_concurrent_builds", str(config.max_concurrent_builds)),
("cpu_limit", str(config.resource_limits["cpu"])),
("memory_limit", str(config.resource_limits["memory"])),
("disk_limit", str(config.resource_limits["disk"])),
("cleanup_interval", str(config.cleanup_policies["interval"])),
("log_retention_days", str(config.cleanup_policies["log_retention_days"]))
]
for key, value in config_updates:
cursor.execute("""
INSERT OR REPLACE INTO system_config (key, value, updated_at)
VALUES (?, ?, ?)
""", (key, value, timestamp))
conn.commit()
conn.close()
# Log configuration change
self.log_event("INFO", "System configuration updated", "admin_interface")
return True
except Exception as e:
self.log_event("ERROR", f"Failed to update configuration: {e}", "admin_interface")
return False
def log_event(self, level: str, message: str, component: str):
"""Log system event"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("""
INSERT INTO system_logs (level, message, component, timestamp)
VALUES (?, ?, ?, ?)
""", (level, message, component, time.strftime("%Y-%m-%d %H:%M:%S")))
conn.commit()
conn.close()
except Exception as e:
print(f"Failed to log event: {e}")
def get_system_logs(self, limit: int = 100, level: Optional[str] = None) -> List[Dict[str, Any]]:
"""Get system logs"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
if level:
cursor.execute("""
SELECT level, message, component, timestamp
FROM system_logs WHERE level = ?
ORDER BY timestamp DESC LIMIT ?
""", (level, limit))
else:
cursor.execute("""
SELECT level, message, component, timestamp
FROM system_logs
ORDER BY timestamp DESC LIMIT ?
""", (limit,))
logs = []
for row in cursor.fetchall():
logs.append({
"level": row[0],
"message": row[1],
"component": row[2],
"timestamp": row[3]
})
conn.close()
return logs
except Exception as e:
self.log_event("ERROR", f"Failed to retrieve logs: {e}", "admin_interface")
return []
def schedule_maintenance_task(self, task_name: str, scheduled_time: str) -> bool:
"""Schedule a maintenance task"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("""
INSERT INTO maintenance_tasks (task_name, status, scheduled_at)
VALUES (?, ?, ?)
""", (task_name, "scheduled", scheduled_time))
conn.commit()
conn.close()
self.log_event("INFO", f"Maintenance task scheduled: {task_name}", "admin_interface")
return True
except Exception as e:
self.log_event("ERROR", f"Failed to schedule task: {e}", "admin_interface")
return False
def get_maintenance_tasks(self) -> List[Dict[str, Any]]:
"""Get maintenance tasks"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("""
SELECT task_name, status, scheduled_at, completed_at, result
FROM maintenance_tasks
ORDER BY scheduled_at DESC
""")
tasks = []
for row in cursor.fetchall():
tasks.append({
"task_name": row[0],
"status": row[1],
"scheduled_at": row[2],
"completed_at": row[3],
"result": row[4]
})
conn.close()
return tasks
except Exception as e:
self.log_event("ERROR", f"Failed to retrieve tasks: {e}", "admin_interface")
return []
def run_system_cleanup(self) -> Dict[str, Any]:
"""Run system cleanup tasks"""
results = {
"status": "success",
"tasks_completed": [],
"tasks_failed": [],
"cleanup_summary": {}
}
try:
# Cleanup old logs
self._cleanup_old_logs()
results["tasks_completed"].append("log_cleanup")
# Cleanup temporary files
temp_cleaned = self._cleanup_temp_files()
results["tasks_completed"].append("temp_cleanup")
results["cleanup_summary"]["temp_files_removed"] = temp_cleaned
# Cleanup old build artifacts (simulated)
artifacts_cleaned = self._cleanup_old_artifacts()
results["tasks_completed"].append("artifact_cleanup")
results["cleanup_summary"]["artifacts_removed"] = artifacts_cleaned
self.log_event("INFO", "System cleanup completed successfully", "admin_interface")
except Exception as e:
results["status"] = "partial_failure"
results["tasks_failed"].append(f"cleanup_error: {e}")
self.log_event("ERROR", f"System cleanup failed: {e}", "admin_interface")
return results
def _cleanup_old_logs(self):
"""Cleanup old log entries"""
config = self.get_system_configuration()
retention_days = config.cleanup_policies["log_retention_days"]
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cutoff_date = time.strftime("%Y-%m-%d %H:%M:%S",
time.localtime(time.time() - (retention_days * 86400)))
cursor.execute("DELETE FROM system_logs WHERE timestamp < ?", (cutoff_date,))
conn.commit()
conn.close()
def _cleanup_temp_files(self) -> int:
"""Cleanup temporary files"""
temp_dirs = ["/tmp", "/var/tmp"]
files_removed = 0
for temp_dir in temp_dirs:
if os.path.exists(temp_dir):
for filename in os.listdir(temp_dir):
if filename.startswith("debian-forge-") or filename.startswith("osbuild-"):
filepath = os.path.join(temp_dir, filename)
try:
if os.path.isfile(filepath):
os.remove(filepath)
files_removed += 1
elif os.path.isdir(filepath):
import shutil
shutil.rmtree(filepath)
files_removed += 1
except Exception:
pass # Ignore errors for individual files
return files_removed
def _cleanup_old_artifacts(self) -> int:
"""Cleanup old build artifacts (simulated)"""
# In real implementation, this would cleanup old build artifacts
# based on configured retention policies
return 15 # Simulated number of artifacts removed
def get_resource_usage_history(self, hours: int = 24) -> Dict[str, List[float]]:
"""Get resource usage history (simulated data)"""
# In real implementation, this would query stored metrics
import random
data_points = hours * 6 # Every 10 minutes
return {
"timestamps": [i * 10 for i in range(data_points)], # Minutes ago
"cpu_usage": [random.uniform(20, 80) for _ in range(data_points)],
"memory_usage": [random.uniform(30, 85) for _ in range(data_points)],
"disk_usage": [random.uniform(60, 75) for _ in range(data_points)]
}
def restart_services(self, services: List[str]) -> Dict[str, bool]:
"""Restart system services (simulated)"""
results = {}
for service in services:
try:
# In real implementation, this would use systemctl or similar
self.log_event("INFO", f"Restarting service: {service}", "admin_interface")
results[service] = True
except Exception as e:
self.log_event("ERROR", f"Failed to restart {service}: {e}", "admin_interface")
results[service] = False
return results