#!/bin/bash # 14-admin-utilities.sh - Admin Utilities for Particle-OS apt-layer # Provides system health monitoring, performance analytics, and admin tools # --- Color and Symbols --- GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' CYAN='\033[0;36m' NC='\033[0m' CHECK="â" WARN="â ï¸ " CROSS="â" INFO="â¹ï¸ " # --- Helper: Check for WSL --- is_wsl() { grep -qi microsoft /proc/version 2>/dev/null } get_wsl_version() { if is_wsl; then if grep -q WSL2 /proc/version 2>/dev/null; then echo "WSL2" else echo "WSL1" fi fi } # --- System Health Monitoring --- health_check() { local health_status=0 echo -e "${CYAN}================= System Health Check =================${NC}" echo -e "${INFO} Hostname: $(hostname 2>/dev/null || echo N/A)" echo -e "${INFO} Uptime: $(uptime -p 2>/dev/null || echo N/A)" echo -e "${INFO} Kernel: $(uname -r 2>/dev/null || echo N/A)" if is_wsl; then echo -e "${INFO} WSL: $(get_wsl_version)" fi echo -e "${INFO} Load Avg: $(awk '{print $1, $2, $3}' /proc/loadavg 2>/dev/null || echo N/A)" # CPU Info if command -v lscpu &>/dev/null; then cpu_model=$(lscpu | grep 'Model name' | awk -F: '{print $2}' | xargs) cpu_cores=$(lscpu | grep '^CPU(s):' | awk '{print $2}') echo -e "${INFO} CPU: $cpu_model ($cpu_cores cores)" else echo -e "${WARN} CPU: lscpu not available" health_status=1 fi # Memory if command -v free &>/dev/null; then mem_line=$(free -m | grep Mem) mem_total=$(echo $mem_line | awk '{print $2}') mem_used=$(echo $mem_line | awk '{print $3}') mem_free=$(echo $mem_line | awk '{print $4}') mem_perc=$((100 * mem_used / mem_total)) echo -e "${INFO} Memory: ${mem_total}MiB total, ${mem_used}MiB used (${mem_perc}%)" else echo -e "${WARN} Memory: free not available" health_status=1 fi # Disk if command -v df &>/dev/null; then disk_root=$(df -h / | tail -1) disk_total=$(echo $disk_root | awk '{print $2}') disk_used=$(echo $disk_root | awk '{print $3}') disk_avail=$(echo $disk_root | awk '{print $4}') disk_perc=$(echo $disk_root | awk '{print $5}') echo -e "${INFO} Disk /: $disk_total total, $disk_used used, $disk_avail free ($disk_perc)" if [ -d /var/lib/particle-os ]; then disk_ublue=$(df -h /var/lib/particle-os 2>/dev/null | tail -1) if [ -n "$disk_ublue" ]; then ublue_total=$(echo $disk_ublue | awk '{print $2}') ublue_used=$(echo $disk_ublue | awk '{print $3}') ublue_avail=$(echo $disk_ublue | awk '{print $4}') ublue_perc=$(echo $disk_ublue | awk '{print $5}') echo -e "${INFO} Disk /var/lib/particle-os: $ublue_total total, $ublue_used used, $ublue_avail free ($ublue_perc)" fi fi else echo -e "${WARN} Disk: df not available" health_status=1 fi # OverlayFS/ComposeFS overlays=$(mount | grep overlay | wc -l) composefs=$(mount | grep composefs | wc -l) echo -e "${INFO} OverlayFS: $overlays overlays mounted" echo -e "${INFO} ComposeFS: $composefs composefs mounted" # Bootloader if command -v bootctl &>/dev/null; then boot_status=$(bootctl status 2>/dev/null | grep 'System:' | xargs) echo -e "${INFO} Bootloader: ${boot_status:-N/A}" else echo -e "${WARN} Bootloader: bootctl not available" fi # Security if command -v apparmor_status &>/dev/null; then sec_status=$(apparmor_status | grep 'profiles are in enforce mode' || echo 'N/A') echo -e "${INFO} Security: $sec_status" else echo -e "${WARN} Security: apparmor_status not available" fi # Layer Integrity/Deployment echo -e "${CYAN}-----------------------------------------------------${NC}" echo -e "${INFO} Layer Integrity: [Coming soon] (future: check layer hashes)" echo -e "${INFO} Deployment Status: [Coming soon] (future: show active deployments)" # Top processes echo -e "${CYAN}---------------- Top 3 Processes ---------------------${NC}" if command -v ps &>/dev/null; then echo -e "${INFO} By CPU:" ps -eo pid,comm,%cpu --sort=-%cpu | head -n 4 | tail -n 3 | awk '{printf " PID: %-6s %-20s CPU: %s%%\n", $1, $2, $3}' echo -e "${INFO} By MEM:" ps -eo pid,comm,%mem --sort=-%mem | head -n 4 | tail -n 3 | awk '{printf " PID: %-6s %-20s MEM: %s%%\n", $1, $2, $3}' else echo -e "${WARN} ps not available for process listing" fi echo -e "${CYAN}-----------------------------------------------------${NC}" # Summary if [ $health_status -eq 0 ]; then echo -e "${GREEN}${CHECK} System health: OK${NC}" else echo -e "${YELLOW}${WARN} System health: WARNING (see above)${NC}" fi echo -e "${CYAN}=====================================================${NC}" } # --- Performance Analytics --- performance_report() { echo -e "${CYAN}=============== Performance Analytics ===============${NC}" echo -e "${INFO} Layer creation time (last 5): [Coming soon] (future: show timing logs)" echo -e "${INFO} Resource usage (CPU/mem): [Coming soon] (future: show resource stats)" if command -v iostat &>/dev/null; then echo -e "${INFO} Disk I/O stats:" iostat | grep -A1 Device | tail -n +2 else echo -e "${WARN} Disk I/O stats: iostat not available" fi echo -e "${INFO} Historical trends: [Coming soon] (future: show trends if data available)" echo -e "${CYAN}=====================================================${NC}" } # --- Automated Maintenance --- admin_cleanup() { # Defaults local days=30 local dry_run=false local keep_recent=2 local DEPLOYMENTS_DIR="/var/lib/particle-os/deployments" local LOGS_DIR="/var/log/apt-layer" local BACKUPS_DIR="/var/lib/particle-os/backups" # Load config from JSON if available local config_file="$(dirname "${BASH_SOURCE[0]}")/../config/maintenance.json" if [ -f "$config_file" ] && command -v jq &>/dev/null; then days=$(jq -r '.retention_days // 30' "$config_file") keep_recent=$(jq -r '.keep_recent // 2' "$config_file") DEPLOYMENTS_DIR=$(jq -r '.deployments_dir // "/var/lib/particle-os/deployments"' "$config_file") LOGS_DIR=$(jq -r '.logs_dir // "/var/log/apt-layer"' "$config_file") BACKUPS_DIR=$(jq -r '.backups_dir // "/var/lib/particle-os/backups"' "$config_file") fi # Parse arguments (override config) while [[ $# -gt 0 ]]; do case $1 in --days|-d) days="$2"; shift 2;; --dry-run) dry_run=true; shift;; --keep-recent) keep_recent="$2"; shift 2;; --deployments-dir) DEPLOYMENTS_DIR="$2"; shift 2;; --logs-dir) LOGS_DIR="$2"; shift 2;; --backups-dir) BACKUPS_DIR="$2"; shift 2;; --schedule) echo -e "${YELLOW}${WARN} Scheduled cleanup: Not yet implemented (will use systemd/cron)${NC}"; return;; *) shift;; esac done echo -e "${CYAN}--- Automated Maintenance Cleanup ---${NC}" echo -e "${INFO} Retention: $days days" echo -e "${INFO} Keep recent: $keep_recent items" echo -e "${INFO} Deployments dir: $DEPLOYMENTS_DIR" echo -e "${INFO} Logs dir: $LOGS_DIR" echo -e "${INFO} Backups dir: $BACKUPS_DIR" if [ "$dry_run" = true ]; then echo -e "${YELLOW}${WARN} DRY RUN MODE - No files will be deleted${NC}" fi local total_deleted=0 # Helper function to cleanup directory cleanup_directory() { local dir="$1" local description="$2" local deleted_count=0 if [ ! -d "$dir" ]; then echo -e "${INFO} $description: Directory does not exist, skipping" return fi echo -e "${INFO} $description: Scanning $dir" # Get list of files/directories older than retention period local old_items=() if command -v find &>/dev/null; then while IFS= read -r -d '' item; do old_items+=("$item") done < <(find "$dir" -maxdepth 1 -type f -o -type d -mtime +$days -print0 2>/dev/null) fi # Remove the most recent items from deletion list if [ ${#old_items[@]} -gt 0 ] && [ $keep_recent -gt 0 ]; then # Sort by modification time (newest first) and keep the most recent local sorted_items=($(printf '%s\n' "${old_items[@]}" | xargs -I {} stat -c '%Y %n' {} 2>/dev/null | sort -nr | tail -n +$((keep_recent + 1)) | awk '{print $2}')) old_items=("${sorted_items[@]}") fi if [ ${#old_items[@]} -eq 0 ]; then echo -e "${INFO} $description: No items to delete" return fi echo -e "${INFO} $description: Found ${#old_items[@]} items to delete" for item in "${old_items[@]}"; do if [ "$dry_run" = true ]; then echo -e " ${YELLOW}Would delete: $item${NC}" else if rm -rf "$item" 2>/dev/null; then echo -e " ${GREEN}Deleted: $item${NC}" ((deleted_count++)) else echo -e " ${RED}Failed to delete: $item${NC}" fi fi done if [ "$dry_run" = false ]; then total_deleted=$((total_deleted + deleted_count)) fi } # Cleanup each directory cleanup_directory "$DEPLOYMENTS_DIR" "Deployments" cleanup_directory "$LOGS_DIR" "Logs" cleanup_directory "$BACKUPS_DIR" "Backups" # Summary if [ "$dry_run" = true ]; then echo -e "${YELLOW}${WARN} Dry run completed - no files were deleted${NC}" else echo -e "${GREEN}${CHECK} Cleanup complete - $total_deleted items deleted${NC}" fi echo -e "${CYAN}-------------------------------------${NC}" } # --- Backup/Restore (Stub) --- admin_backup() { echo -e "${YELLOW}${WARN} Backup: Not yet implemented${NC}" } admin_restore() { echo -e "${YELLOW}${WARN} Restore: Not yet implemented${NC}" } # --- Command Dispatch --- admin_utilities_main() { case "${1:-}" in health|health-check) health_check ;; perf|performance|analytics) performance_report ;; cleanup) shift admin_cleanup "$@" ;; backup) admin_backup ;; restore) admin_restore ;; help|--help|-h|"") echo -e "${CYAN}Admin Utilities Commands:${NC}" echo -e " ${GREEN}health${NC} - System health check" echo -e " ${GREEN}perf${NC} - Performance analytics" echo -e " ${GREEN}cleanup${NC} - Maintenance cleanup (--days N, --dry-run, --keep-recent N)" echo -e " ${GREEN}backup${NC} - Backup configs/layers (stub)" echo -e " ${GREEN}restore${NC} - Restore from backup (stub)" echo -e " ${GREEN}help${NC} - Show this help message" ;; *) echo -e "${RED}${CROSS} Unknown admin command: $1${NC}" admin_utilities_main help ;; esac }