diff --git a/baseTemplate/views.py b/baseTemplate/views.py index 90622e843..aee6450b5 100644 --- a/baseTemplate/views.py +++ b/baseTemplate/views.py @@ -537,12 +537,17 @@ def RestartCyberPanel(request): def getDashboardStats(request): try: - val = request.session['userID'] + val = request.session.get('userID') + if val is None: + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Session required'}), + content_type='application/json' + ) currentACL = ACLManager.loadedACL(val) admin = Administrator.objects.get(pk=val) # Check if user is admin - if currentACL['admin'] == 1: + if currentACL.get('admin', 0) == 1: # Admin can see all resources total_users = Administrator.objects.count() total_sites = Websites.objects.count() @@ -580,7 +585,7 @@ def getDashboardStats(request): total_emails = EUsers.objects.filter(emailOwner__domainOwner__domain__in=website_names).count() # Count FTP users associated with user's domains - total_ftp_users = FTPUsers.objects.filter(domain__in=website_names).count() + total_ftp_users = FTPUsers.objects.filter(domain__domain__in=website_names).count() else: total_wp_sites = 0 total_dbs = 0 @@ -598,18 +603,24 @@ def getDashboardStats(request): } return HttpResponse(json.dumps(data), content_type='application/json') except Exception as e: - return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + logging.writeToFile('getDashboardStats error: %s' % str(e)) + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Failed to load dashboard stats'}), + content_type='application/json' + ) def getTrafficStats(request): try: - val = request.session['userID'] + val = request.session.get('userID') + if val is None: + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Session required'}), + content_type='application/json' + ) currentACL = ACLManager.loadedACL(val) - - # Only admins should see system-wide network stats if not currentACL.get('admin', 0): return HttpResponse(json.dumps({'status': 0, 'error_message': 'Admin access required', 'admin_only': True}), content_type='application/json') - # Get network stats from /proc/net/dev (Linux) rx = tx = 0 with open('/proc/net/dev', 'r') as f: for line in f.readlines(): @@ -617,42 +628,49 @@ def getTrafficStats(request): continue if ':' in line: parts = line.split() - rx += int(parts[1]) - tx += int(parts[9]) - data = { - 'rx_bytes': rx, - 'tx_bytes': tx, - 'status': 1 - } + try: + if len(parts) >= 10: + rx += int(parts[1]) + tx += int(parts[9]) + except (ValueError, IndexError): + continue + data = {'rx_bytes': rx, 'tx_bytes': tx, 'status': 1} return HttpResponse(json.dumps(data), content_type='application/json') except Exception as e: - return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + logging.writeToFile('getTrafficStats error: %s' % str(e)) + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Failed to load traffic stats'}), + content_type='application/json' + ) def getDiskIOStats(request): try: - val = request.session['userID'] + val = request.session.get('userID') + if val is None: + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Session required'}), + content_type='application/json' + ) currentACL = ACLManager.loadedACL(val) - - # Only admins should see system-wide disk I/O stats if not currentACL.get('admin', 0): return HttpResponse(json.dumps({'status': 0, 'error_message': 'Admin access required', 'admin_only': True}), content_type='application/json') - # Parse /proc/diskstats for all disks read_sectors = 0 write_sectors = 0 - sector_size = 512 # Most Linux systems use 512 bytes per sector + sector_size = 512 with open('/proc/diskstats', 'r') as f: for line in f: parts = line.split() if len(parts) < 14: continue - # parts[2] is device name, skip loopback/ram devices dev = parts[2] if dev.startswith('loop') or dev.startswith('ram'): continue - # 6th and 10th columns: sectors read/written - read_sectors += int(parts[5]) - write_sectors += int(parts[9]) + try: + read_sectors += int(parts[5]) + write_sectors += int(parts[9]) + except (ValueError, IndexError): + continue data = { 'read_bytes': read_sectors * sector_size, 'write_bytes': write_sectors * sector_size, @@ -660,34 +678,42 @@ def getDiskIOStats(request): } return HttpResponse(json.dumps(data), content_type='application/json') except Exception as e: - return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + logging.writeToFile('getDiskIOStats error: %s' % str(e)) + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Failed to load disk I/O stats'}), + content_type='application/json' + ) def getCPULoadGraph(request): try: - val = request.session['userID'] + val = request.session.get('userID') + if val is None: + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Session required'}), + content_type='application/json' + ) currentACL = ACLManager.loadedACL(val) - - # Only admins should see system-wide CPU stats if not currentACL.get('admin', 0): return HttpResponse(json.dumps({'status': 0, 'error_message': 'Admin access required', 'admin_only': True}), content_type='application/json') - # Parse /proc/stat for the 'cpu' line + cpu_times = [] with open('/proc/stat', 'r') as f: for line in f: if line.startswith('cpu '): parts = line.strip().split() - # parts[1:] are user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice - cpu_times = [float(x) for x in parts[1:]] + try: + cpu_times = [float(x) for x in parts[1:]] + except (ValueError, IndexError): + pass break - else: - cpu_times = [] - data = { - 'cpu_times': cpu_times, - 'status': 1 - } + data = {'cpu_times': cpu_times, 'status': 1} return HttpResponse(json.dumps(data), content_type='application/json') except Exception as e: - return HttpResponse(json.dumps({'status': 0, 'error_message': str(e)}), content_type='application/json') + logging.writeToFile('getCPULoadGraph error: %s' % str(e)) + return HttpResponse( + json.dumps({'status': 0, 'error_message': 'Failed to load CPU stats'}), + content_type='application/json' + ) @csrf_exempt @require_GET diff --git a/install.sh b/install.sh index 18088684d..94be2eff8 100644 --- a/install.sh +++ b/install.sh @@ -4,10 +4,141 @@ # This installer uses modules for better organization and maintainability # Each module is kept under 500 lines for easy management +# Note: We use 'set -e' carefully - some operations need to handle failures gracefully set -e # Get script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Early logging function (before main functions are defined) +early_log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" +} + +# Check if script is being executed via curl/wget (SCRIPT_DIR will be /dev/fd/...) +# In that case, we need to clone the repository first +if [[ "$SCRIPT_DIR" == /dev/fd/* ]] || [[ ! -d "$SCRIPT_DIR/modules" ]]; then + # Script is being executed via curl/wget, need to clone repo first + early_log "📥 Detected curl/wget execution - cloning repository..." + + # Determine branch from arguments or use default + BRANCH_NAME="v2.5.5-dev" + ARGS=("$@") + for i in "${!ARGS[@]}"; do + if [[ "${ARGS[i]}" == "-b" ]] || [[ "${ARGS[i]}" == "--branch" ]]; then + if [[ -n "${ARGS[i+1]}" ]]; then + BRANCH_NAME="${ARGS[i+1]}" + fi + break + fi + done + + # Clone repository to temporary directory + # Try multiple locations if /tmp is full + TEMP_DIR="" + early_log "Checking available disk space..." + + # Try to clean up old temp directories first + rm -rf /tmp/cyberpanel-installer-* 2>/dev/null || true + rm -rf /var/tmp/cyberpanel-installer-* 2>/dev/null || true + + # Try multiple temp locations (disable set -e for this section) + set +e + for temp_location in "/tmp/cyberpanel-installer-$$" "/var/tmp/cyberpanel-installer-$$" "$HOME/cyberpanel-installer-$$" "/root/cyberpanel-installer-$$"; do + if mkdir -p "$temp_location" 2>/dev/null; then + TEMP_DIR="$temp_location" + early_log "Using temporary directory: $TEMP_DIR" + break + fi + done + set -e + + # If we still don't have a temp dir, skip git clone and use fallback + if [ -z "$TEMP_DIR" ]; then + early_log "⚠️ Cannot create temporary directory (disk may be full)" + early_log "Skipping git clone, using fallback installer directly..." + # Skip to fallback installer + TEMP_DIR="" + fi + + # Only attempt git clone if we have a temp directory + if [ -n "$TEMP_DIR" ]; then + early_log "📦 Cloning CyberPanel repository (branch: $BRANCH_NAME)..." + early_log "This may take a minute depending on your connection speed..." + + # Try git clone with timeout and better error handling + GIT_CLONE_SUCCESS=false + + if command -v timeout >/dev/null 2>&1; then + if timeout 180 git clone --depth 1 --branch "$BRANCH_NAME" https://github.com/master3395/cyberpanel.git "$TEMP_DIR/cyberpanel" >/tmp/git-clone.log 2>&1; then + GIT_CLONE_SUCCESS=true + fi + else + # No timeout command, try without timeout + if git clone --depth 1 --branch "$BRANCH_NAME" https://github.com/master3395/cyberpanel.git "$TEMP_DIR/cyberpanel" >/tmp/git-clone.log 2>&1; then + GIT_CLONE_SUCCESS=true + fi + fi + + # Verify clone was successful and structure is valid + if [ "$GIT_CLONE_SUCCESS" = true ] && [ -d "$TEMP_DIR/cyberpanel" ] && [ -f "$TEMP_DIR/cyberpanel/install.sh" ] && [ -d "$TEMP_DIR/cyberpanel/modules" ]; then + SCRIPT_DIR="$TEMP_DIR/cyberpanel" + cd "$SCRIPT_DIR" + early_log "✅ Repository cloned successfully" + else + # Git clone failed or structure invalid, use fallback + GIT_ERROR=$(tail -3 /tmp/git-clone.log 2>/dev/null | tr '\n' ' ') + early_log "⚠️ Git clone failed or incomplete" + if [ -n "$GIT_ERROR" ]; then + early_log "Last error: $GIT_ERROR" + fi + early_log "Falling back to legacy installer..." + rm -rf "$TEMP_DIR/cyberpanel" 2>/dev/null || true + TEMP_DIR="" # Clear TEMP_DIR to trigger fallback + fi + fi + + # If git clone failed or we couldn't create temp dir, use fallback + if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR/cyberpanel" ] || [ ! -f "$TEMP_DIR/cyberpanel/install.sh" ]; then + # Fallback: try to download install.sh from the old method + early_log "Using fallback installer method..." + OUTPUT=$(cat /etc/*release 2>/dev/null || echo "") + if echo "$OUTPUT" | grep -qE "(CentOS|AlmaLinux|CloudLinux|Rocky)" ; then + SERVER_OS="CentOS8" + yum install -y -q curl wget 2>/dev/null || dnf install -y -q curl wget 2>/dev/null || true + elif echo "$OUTPUT" | grep -qE "Ubuntu" ; then + SERVER_OS="Ubuntu" + apt install -y -qq wget curl 2>/dev/null || true + else + early_log "❌ Unable to detect OS for fallback installer" + exit 1 + fi + + # Try multiple locations for fallback script + FALLBACK_SCRIPT="" + for fallback_location in "/tmp/cyberpanel.sh" "/var/tmp/cyberpanel.sh" "$HOME/cyberpanel.sh"; do + rm -f "$fallback_location" 2>/dev/null || true + if curl --silent -o "$fallback_location" "https://cyberpanel.sh/?dl&$SERVER_OS" 2>/dev/null || \ + wget -q -O "$fallback_location" "https://cyberpanel.sh/?dl&$SERVER_OS" 2>/dev/null; then + if [ -f "$fallback_location" ]; then + FALLBACK_SCRIPT="$fallback_location" + chmod +x "$FALLBACK_SCRIPT" + break + fi + fi + done + + if [ -n "$FALLBACK_SCRIPT" ] && [ -f "$FALLBACK_SCRIPT" ]; then + early_log "✅ Fallback installer downloaded, executing..." + exec "$FALLBACK_SCRIPT" "$@" + else + early_log "❌ Failed to download fallback installer" + early_log "Please free up disk space or check your internet connection" + exit 1 + fi + fi +fi # Close the if [[ "$SCRIPT_DIR" == /dev/fd/* ]] block + MODULES_DIR="$SCRIPT_DIR/modules" # Colors for output @@ -88,11 +219,22 @@ detect_operating_system() { print_status "$BLUE" "🔍 Detecting operating system..." if detect_os; then - # Get OS information + # Get OS information using get_os_info() to ensure we capture the values + # This outputs variable assignments that we can eval eval $(get_os_info) + + # Export them to ensure they're available to all functions + export SERVER_OS OS_FAMILY PACKAGE_MANAGER ARCHITECTURE + print_status "$GREEN" "✅ OS detected: $SERVER_OS ($OS_FAMILY)" print_status "$GREEN" "✅ Package manager: $PACKAGE_MANAGER" print_status "$GREEN" "✅ Architecture: $ARCHITECTURE" + + # Verify variables are set + if [ -z "$SERVER_OS" ] || [ -z "$OS_FAMILY" ] || [ -z "$PACKAGE_MANAGER" ]; then + print_status "$RED" "❌ OS variables not properly set after detection" + return 1 + fi return 0 else print_status "$RED" "❌ Failed to detect operating system" @@ -104,6 +246,31 @@ detect_operating_system() { install_dependencies() { print_status "$BLUE" "📦 Installing dependencies..." + # Debug: Show current variable values + print_status "$YELLOW" "DEBUG: SERVER_OS='$SERVER_OS', OS_FAMILY='$OS_FAMILY', PACKAGE_MANAGER='$PACKAGE_MANAGER'" + + # Ensure variables are set (they should be from detect_operating_system) + if [ -z "$SERVER_OS" ] || [ -z "$OS_FAMILY" ] || [ -z "$PACKAGE_MANAGER" ]; then + print_status "$YELLOW" "⚠️ OS variables not set, re-detecting..." + # Try to get them again + if detect_os; then + # Variables should be set by detect_os() in the sourced module + export SERVER_OS OS_FAMILY PACKAGE_MANAGER ARCHITECTURE + print_status "$GREEN" "✅ Re-detected: SERVER_OS=$SERVER_OS, OS_FAMILY=$OS_FAMILY, PACKAGE_MANAGER=$PACKAGE_MANAGER" + else + print_status "$RED" "❌ Failed to detect OS for dependency installation" + return 1 + fi + fi + + # Verify variables one more time before calling + if [ -z "$SERVER_OS" ] || [ -z "$OS_FAMILY" ] || [ -z "$PACKAGE_MANAGER" ]; then + print_status "$RED" "❌ OS variables still not set after re-detection" + return 1 + fi + + print_status "$BLUE" "Calling manage_dependencies with: SERVER_OS='$SERVER_OS', OS_FAMILY='$OS_FAMILY', PACKAGE_MANAGER='$PACKAGE_MANAGER'" + if manage_dependencies "$SERVER_OS" "$OS_FAMILY" "$PACKAGE_MANAGER"; then print_status "$GREEN" "✅ Dependencies installed successfully" return 0 @@ -214,6 +381,42 @@ parse_arguments() { done } +# Function to check disk space +check_disk_space() { + local required_gb=10 # Minimum 10GB required + local root_available_gb=0 + + # Get available space on root filesystem + if command -v df >/dev/null 2>&1; then + root_available_gb=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//' | cut -d. -f1) + if [ -z "$root_available_gb" ] || [ "$root_available_gb" = "" ]; then + # Try alternative method + root_available_gb=$(df -BG / | tail -1 | awk '{print $4}' | sed 's/G//' | cut -d. -f1) + fi + fi + + # If we couldn't get the value, try without -BG flag + if [ -z "$root_available_gb" ] || ! [[ "$root_available_gb" =~ ^[0-9]+$ ]]; then + root_available_gb=$(df / | awk 'NR==2 {print $4}' | awk '{printf "%.0f", $1/1024/1024}') + fi + + print_status "$BLUE" "💾 Checking disk space..." + print_status "$BLUE" " Required: ${required_gb}GB minimum" + + if [[ "$root_available_gb" =~ ^[0-9]+$ ]] && [ "$root_available_gb" -ge "$required_gb" ]; then + print_status "$GREEN" " Available: ${root_available_gb}GB (✅ Sufficient)" + return 0 + elif [[ "$root_available_gb" =~ ^[0-9]+$ ]]; then + print_status "$YELLOW" " Available: ${root_available_gb}GB (⚠️ Less than ${required_gb}GB recommended)" + print_status "$YELLOW" " Installation may fail if disk space runs out" + return 1 + else + print_status "$YELLOW" " Could not determine available disk space" + print_status "$YELLOW" " Please ensure at least ${required_gb}GB is available" + return 1 + fi +} + # Main installation function main() { # Initialize log file @@ -223,6 +426,9 @@ main() { print_status "$BLUE" "🚀 Enhanced CyberPanel Installer Starting..." print_status "$BLUE" "Log file: /var/log/cyberpanel_install.log" + # Check disk space before proceeding + check_disk_space + # Parse command line arguments parse_arguments "$@" @@ -251,4 +457,4 @@ main() { } # Run main function -main "$@" \ No newline at end of file +main "$@" diff --git a/install/install.py b/install/install.py index 1402a8bb9..e2ab05e25 100644 --- a/install/install.py +++ b/install/install.py @@ -1451,9 +1451,189 @@ module cyberpanel_ols { except: return False + def checkExistingMariaDB(self): + """Check if MariaDB/MySQL is already installed and return version info""" + try: + # Check if MariaDB/MySQL server package is installed + if self.distro == ubuntu: + command = 'dpkg -l | grep -iE "^(ii|rc).*mariadb-server|mysql-server" 2>/dev/null || echo ""' + else: + command = 'rpm -qa | grep -iE "^(mariadb-server|mysql-server|MariaDB-server)" 2>/dev/null || echo ""' + + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + + if result.stdout.strip(): + # MariaDB/MySQL server package is installed, get version + version_command = 'mysql --version 2>/dev/null || mariadb --version 2>/dev/null || echo ""' + version_result = subprocess.run(version_command, shell=True, capture_output=True, universal_newlines=True) + version_output = version_result.stdout.strip() + + if version_output: + # Extract version number (e.g., "10.11.15" from "mysql Ver 10.11.15-MariaDB") + import re + version_match = re.search(r'(\d+\.\d+\.\d+)', version_output) + if version_match: + installed_version = version_match.group(1) + major_minor = '.'.join(installed_version.split('.')[:2]) # e.g., "10.11" + logging.InstallLog.writeToFile(f"Found existing MariaDB installation: {installed_version} (major.minor: {major_minor})") + return True, installed_version, major_minor + + logging.InstallLog.writeToFile("Found MariaDB/MySQL package but could not determine version") + return True, "unknown", "unknown" + + # Also check if MariaDB service exists and data directory exists (might be installed but package query failed) + if os.path.exists('/var/lib/mysql') and os.listdir('/var/lib/mysql'): + logging.InstallLog.writeToFile("Found MariaDB data directory, assuming MariaDB is installed") + # Try to get version one more time + version_command = 'mysql --version 2>/dev/null || mariadb --version 2>/dev/null || echo ""' + version_result = subprocess.run(version_command, shell=True, capture_output=True, universal_newlines=True) + version_output = version_result.stdout.strip() + if version_output: + import re + version_match = re.search(r'(\d+\.\d+\.\d+)', version_output) + if version_match: + installed_version = version_match.group(1) + major_minor = '.'.join(installed_version.split('.')[:2]) + return True, installed_version, major_minor + return True, "unknown", "unknown" + + return False, None, None + except Exception as e: + logging.InstallLog.writeToFile(f"Error checking existing MariaDB: {str(e)}") + return False, None, None + + def _attemptMariaDBUpgrade(self): + """Attempt to upgrade MariaDB to 12.1. Returns True if successful, False otherwise.""" + try: + if self.distro == ubuntu: + # Ubuntu MariaDB upgrade + command = 'DEBIAN_FRONTEND=noninteractive apt-get install software-properties-common apt-transport-https curl -y' + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + logging.InstallLog.writeToFile(f"Failed to install prerequisites: {result.stderr}") + return False + + command = "mkdir -p /etc/apt/keyrings" + subprocess.run(command, shell=True, check=False) + + command = "curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'" + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + logging.InstallLog.writeToFile(f"Failed to download MariaDB keyring: {result.stderr}") + return False + + # Setup MariaDB 12.1 repository + command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=12.1' + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + logging.InstallLog.writeToFile(f"Failed to setup MariaDB repository: {result.stderr}") + return False + + command = 'DEBIAN_FRONTEND=noninteractive apt-get update -y' + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + logging.InstallLog.writeToFile(f"Failed to update package list: {result.stderr}") + return False + + # Attempt to install MariaDB 12.1 + command = "DEBIAN_FRONTEND=noninteractive apt-get install mariadb-server -y" + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + # Check if error is due to upgrade restrictions + error_output = result.stderr + result.stdout + if "upgrade" in error_output.lower() or "manual" in error_output.lower(): + logging.InstallLog.writeToFile("MariaDB upgrade blocked - requires manual intervention") + else: + logging.InstallLog.writeToFile(f"MariaDB installation failed: {error_output}") + return False + + return True + else: + # RHEL-based MariaDB upgrade + # Setup MariaDB 12.1 repository + command = 'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=12.1' + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + logging.InstallLog.writeToFile(f"Failed to setup MariaDB repository: {result.stderr}") + return False + + # Attempt to install MariaDB 12.1 + # Use --allowerasing to allow package replacements if needed + command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y --allowerasing' + result = subprocess.run(command, shell=True, capture_output=True, universal_newlines=True) + if result.returncode != 0: + # Check if error is due to upgrade restrictions + error_output = result.stderr + result.stdout + if "PREIN scriptlet failed" in error_output or "upgrade" in error_output.lower() or "manual" in error_output.lower(): + logging.InstallLog.writeToFile("MariaDB upgrade blocked by package manager - requires manual intervention") + else: + logging.InstallLog.writeToFile(f"MariaDB installation failed: {error_output}") + return False + + return True + + except Exception as e: + logging.InstallLog.writeToFile(f"Exception during MariaDB upgrade attempt: {str(e)}") + return False + def installMySQL(self, mysql): """Install MySQL/MariaDB""" try: + # Check if MariaDB is already installed + is_installed, installed_version, major_minor = self.checkExistingMariaDB() + + if is_installed: + self.stdOut(f"MariaDB/MySQL is already installed (version: {installed_version})", 1) + + # Check if we need to upgrade + should_try_upgrade = False + if major_minor and major_minor != "unknown": + try: + major_ver = float(major_minor) + if major_ver < 12.0: + should_try_upgrade = True + self.stdOut(f"Existing MariaDB {major_minor} detected. Attempting to upgrade to MariaDB 12.1...", 1) + self.stdOut("If upgrade fails, we will use the existing MariaDB installation.", 1) + except (ValueError, TypeError): + pass + + # If MariaDB 10.x is installed, try to upgrade to 12.1 first + if should_try_upgrade: + try: + self.stdOut("Attempting to install MariaDB 12.1...", 1) + upgrade_success = self._attemptMariaDBUpgrade() + if upgrade_success: + self.stdOut("✅ Successfully upgraded to MariaDB 12.1", 1) + self.startMariaDB() + self.changeMYSQLRootPassword() + self.fixMariaDB() + return True + else: + self.stdOut("⚠️ MariaDB 12.1 upgrade failed, using existing MariaDB installation", 1) + self.startMariaDB() + return True + except Exception as upgrade_error: + error_msg = str(upgrade_error) + logging.InstallLog.writeToFile(f"MariaDB upgrade attempt failed: {error_msg}") + + # Check if error is due to upgrade restrictions + if "PREIN scriptlet failed" in error_msg or "upgrade" in error_msg.lower() or "manual" in error_msg.lower(): + self.stdOut("⚠️ MariaDB upgrade blocked by package manager (10.x to 12.x requires manual upgrade)", 1) + self.stdOut(f"Using existing MariaDB {installed_version} installation", 1) + else: + self.stdOut(f"⚠️ MariaDB upgrade failed: {error_msg}", 1) + self.stdOut(f"Using existing MariaDB {installed_version} installation", 1) + + # Fall back to existing installation + self.startMariaDB() + return True + + # MariaDB 12.x or higher already installed, or version unknown but working + # Just ensure it's running + self.stdOut("Using existing MariaDB installation", 1) + self.startMariaDB() + return True + self.stdOut("Installing MySQL/MariaDB...", 1) if self.distro == ubuntu: