Copilot commented on code in PR #1:
URL: 
https://github.com/apache/cloudstack-installer/pull/1#discussion_r2486225684


##########
installer.sh:
##########
@@ -0,0 +1,2287 @@
+#!/usr/bin/env bash
+# c8k.in/installer.sh - Easiest Apache CloudStack Installer
+# Install with this command (from your Ubuntu/EL host):
+#
+# curl -sSfL https://c8k.in/installer.sh | bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euo pipefail  # Exit on error, undefined vars, pipe failures
+
+# Global variables
+SCRIPT_NAME="Apache CloudStack Installer"
+CS_LOGFILE="$PWD/cloudstack-install.log"
+TRACKER_FILE="$PWD/cloudstack-installer-tracker.conf"
+
+OS_TYPE=""
+PACKAGE_MANAGER=""
+SELECTED_COMPONENTS=()
+ZONE_TYPE=""
+MYSQL_SERVICE=""
+MYSQL_CONF_DIR=""
+
+BRIDGE=cloudbr0
+HOST_IP=
+GATEWAY=
+DNS="8.8.8.8"
+NETMASK="255.255.255.0"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 1 = Prompt mode (interactive), 0 = Silent (non-interactive)
+PROMPT=1
+
+#-------------------------------------------------------------------------------
+# Utility functions
+#-------------------------------------------------------------------------------
+
+# checks if prompt mode is enabled
+is_interactive() { (( PROMPT )); }
+is_silent()      { (( !PROMPT )); }
+
+# Log related utilities
+#-------------------------------------------------------------------------------
+
+# Clean up ANSI escape sequences from logs
+strip_ansi() {
+    sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'
+}
+
+# Logging function
+log() {
+    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$CS_LOGFILE"
+}
+
+# Error handling function
+error_exit() {
+    echo -e "${RED}ERROR: $1${NC}" >&2
+    log "ERROR: $1"
+    exit 1
+}
+
+# Success message function
+success_msg() {
+    echo -e "${GREEN}SUCCESS: $1${NC}"
+    log "SUCCESS: $1"
+}
+
+# Warning message function
+warn_msg() {
+    echo -e "${YELLOW}WARNING: $1${NC}"
+    log "WARNING: $1"
+}
+
+# Info message function
+info_msg() {
+    echo -e "${BLUE}INFO: $1${NC}"
+    log "INFO: $1"
+}
+
+# Utilities for resource checks
+#-------------------------------------------------------------------------------
+
+# Check if running as root
+check_root() {
+    if [[ $EUID -ne 0 ]]; then
+        error_exit "This script must be run as root. Please use 'sudo $0'"
+    fi
+}
+
+# Check system resources (RAM and Disk)
+check_system_resources() {
+    MIN_RAM_KB=$((8 * 1024 * 1024))  # 8 GB in KB
+    TOTAL_RAM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
+
+    # Check if RAM is within the desired range
+    if [ "$TOTAL_RAM_KB" -ge "$MIN_RAM_KB" ]; then
+        success_msg "RAM check passed: $(awk "BEGIN {printf \"%.2f\", 
$TOTAL_RAM_KB/1024/1024}") GB"
+    else
+        error_exit "RAM check failed: System has $(awk "BEGIN {printf 
\"%.2f\", $TOTAL_RAM_KB/1024/1024}") GB RAM"
+    fi
+
+    MIN_DISK_GB=75  # Minimum disk space in GB
+    TOTAL_DISK_GB=$(df / | tail -1 | awk '{print $2}' | awk '{printf "%.0f", 
$1/1024/1024}')
+
+    # Check if disk space is within the desired range
+    if [ "$TOTAL_DISK_GB" -ge "$MIN_DISK_GB" ]; then
+        success_msg "Disk space check passed: $TOTAL_DISK_GB GB available"
+    else
+        error_exit "Disk space check failed: System has only $TOTAL_DISK_GB GB 
available"
+    fi
+}
+
+# Ensure kvm modules are loaded for KVM Agent
+check_kvm_support() {
+    info_msg "Checking KVM prerequisites..."
+    if ! grep -E 'vmx|svm' /proc/cpuinfo >/dev/null; then
+        error_exit "CPU does not support hardware virtualization (agent)"
+    fi
+    success_msg "βœ“ CPU virtualization support detected"
+
+    if ! lsmod | grep -q kvm; then
+        error_exit "KVM kernel module is not loaded"
+    fi
+    success_msg "βœ“ KVM kernel module loaded"
+}
+
+# Initialize OS_TYPE, PACKAGE_MANAGER, MYSQL_SERVICE, MYSQL_CONF_DIR
+detect_os() {
+    if [[ ! -f /etc/os-release ]]; then
+        error_exit "Cannot detect operating system. /etc/os-release not found."
+    fi
+    
+    source /etc/os-release
+    OS_TYPE=$ID
+    OS_VERSION=$VERSION_ID
+    VERSION_CODENAME=${VERSION_CODENAME:-}
+    
+    case "$OS_TYPE" in
+        ubuntu|debian)
+            PACKAGE_MANAGER="apt"
+            MYSQL_SERVICE="mysql"
+            MYSQL_CONF_DIR="/etc/mysql/mysql.conf.d"
+            ;;
+        rhel|centos|ol|rocky|almalinux)
+            PACKAGE_MANAGER="dnf"
+            MYSQL_SERVICE="mysqld"
+            MYSQL_CONF_DIR="/etc/my.cnf.d"
+            ;;
+        *)
+            echo "Unsupported OS: $OS_TYPE"
+            exit 1
+            ;;
+    esac
+
+    log "OS Detection: $OS_TYPE with package manager: $PACKAGE_MANAGER"
+}
+
+# Tracker file utilities
+#-------------------------------------------------------------------------------
+# maintains tracker values for installation and configuration steps
+
+declare -A tracker_values
+
+# loads tracker file data into tracker_values
+load_tracker() {
+    if [[ ! -f "$TRACKER_FILE" ]]; then
+        echo "# CloudStack Installer Tracker Config" > "$TRACKER_FILE"
+        echo "# Created on: $(date)" >> "$TRACKER_FILE"
+        return 0
+    fi
+
+    while IFS='=' read -r key value; do
+        [[ -z "$key" || "$key" =~ ^# ]] && continue
+        tracker_values["$key"]="$value"
+    done < "$TRACKER_FILE"
+}
+
+get_tracker_field() {
+    local key="$1"
+    echo "${tracker_values[$key]:-}"
+}
+
+# Save or update a field in the tracker file
+set_tracker_field() {
+    local key="$1"
+    local value="$2"
+
+    # Update associative array
+    tracker_values["$key"]="$value"
+
+    # Update or append key=value in tracker file
+    if grep -q "^$key=" "$TRACKER_FILE"; then
+        sed -i "s|^$key=.*|$key=$value|" "$TRACKER_FILE"
+    else
+        echo "$key=$value" >> "$TRACKER_FILE"
+    fi
+}
+
+# checks if a step is already tracked in tracker file
+is_step_tracked() {
+    local key="$1"
+    [[ -n "${tracker_values[$key]:-}" ]]
+}
+
+# Utility functions for Repository setup
+#-------------------------------------------------------------------------------
+
+# Map Debian codenames to Ubuntu codenames for CloudStack repo
+get_ubuntu_codename_for_debian() {
+    case "$1" in
+        buster|bullseye)
+            echo "focal"
+            ;;
+        bookworm|trixie)
+            echo "jammy"
+            ;;
+        *)
+            echo "ERROR: Unsupported Debian codename '$1'" >&2
+            return 1
+            ;;
+    esac
+}
+
+#  Determine repo_path for CloudStack repository based on OS type and version
+determine_rpm_distro_version() {
+    # Extract major version (8 or 9) from version string
+    local major_version=${OS_VERSION%%.*}
+    case "$OS_TYPE" in
+        centos)
+            echo "centos/$major_version"
+            ;;
+        rhel)
+            echo "rhel/$major_version"
+            ;;
+        rocky|almalinux|ol)
+            echo "el/$major_version"
+            ;;
+        *)
+            error_exit "Unsupported OS type: $OS_TYPE"
+            ;;
+    esac
+}
+
+# Validates repository entry format
+validate_repo_entry() {
+    local os_type="$1"
+    local entry="$2"
+
+    # Basic check: not empty
+    if [[ -z "$entry" ]]; then
+        error_exit "CloudStack Repository entry cannot be empty."
+        return 1
+    fi
+
+    # Debian/Ubuntu repo line example:
+    # deb [signed-by=...] https://download.cloudstack.org/ubuntu noble 4.20
+    if [[ "$os_type" =~ ^(ubuntu|debian)$ ]]; then
+        if [[ ! "$entry" =~ https?:// ]]; then
+            error_exit "Invalid Repository entry must include a valid URL 
(http or https)."
+            return 1
+        fi
+    fi
+
+    # RHEL-family example:
+    # https://download.cloudstack.org/centos/9/4.20/
+    if [[ "$os_type" =~ ^(rhel|centos|rocky|almalinux|ol)$ ]]; then
+        if [[ ! "$entry" =~ ^https?:// ]]; then
+            error_exit "Invalid Repository baseurl must start with http:// or 
https://.";
+            return 1
+        fi
+    fi
+
+    # Optional: check version (warn, not fatal)
+    if [[ ! "$entry" =~ 4\.([1-9][0-9]) ]]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Warning" \
+               --msgbox "The repository entry does not appear to contain a 
known CloudStack version (4.xx). Please verify before proceeding." 8 70
+    fi
+
+    return 0
+}
+
+# Update system packages once repository is configured
+update_system_packages() {
+    local percent=1
+    {
+        case "$PACKAGE_MANAGER" in
+            apt)
+                apt-get update 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 50 ] && percent=50
+                    update_progress_bar "$percent" "# Updating package 
lists...\n\n$line"
+                done
+
+                apt-get upgrade -y 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 80 ] && percent=80
+                    update_progress_bar "$percent" "# Installing 
updates...\n\n$line"
+                done
+                ;;
+            dnf)
+                dnf clean all 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 20 ] && percent=20
+                    update_progress_bar "$percent" "# Cleaning package 
cache...\n\n$line"
+                done
+
+                dnf makecache 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 60 ] && percent=60
+                    update_progress_bar "40" "# Updating package 
cache...\n\n$line"

Review Comment:
   The progress bar percentage is hardcoded to '40' instead of using the 
`$percent` variable that is being incremented in the loop. This means the 
progress bar will show 40% for all iterations of this loop, instead of 
progressing from 20 to 60 as intended by the surrounding logic.
   ```suggestion
                       update_progress_bar "$percent" "# Updating package 
cache...\n\n$line"
   ```



##########
README.md:
##########
@@ -1,26 +1,170 @@
 # [c8k.in](https://github.com/apache/cloudstack-installer): One-liner 
installer for [Apache CloudStack](https://cloudstack.apache.org)
 
 > [!NOTE]
-> Only supports CloudStack installation on Ubuntu with x86_64 KVM and tested 
with Ubuntu 22.04 LTS (x86_64). Please use this on a throwaway host or a VM 
(with hardware acceleration) that wouldn't hurt you to lose, in an [RFC1918 
local private network](https://en.wikipedia.org/wiki/Private_network). This is 
currently in `beta` and open for users testing and issue reporting.
+> Supports CloudStack installation on Ubuntu or EL based distro with x86_64 
KVM and tested with Ubuntu 24.04 LTS (x86_64) and Rocky Linux 8. Please use 
this on a throwaway host or a VM (with hardware acceleration) that wouldn't 
hurt you to lose, in an [RFC1918 local private 
network](https://en.wikipedia.org/wiki/Private_network). This is currently in 
`beta` and open for users testing and issue reporting.
 
 NOTE: Work in progress
 
+### πŸš€ The Easiest Way to Install Apache CloudStack on Ubuntu or EL-based 
Distros
+
+One-liners installer automates and simplifies the installation and 
configuration of **Apache CloudStack** and its components:
+
+- CloudStack Management Server
+- CloudStack Agent for KVM Host
+- CloudStack Usage Server
+- MySQL Server
+- NFS Server
+
+---
+
+## Features
+
+- **All-in-One Installation**: Deploy a complete CloudStack setup on a single 
machine
+- **Custom Installation**: Select specific CloudStack components to install
+- **Repository Configuration**: Set up CloudStack repository automatically
+- **Zone Deployment**: Guided wizard for zone deployment
+- **Network Configuration**: Automatically configure network bridges for 
CloudStack
+- **Component Validation**: Verify that all components are properly configured
+- **Progress Tracking**: Track installation progress and resume interrupted 
installations
+- Support re-runs, idempotent with tracker
+
+---
+
+## βš™οΈ Requirements
+
+| Resource | Minimum |
+|-----------|----------|
+| **RAM**   | 8 GB     |
+| **Disk**  | 75 GB    |
+| **Privileges** | Root / `sudo` |
+| **Virtualization** | Hardware virtualization (Intel VT-x / AMD-V) |
+
+Supported Operating Systems:
+- Ubuntu 20.04+, Debian 11+
+- RHEL 8+/CentOS/AlmaLinux/RockyLinux
+
+---
+
+## πŸ“¦ Installation
+
 To install and deploy [CloudStack](https://cloudstack.apache.org), just copy 
and run the following as `root` user:
 
+### Quick Install
+
 ```bash
-curl -sL https://c8k.in/stall.sh | bash
+curl -sSfL https://c8k.in/installer.sh | bash
 ```
 
-Here's how you should really run it though:
+### Download and Run
 
 ```bash
-curl -o install.sh https://c8k.in/stall.sh
-cat install.sh | more # to read it
-bash -x install.sh # to also see what it's doing
+wget https://c8k.in/installer.sh
+chmod +x installer.sh
+sudo ./installer.sh
+```
+
+---
+
+## πŸ–₯️ Installation Menu Options
+
+When you launch the installer, you’ll see a dialog menu like this:
+
 ```
+Select an option:
+1. All-in-One Installation
+2. Custom Installation
+3. Configure CloudStack Repository
+4. Deploy CloudStack Zone
+```
+
+Here’s what each option does:
+
+### **1️⃣ All-in-One Installation**
+Performs a full automated setup of Apache CloudStack on a single host.  
+Includes installation and configuration of:
+- CloudStack Management Server
+- KVM Agent (libvirt, bridge, VNC, firewall)
+- MySQL database server
+- NFS primary and secondary storage
+- Usage Server
+- Zone Deployment
+
+Ideal for test environments, PoC labs, or quick demos β€” ready to use in under 
an hour.
+
+### **2️⃣ Custom Installation**
+Lets you select individual components to install and configure.  
+Useful when you want to:
+- Separate Management and KVM roles across multiple machines 
+- Install/Configure or skip specific services (e.g., NFS, CloudStack Agent, 
Skipping Usage Server)
+- Integrate with an existing CloudStack deployment
+
+### **3️⃣ Configure CloudStack Repository**
+Sets up the official or custom CloudStack package repository for your OS.  
+Automatically detects your distribution (Ubuntu, Debian, RHEL, Rocky, etc.) 
and configures:
+- GPG key
+- Repository URL
+- Package source file
+
+You can reconfigure repository using this option at any time to switch 
versions or mirrors.
+
+### **4️⃣ Deploy CloudStack Zone**
+After installing the components, this option launches a guided **Zone 
Deployment Wizard** that:
+- Creates a new CloudStack Zone (Advanced networking)
+- Adds Pod, Cluster, Host, and Storage resources
+- Configures networking (bridge, IP ranges, VLANs)
+
+Perfect for creating a ready-to-use cloud zone in minutes.
+
+---
+
+## πŸ’‘ Use Cases
+This installer is designed to make **Apache CloudStack** accessible for 
learning, testing, and experimentation - without needing a large-scale 
infrastructure.
+1. Spin up a fully functional CloudStack zone (management, KVM host, and NFS 
storage) on a single VM or physical host for development or QA.
+2. Ideal for CloudStack contributors to test new PRs, validate feature 
behaviour across OS.

Review Comment:
   [nitpick] Corrected spelling of 'behaviour' to 'behavior' for American 
English consistency.
   ```suggestion
   2. Ideal for CloudStack contributors to test new PRs, validate feature 
behavior across OS.
   ```



##########
installer.sh:
##########
@@ -0,0 +1,2287 @@
+#!/usr/bin/env bash
+# c8k.in/installer.sh - Easiest Apache CloudStack Installer
+# Install with this command (from your Ubuntu/EL host):
+#
+# curl -sSfL https://c8k.in/installer.sh | bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euo pipefail  # Exit on error, undefined vars, pipe failures
+
+# Global variables
+SCRIPT_NAME="Apache CloudStack Installer"
+CS_LOGFILE="$PWD/cloudstack-install.log"
+TRACKER_FILE="$PWD/cloudstack-installer-tracker.conf"
+
+OS_TYPE=""
+PACKAGE_MANAGER=""
+SELECTED_COMPONENTS=()
+ZONE_TYPE=""
+MYSQL_SERVICE=""
+MYSQL_CONF_DIR=""
+
+BRIDGE=cloudbr0
+HOST_IP=
+GATEWAY=
+DNS="8.8.8.8"
+NETMASK="255.255.255.0"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 1 = Prompt mode (interactive), 0 = Silent (non-interactive)
+PROMPT=1
+
+#-------------------------------------------------------------------------------
+# Utility functions
+#-------------------------------------------------------------------------------
+
+# checks if prompt mode is enabled
+is_interactive() { (( PROMPT )); }
+is_silent()      { (( !PROMPT )); }
+
+# Log related utilities
+#-------------------------------------------------------------------------------
+
+# Clean up ANSI escape sequences from logs
+strip_ansi() {
+    sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'
+}
+
+# Logging function
+log() {
+    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$CS_LOGFILE"
+}
+
+# Error handling function
+error_exit() {
+    echo -e "${RED}ERROR: $1${NC}" >&2
+    log "ERROR: $1"
+    exit 1
+}
+
+# Success message function
+success_msg() {
+    echo -e "${GREEN}SUCCESS: $1${NC}"
+    log "SUCCESS: $1"
+}
+
+# Warning message function
+warn_msg() {
+    echo -e "${YELLOW}WARNING: $1${NC}"
+    log "WARNING: $1"
+}
+
+# Info message function
+info_msg() {
+    echo -e "${BLUE}INFO: $1${NC}"
+    log "INFO: $1"
+}
+
+# Utilities for resource checks
+#-------------------------------------------------------------------------------
+
+# Check if running as root
+check_root() {
+    if [[ $EUID -ne 0 ]]; then
+        error_exit "This script must be run as root. Please use 'sudo $0'"
+    fi
+}
+
+# Check system resources (RAM and Disk)
+check_system_resources() {
+    MIN_RAM_KB=$((8 * 1024 * 1024))  # 8 GB in KB
+    TOTAL_RAM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
+
+    # Check if RAM is within the desired range
+    if [ "$TOTAL_RAM_KB" -ge "$MIN_RAM_KB" ]; then
+        success_msg "RAM check passed: $(awk "BEGIN {printf \"%.2f\", 
$TOTAL_RAM_KB/1024/1024}") GB"
+    else
+        error_exit "RAM check failed: System has $(awk "BEGIN {printf 
\"%.2f\", $TOTAL_RAM_KB/1024/1024}") GB RAM"
+    fi
+
+    MIN_DISK_GB=75  # Minimum disk space in GB
+    TOTAL_DISK_GB=$(df / | tail -1 | awk '{print $2}' | awk '{printf "%.0f", 
$1/1024/1024}')
+
+    # Check if disk space is within the desired range
+    if [ "$TOTAL_DISK_GB" -ge "$MIN_DISK_GB" ]; then
+        success_msg "Disk space check passed: $TOTAL_DISK_GB GB available"
+    else
+        error_exit "Disk space check failed: System has only $TOTAL_DISK_GB GB 
available"
+    fi
+}
+
+# Ensure kvm modules are loaded for KVM Agent
+check_kvm_support() {
+    info_msg "Checking KVM prerequisites..."
+    if ! grep -E 'vmx|svm' /proc/cpuinfo >/dev/null; then
+        error_exit "CPU does not support hardware virtualization (agent)"
+    fi
+    success_msg "βœ“ CPU virtualization support detected"
+
+    if ! lsmod | grep -q kvm; then
+        error_exit "KVM kernel module is not loaded"
+    fi
+    success_msg "βœ“ KVM kernel module loaded"
+}
+
+# Initialize OS_TYPE, PACKAGE_MANAGER, MYSQL_SERVICE, MYSQL_CONF_DIR
+detect_os() {
+    if [[ ! -f /etc/os-release ]]; then
+        error_exit "Cannot detect operating system. /etc/os-release not found."
+    fi
+    
+    source /etc/os-release
+    OS_TYPE=$ID
+    OS_VERSION=$VERSION_ID
+    VERSION_CODENAME=${VERSION_CODENAME:-}
+    
+    case "$OS_TYPE" in
+        ubuntu|debian)
+            PACKAGE_MANAGER="apt"
+            MYSQL_SERVICE="mysql"
+            MYSQL_CONF_DIR="/etc/mysql/mysql.conf.d"
+            ;;
+        rhel|centos|ol|rocky|almalinux)
+            PACKAGE_MANAGER="dnf"
+            MYSQL_SERVICE="mysqld"
+            MYSQL_CONF_DIR="/etc/my.cnf.d"
+            ;;
+        *)
+            echo "Unsupported OS: $OS_TYPE"
+            exit 1
+            ;;
+    esac
+
+    log "OS Detection: $OS_TYPE with package manager: $PACKAGE_MANAGER"
+}
+
+# Tracker file utilities
+#-------------------------------------------------------------------------------
+# maintains tracker values for installation and configuration steps
+
+declare -A tracker_values
+
+# loads tracker file data into tracker_values
+load_tracker() {
+    if [[ ! -f "$TRACKER_FILE" ]]; then
+        echo "# CloudStack Installer Tracker Config" > "$TRACKER_FILE"
+        echo "# Created on: $(date)" >> "$TRACKER_FILE"
+        return 0
+    fi
+
+    while IFS='=' read -r key value; do
+        [[ -z "$key" || "$key" =~ ^# ]] && continue
+        tracker_values["$key"]="$value"
+    done < "$TRACKER_FILE"
+}
+
+get_tracker_field() {
+    local key="$1"
+    echo "${tracker_values[$key]:-}"
+}
+
+# Save or update a field in the tracker file
+set_tracker_field() {
+    local key="$1"
+    local value="$2"
+
+    # Update associative array
+    tracker_values["$key"]="$value"
+
+    # Update or append key=value in tracker file
+    if grep -q "^$key=" "$TRACKER_FILE"; then
+        sed -i "s|^$key=.*|$key=$value|" "$TRACKER_FILE"
+    else
+        echo "$key=$value" >> "$TRACKER_FILE"
+    fi
+}
+
+# checks if a step is already tracked in tracker file
+is_step_tracked() {
+    local key="$1"
+    [[ -n "${tracker_values[$key]:-}" ]]
+}
+
+# Utility functions for Repository setup
+#-------------------------------------------------------------------------------
+
+# Map Debian codenames to Ubuntu codenames for CloudStack repo
+get_ubuntu_codename_for_debian() {
+    case "$1" in
+        buster|bullseye)
+            echo "focal"
+            ;;
+        bookworm|trixie)
+            echo "jammy"
+            ;;
+        *)
+            echo "ERROR: Unsupported Debian codename '$1'" >&2
+            return 1
+            ;;
+    esac
+}
+
+#  Determine repo_path for CloudStack repository based on OS type and version
+determine_rpm_distro_version() {
+    # Extract major version (8 or 9) from version string
+    local major_version=${OS_VERSION%%.*}
+    case "$OS_TYPE" in
+        centos)
+            echo "centos/$major_version"
+            ;;
+        rhel)
+            echo "rhel/$major_version"
+            ;;
+        rocky|almalinux|ol)
+            echo "el/$major_version"
+            ;;
+        *)
+            error_exit "Unsupported OS type: $OS_TYPE"
+            ;;
+    esac
+}
+
+# Validates repository entry format
+validate_repo_entry() {
+    local os_type="$1"
+    local entry="$2"
+
+    # Basic check: not empty
+    if [[ -z "$entry" ]]; then
+        error_exit "CloudStack Repository entry cannot be empty."
+        return 1
+    fi
+
+    # Debian/Ubuntu repo line example:
+    # deb [signed-by=...] https://download.cloudstack.org/ubuntu noble 4.20
+    if [[ "$os_type" =~ ^(ubuntu|debian)$ ]]; then
+        if [[ ! "$entry" =~ https?:// ]]; then
+            error_exit "Invalid Repository entry must include a valid URL 
(http or https)."
+            return 1
+        fi
+    fi
+
+    # RHEL-family example:
+    # https://download.cloudstack.org/centos/9/4.20/
+    if [[ "$os_type" =~ ^(rhel|centos|rocky|almalinux|ol)$ ]]; then
+        if [[ ! "$entry" =~ ^https?:// ]]; then
+            error_exit "Invalid Repository baseurl must start with http:// or 
https://.";
+            return 1
+        fi
+    fi
+
+    # Optional: check version (warn, not fatal)
+    if [[ ! "$entry" =~ 4\.([1-9][0-9]) ]]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Warning" \
+               --msgbox "The repository entry does not appear to contain a 
known CloudStack version (4.xx). Please verify before proceeding." 8 70
+    fi
+
+    return 0
+}
+
+# Update system packages once repository is configured
+update_system_packages() {
+    local percent=1
+    {
+        case "$PACKAGE_MANAGER" in
+            apt)
+                apt-get update 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 50 ] && percent=50
+                    update_progress_bar "$percent" "# Updating package 
lists...\n\n$line"
+                done
+
+                apt-get upgrade -y 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 80 ] && percent=80
+                    update_progress_bar "$percent" "# Installing 
updates...\n\n$line"
+                done
+                ;;
+            dnf)
+                dnf clean all 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 20 ] && percent=20
+                    update_progress_bar "$percent" "# Cleaning package 
cache...\n\n$line"
+                done
+
+                dnf makecache 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 60 ] && percent=60
+                    update_progress_bar "40" "# Updating package 
cache...\n\n$line"
+                done
+
+                dnf update -y 2>&1 | while IFS= read -r line; do
+                    percent=$((percent + 1))
+                    [ $percent -gt 80 ] && percent=80
+                    update_progress_bar "$percent" "# Installing system 
updates...\n\n$line"
+                done
+                ;;
+        esac
+
+        update_progress_bar "100" "# System update complete!"
+        sleep 2
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "System Update" \
+               --gauge "Updating system packages..." 15 80 0
+
+    # Verify update success
+    if [[ $? -eq 0 ]]; then
+        show_dialog info "System Update" "System packages have been 
successfully updated." 2
+    else
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Error" \
+               --msgbox "Failed to update system packages. Please check the 
logs." 6 50
+        return 1
+    fi
+}
+
+# Installation related utilities
+#-------------------------------------------------------------------------------
+
+is_package_installed() {
+    local pkgs=("$@")  # Accept multiple packages
+    local pkg
+
+    for pkg in "${pkgs[@]}"; do
+        case "$PACKAGE_MANAGER" in
+            apt)
+                if ! dpkg -s "$pkg" &>/dev/null; then
+                    return 1  # one package missing -> not installed
+                fi
+                ;;
+            dnf)
+                if ! dnf list installed "$pkg" &>/dev/null; then
+                    return 1
+                fi
+                ;;
+        esac
+    done
+    return 0  # all packages installed
+}
+
+install_package() {
+    local packages=("$@")  # Capture all arguments (1..N)
+
+    case "$PACKAGE_MANAGER" in
+        apt)
+            DEBIAN_FRONTEND=noninteractive apt-get install -y "${packages[@]}"
+            ;;
+        dnf)
+            dnf install -y "${packages[@]}"
+            ;;
+        *)
+            echo "Unsupported package manager: $PACKAGE_MANAGER" >&2
+            return 1
+            ;;
+    esac
+}
+
+# Generic function, performs package installation with progress dialog
+install_pkg_with_progress_bar() {
+    local title="$1"
+    local package_name="$2"
+    local tracker_key="$3"
+
+    # Skip if already configured
+    if is_step_tracked "$tracker_key"; then
+        log "$title is already installed. Skipping."
+        show_dialog "info" "$title Installation" "$title is already installed. 
Skipping installation."
+        return 0
+    fi
+
+    # Skip if package already installed
+    if is_package_installed "$package_name"; then
+        log "$title package already present. Skipping installation."
+        set_tracker_field "$tracker_key" "yes"
+        show_dialog "info" "$title Installation" "$title package already 
present. Skipping installation."
+        return 0
+    fi
+
+    log "Installing $title..."
+
+    # Temporary log file
+    local TMP_LOG
+    TMP_LOG=$(mktemp /tmp/install_${tracker_key}.XXXXXX.log)
+
+    # Start installation in the background
+    install_package $package_name >> "$TMP_LOG" 2>&1 &
+    local INSTALL_PID=$!
+
+    local percent=0
+    local start_msg="Installing $title..."
+
+    {
+        update_progress_bar "$percent" "# $start_msg"
+
+        while kill -0 "$INSTALL_PID" 2>/dev/null; do
+            local tail_output
+            tail_output=$(tail -n 5 "$TMP_LOG" | strip_ansi | tr -d '\r')
+            
+            # Add left padding, truncate width, and wrap safely
+            tail_output=$(echo "$tail_output" \
+            | sed 's/^/   /' \
+            | fold -s -w 80 \
+            | tail -n 5)
+
+            echo "$percent"
+            echo "XXX"
+            echo "# $start_msg"
+            echo
+            echo "$tail_output"
+            echo "XXX"
+
+            percent=$((percent + 1))
+            [ $percent -gt 90 ] && percent=90
+            sleep 1
+        done
+    } | dialog --backtitle "$SCRIPT_NAME" --title "$title Installation" 
--gauge "Installing $title..." 15 80 0
+
+    wait "$INSTALL_PID"
+    local status=$?
+
+    if [ $status -eq 0 ]; then
+        set_tracker_field "$tracker_key" "yes"
+        log "$title installed successfully."
+        rm -f "$TMP_LOG"
+        return 0
+    else
+        log "Failed to install $title. Check $TMP_LOG"
+        error_exit "Failed to install $title."
+    fi
+}
+
+# function to update progress bar in the dialog
+update_progress_bar() {
+    local percent="$1"
+    local msg="$2"
+    echo "XXX"
+    echo "$percent"
+    echo -e "$msg"
+    echo "XXX"
+}
+
+# utility function to display dialog for info or message purpose
+show_dialog() {
+    local mode="$1"
+    local title="$2"
+    local msg="$3"
+    local seconds="${4:-3}"
+    local height="${5:-7}"
+    local width="${6:-60}"
+
+    case "$mode" in
+        info)
+            dialog --backtitle "$SCRIPT_NAME" \
+                    --title "$title" \
+                    --infobox "$msg" $height $width
+            sleep "$seconds"
+            return 0
+            ;;
+        msg)
+            dialog --backtitle "$SCRIPT_NAME" \
+                    --title "$title" \
+                    --msgbox "$msg" $height $width
+            return 0
+            ;;
+        *)
+            echo "Unknown mode: $mode"
+            return 1
+            ;;
+    esac
+}
+
+# Attempts to start service_name
+start_service_with_progress() {
+    local service_name="$1"
+    local display_name="${2:-$service_name}"
+    local start_percent="${3:-60}"
+    local end_percent="${4:-90}"
+
+    if ! systemctl is-active --quiet "$service_name"; then
+        {
+            update_progress_bar "$start_percent" "# Starting $display_name 
service..."
+            systemctl start "$service_name" >/dev/null 2>&1 &
+            local pid=$!
+
+            local i
+            for ((i=start_percent; i<=end_percent; i++)); do
+                update_progress_bar "$i" "# Waiting for $display_name to 
start..."
+                sleep 0.2
+            done
+
+            wait "$pid"
+            update_progress_bar "$end_percent" "# $display_name service 
started successfully."
+            sleep 1
+        } | dialog --backtitle "$SCRIPT_NAME" \
+                   --title "Starting $display_name Service" \
+                   --gauge "Starting $display_name service..." 15 70 0
+
+        systemctl enable "$service_name" >/dev/null 2>&1
+        if systemctl is-active --quiet "$service_name"; then
+            log "$display_name service started successfully."
+        else
+            error_exit "Failed to start $display_name service."
+        fi
+    else
+        log "$display_name service is already running."
+    fi
+}
+
+
+# CloudStack Banner gets displayed at the end of Zone Deployment
+show_cloudstack_banner() {
+    local banner="
+    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
+    β–ˆβ”€β–„β–„β–„β”€β–ˆβ–„β”€β–„β–ˆβ–ˆβ–ˆβ”€β–„β–„β”€β–ˆβ–„β”€β–ˆβ–ˆβ”€β–„β–ˆβ–„β”€β–„β–„β–€β–ˆβ”€β–„β–„β–„β–„β–ˆβ”€β–„β”€β–„β”€β–ˆβ–ˆβ–€β–„β”€β–ˆβ–ˆβ”€β–„β–„β–„β”€β–ˆβ–„β”€β–ˆβ”€β–„β–ˆ
+    β–ˆβ”€β–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆβ”€β–ˆβ–ˆβ–€β–ˆβ”€β–ˆβ–ˆβ”€β–ˆβ–ˆβ”€β–ˆβ–ˆβ”€β–ˆβ–ˆβ–ˆβ”€β–ˆβ–ˆβ”€β–ˆβ–„β–„β–„β–„β”€β–ˆβ–ˆβ–ˆβ”€β–ˆβ–ˆβ–ˆβ–ˆβ”€β–€β”€β–ˆβ–ˆβ”€β–ˆβ–ˆβ–ˆβ–€β–ˆβ–ˆβ”€β–„β–€β–ˆβ–ˆ
+    β–€β–„β–„β–„β–„β–„β–€β–„β–„β–„β–„β–„β–€β–„β–„β–„β–„β–€β–€β–„β–„β–„β–„β–€β–€β–„β–„β–„β–„β–€β–€β–„β–„β–„β–„β–„β–€β–€β–„β–„β–„β–€β–€β–„β–„β–€β–„β–„β–€β–„β–„β–„β–„β–„β–€β–„β–„β–€β–„β–„β–€
+
+    CloudStack Installation Complete!
+    --------------------------------
+    Access Details:
+
+    URL: http://$HOST_IP:8080/client
+    Username: admin
+    Password: password
+
+    Note: Please change the default password after first login.
+    "
+
+    dialog --backtitle "$SCRIPT_NAME" \
+        --title "Installation Complete" \
+        --colors \
+        --msgbox "\Z1$banner\Zn" 20 70
+}
+
+# Find free IP addresses in a given network range
+find_free_ip_range() {
+    local network=$1
+    local start_from=$2
+    local count=$3
+    local tempfile=$(mktemp)
+
+    # Scan network to find used IPs
+    nmap -sn -n "$network" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' > 
"$tempfile"
+
+    # Get base network
+    local base_net="${network%.*}"
+    local free_ips=()
+    local current=$start_from
+    
+    while [[ ${#free_ips[@]} -lt $count && $current -lt 255 ]]; do
+        if ! grep -q "$base_net.$current" "$tempfile"; then
+            free_ips+=("$base_net.$current")
+        fi
+        ((current++))
+    done
+    
+    rm -f "$tempfile"
+    echo "${free_ips[@]}"
+}
+
+#-------------------------------------------------------------------------------
+# Script specific functions
+#-------------------------------------------------------------------------------
+
+
+is_cloudstack_pkg_selected() {
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " management " || \
+          " ${SELECTED_COMPONENTS[@]} " =~ " agent " || \
+          " ${SELECTED_COMPONENTS[@]} " =~ " usage " ]]; then
+        return 0  # true
+    else
+        return 1  # false
+    fi
+}
+
+
+# Configure CloudStack repository for Debian/Ubuntu
+#-------------------------------------------------------------------------------
+
+_configure_deb_repo() {
+    local gpg_key_url="$1"
+    local repo_entry="$2"
+    {
+        echo "Configuring CloudStack repository..."
+        echo "Adding CloudStack's signing key..."
+
+        if curl -fsSL "$gpg_key_url" | gpg --dearmor | sudo tee 
/etc/apt/keyrings/cloudstack.gpg > /dev/null; then
+            echo "CloudStack signing key added successfully."
+        else
+            error_exit "Failed to add CloudStack signing key."
+        fi
+        
+        echo "Adding CloudStack repository..."
+        if echo "deb [signed-by=/etc/apt/keyrings/cloudstack.gpg] $repo_entry" 
| sudo tee /etc/apt/sources.list.d/cloudstack.list > /dev/null; then
+            echo "CloudStack repository added successfully."
+        else
+            error_exit "Failed to add CloudStack repository."
+        fi
+    } | dialog --backtitle "$SCRIPT_NAME" \
+                --title "Repository Configuration" \
+                --programbox "Configuring CloudStack repository..." 15 70
+}
+
+_configure_rpm_repo () {
+    local gpg_key_url="$1"
+    local repo_entry="$2"
+    {
+        echo "Adding CloudStack repository..."
+        if cat > /etc/yum.repos.d/cloudstack.repo <<EOF
+[cloudstack]
+name=CloudStack
+baseurl=$repo_entry
+enabled=1
+gpgcheck=0
+gpgkey=$gpg_key_url
+EOF
+        then
+            echo "Repository added successfully"
+        else
+            error_exit "Failed to create CloudStack repository file"
+        fi
+    } | dialog --backtitle "$SCRIPT_NAME" \
+            --title "Repository Configuration" \
+            --programbox "Configuring CloudStack repository..." 15 70
+}
+
+configure_cloudstack_repo() {
+    local title="CloudStack Repository Configuration"
+    local tracker_key="repo_url"
+    local repo_file=""
+    local repo_entry=""
+    case "$OS_TYPE" in
+        ubuntu|debian)
+            repo_file="/etc/apt/sources.list.d/cloudstack.list"
+            if [[ -f "$repo_file" ]]; then
+                repo_entry=$(grep -E '^deb ' "$repo_file" | sed -E 's/^.*] //')
+            fi
+            ;;
+        rhel|centos|ol|rocky|almalinux)
+            repo_file="/etc/yum.repos.d/cloudstack.repo"
+            if [[ -f "$repo_file" ]]; then
+                repo_entry=$(grep -E '^baseurl=' "$repo_file" | cut -d'=' -f2-)
+            fi
+            ;;
+        *)
+            dialog --msgbox "Unsupported OS: $OS_TYPE" 6 50
+            exit 1
+            ;;
+    esac
+    # If repo already exists, show info and exit gracefully for silent mode
+    # allow reconfiguration for interactive mode
+    if [[ -n "$repo_entry" ]]; then
+        if is_silent; then
+            show_dialog "info" \
+            "$title" \
+            "CloudStack repository is already configured:\n\n$repo_entry"
+            set_tracker_field "$tracker_key" "$repo_entry"
+            return 0
+        fi
+        
+        if is_interactive; then
+            if dialog --backtitle "$SCRIPT_NAME" \
+               --title "$title" \
+               --yesno "CloudStack repository is already 
configured:\n\n$repo_entry\n\nDo you want to reconfigure it?" 12 70; then
+                log "User opted to reconfigure existing CloudStack repository."
+            else
+                show_dialog "info" "$title" "Skipping CloudStack repository 
configuration."
+                set_tracker_field "$tracker_key" "$repo_entry"
+                return 0
+            fi
+        fi
+    fi
+
+    # default repo_entry is required if repo_entry is not set
+    if [[ -z "$repo_entry" ]]; then
+        # Set default repo_entry based on OS
+        local default_repo_url="https://download.cloudstack.org";
+        local default_cs_version="4.21"
+        # Build default repo_entry depending on distro
+        case "$OS_TYPE" in
+            ubuntu|debian)
+                local ubuntu_codename="$VERSION_CODENAME"
+                if [[ "$OS_TYPE" == "debian" ]]; then
+                    ubuntu_codename=$(get_ubuntu_codename_for_debian 
"$VERSION_CODENAME") || exit 1
+                fi
+                default_repo_entry="${default_repo_url}/ubuntu 
$ubuntu_codename $default_cs_version"
+                ;;
+            rhel|centos|ol|rocky|almalinux)
+                local repo_path
+                repo_path=$(determine_rpm_distro_version)
+                
default_repo_entry="${default_repo_url}/${repo_path}/${default_cs_version}/"
+                ;;
+        esac
+        repo_entry="$default_repo_entry"
+    fi
+
+    if is_interactive; then
+        width=60
+        prompt_text="Enter the CloudStack repository url:"
+        if [[ "$OS_TYPE" =~ ^(ubuntu|debian)$ ]]; then
+            prompt_text="Enter the CloudStack repository url.\n\nSupported 
formats:\nβ€’ Ubuntu-style (deb ... ubuntu codename version)\nβ€’ Flat layout (deb 
... /)\nExample: deb [signed-by=...] 
http://packages.shapeblue.com/cloudstack/upstream/debian/4.21/ /"
+            width=90
+        fi
+        height=$(( $(echo -e "$prompt_text" | wc -l) + 8 ))
+        repo_entry=$(dialog --clear \
+            --backtitle "$SCRIPT_NAME" \
+            --title "Configure CloudStack Repository" \
+            --inputbox "$prompt_text" "$height" "$width" "$repo_entry" \
+            3>&1 1>&2 2>&3)
+        
+        validate_repo_entry "$OS_TYPE" "$repo_entry" || {
+            error_exit "Invalid repository entry provided by user."
+        }
+    fi
+
+    local repo_base_url=$(echo "$repo_entry" | sed -E 's|.*(https?://[^/ 
]+).*|\1|')
+    local gpg_url="${repo_base_url}/release.asc"
+    if is_interactive; then
+        if ! dialog --backtitle "$SCRIPT_NAME" \
+            --title "Confirm Repository" \
+            --yesno "The following CloudStack repository will be 
added:\n\n$repo_entry\n\nProceed?" 12 70; then
+            error_exit "CloudStack repository configuration cancelled by user."
+        fi
+    else
+        show_dialog "info" "$title" "The following CloudStack repository will 
be added:\n\n$repo_entry" 4
+    fi
+           
+    log "Configuring CS repo: $repo_entry"
+    case "$OS_TYPE" in
+        ubuntu|debian)
+            _configure_deb_repo "$gpg_url" "$repo_entry"
+            ;;
+        rhel|centos|ol|rocky|almalinux)
+            _configure_rpm_repo "$gpg_url" "$repo_entry"
+            ;;
+        *)
+            dialog --msgbox "Unsupported OS: $OS_TYPE" 6 50
+            error_exit "Unsupported OS: $OS_TYPE"
+            ;;
+    esac
+    log "Configured CS repo: $repo_entry"
+    set_tracker_field "$tracker_key" "$repo_entry"
+}
+
+
+# Install base dependencies required for CloudStack
+#-------------------------------------------------------------------------------
+
+install_dialog_utility() {
+    log "Updating package list..."
+    case "$PACKAGE_MANAGER" in
+        apt)
+            apt-get update || error_exit "Failed to update package lists"
+            ;;
+        dnf)
+            dnf makecache || error_exit "Failed to update package cache"
+            ;;
+    esac
+
+    log "Installing 'dialog'..."
+    case "$PACKAGE_MANAGER" in
+        apt)
+            apt-get install -y dialog || error_exit "Failed to install dialog"
+            ;;
+        dnf)
+            dnf install -y dialog || error_exit "Failed to install dialog"
+            ;;
+    esac
+}
+
+install_base_dependencies() {
+    log "Starting base dependencies installation..."
+    if ! command -v dialog &>/dev/null; then
+        install_dialog_utility
+    fi
+    
+    TMP_LOG=$(mktemp /tmp/install_base.XXXXXX.log)
+    title="Installing base dependencies (qemu-kvm, python, curl, etc.)..."
+    {   
+        update_progress_bar "30" "$title"
+        case "$PACKAGE_MANAGER" in
+            apt)
+                DEBIAN_FRONTEND=noninteractive \
+                apt-get install -y qemu-kvm apt-utils curl openntpd 
openssh-server sshpass sudo wget jq htop tar nmap bridge-utils util-linux >> 
"$TMP_LOG" 2>&1 &
+                ;;
+            dnf)
+                dnf install -y curl openssh-server chrony sshpass sudo wget jq 
tar nmap util-linux >> "$TMP_LOG" 2>&1 &
+                ;;
+        esac
+
+        INSTALL_PID=$!
+        PERCENT=31
+        while kill -0 "$INSTALL_PID" 2>/dev/null; do
+            tail_output=$(tail -n 5 "$TMP_LOG" | strip_ansi | tr -d '\r')
+            # Add left padding, truncate width, and wrap safely
+            tail_output=$(echo "$tail_output" \
+            | sed 's/^/   /' \
+            | fold -s -w 80 \
+            | tail -n 5)
+            update_progress_bar "$PERCENT" "$title\n\n$tail_output"
+            PERCENT=$((PERCENT + 1))
+            [ "$PERCENT" -ge 90 ] && PERCENT=90
+            sleep 1
+        done
+
+        wait "$INSTALL_PID" || error_exit "Base dependency installation failed"
+        update_progress_bar "100" "Base dependencies installed successfully"
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "Installing Dependencies" \
+               --gauge "Preparing system..." 15 80 0 
+    show_dialog "info" "Dependencies Installation" "All Base dependencies 
installed successfully"
+    log "Base dependencies installed successfully"
+    
+    rm -f "$TMP_LOG"
+}
+
+# Function to show available component versions from repository for 
SELECTED_COMPONENTS
+show_components_versions() {
+    local versions=()
+    local component version_info
+
+    if [[ " ${SELECTED_COMPONENTS[*]} " =~ " management " ]] && [[ ! " 
${SELECTED_COMPONENTS[*]} " =~ " mysql " ]]; then
+        SELECTED_COMPONENTS+=("mysql")
+    fi
+
+    for component in "${SELECTED_COMPONENTS[@]}"; do
+        case "$component" in
+            nfs)
+                if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
+                    version_info=$(apt-cache policy nfs-kernel-server 
2>/dev/null | awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" ]]; then
+                    version_info=$($PACKAGE_MANAGER info nfs-utils 2>/dev/null 
| awk -F':' '/Version/ {gsub(/ /,"",$2); print $2}')
+                fi
+                versions+=("NFS Server: ${version_info:-Not Available}\n")
+                ;;
+            
+            mysql)
+                if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
+                    version_info=$(apt-cache policy mysql-server 2>/dev/null | 
awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" || "$PACKAGE_MANAGER" == 
"yum" ]]; then
+                    version_info=$($PACKAGE_MANAGER info mysql-server 
2>/dev/null | awk -F':' '/Version/ {gsub(/ /,"",$2); print $2}')
+                fi
+                versions+=("MySQL Server: ${version_info:-Not Available}\n")
+                ;;
+            
+            management)
+                if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
+                    version_info=$(apt-cache policy cloudstack-management 
2>/dev/null | awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" ]]; then
+                    version_info=$($PACKAGE_MANAGER info cloudstack-management 
2>/dev/null | awk -F':' '/Version/ {gsub(/ /,"",$2); print $2}')
+                fi
+                versions+=("CloudStack Management Server: ${version_info:-Not 
Available}\n")
+                ;;
+            
+            agent)
+                if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
+                    version_info=$(apt-cache policy cloudstack-agent 
2>/dev/null | awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" ]]; then
+                    version_info=$($PACKAGE_MANAGER info cloudstack-agent 
2>/dev/null | awk -F':' '/Version/ {gsub(/ /,"",$2); print $2}')
+                fi
+                versions+=("CloudStack KVM Agent: ${version_info:-Not 
Available}\n")
+                ;;
+            
+            usage)
+                if [[ "$PACKAGE_MANAGER" == "apt" ]]; then
+                    version_info=$(apt-cache policy cloudstack-usage 
2>/dev/null | awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" ]]; then
+                    version_info=$($PACKAGE_MANAGER info cloudstack-usage 
2>/dev/null | awk -F':' '/Version/ {gsub(/ /,"",$2); print $2}')
+                fi
+                versions+=("CloudStack Usage Server: ${version_info:-Not 
Available}\n")
+                ;;
+        esac
+    done
+    show_dialog info "Component Versions" "Available versions from 
repository:\n\n$(printf '%s\n' "${versions[@]}")" 6 10 55
+}
+
+# Component installation and configuration functions
+#-------------------------------------------------------------------------------
+
+# Function to install CloudStack Management Server
+install_management_server() {
+    local package_name="cloudstack-management"
+    local tracker_key="management_installed"
+    if is_step_tracked "$tracker_key"; then
+        show_dialog "info" "Management Server Installation" "Management Server 
is already installed.\n\nSkipping installation."
+        log "CloudStack Management Server is already installed. Skipping 
installation."
+        return 0
+    fi
+    if is_package_installed "$package_name"; then
+        log "CloudStack Management Server is already installed."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+    install_pkg_with_progress_bar "CloudStack Management Server" 
"$package_name" "$tracker_key"
+}
+
+# Function to configure CloudStack Management Server database
+configure_management_server_database() {
+    local title="CloudStack Database Deployment"
+    local tracker_key="db_deployed"
+    if is_step_tracked "$tracker_key"; then
+        log "CloudStack database is already deployed. Skipping deployment."
+        show_dialog "info" "$title" "$title is already done.\n\nSkipping it."
+        return 0
+    fi
+    log "Starting CloudStack database deployment..."
+    if ! systemctl is-active $MYSQL_SERVICE > /dev/null; then
+         show_dialog "msg" "$title" "MySQL service is not running. Please 
start MySQL before proceeding." 
+        return 1
+    fi
+
+    local db_user="cloud"
+    local db_pass="cloud"
+    if [[ -f "/etc/cloudstack/management/db.properties" ]]; then
+        if mysql -u"$db_user" -p"$db_pass" -e "USE cloud; SHOW TABLES LIKE 
'version';" &>/dev/null; then
+            local current_db_host=$(grep "^cluster.node.IP" 
/etc/cloudstack/management/db.properties | cut -d= -f2)
+            if ! dialog --title "Info" --yesno "CloudStack database appears to 
be already configured.\nCurrent database host: $current_db_host\n\nDo you want 
to reconfigure it?" 10 60; then
+                show_dialog "info" "$title" "Skipping database configuration."
+                set_tracker_field "$tracker_key" "yes"
+                return 0
+            fi
+        fi
+    fi
+
+    if [ -z "$BRIDGE" ]; then
+        BRIDGE=$(dialog --inputbox "Enter the bridge interface name:" 8 50 
"$BRIDGE" 3>&1 1>&2 2>&3)
+    fi
+    if [[ -z "$BRIDGE" ]]; then
+        show_dialog "msg" "$title" "Bridge interface cannot be 
empty.\nAborting."
+        return 1
+    fi
+
+    # Get the bridge IP
+    cloudbr0_ip=$(ip -4 addr show "$BRIDGE" | awk '/inet / {print $2}' | cut 
-d/ -f1)
+    if [[ -z "$cloudbr0_ip" ]]; then
+        show_dialog "msg" "$title" "Could not determine IP address of 
interface '$BRIDGE'.\nAborting."
+        return 1
+    fi
+
+    show_dialog "info" "$title" "Starting CloudStack database deployment using 
IP: $cloudbr0_ip"
+    {
+        cloudstack-setup-databases cloud:cloud@localhost --deploy-as=root: -i 
"$cloudbr0_ip" 2>&1 | \
+            while IFS= read -r line; do
+                msg=$(echo "$line" | strip_ansi)
+                update_progress_bar "50" "Deploying CloudStack 
Database...\n\n$msg"
+            done
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "$title" \
+               --gauge "Starting database deployment..." 10 70 0
+
+    {
+        echo "# Starting CloudStack Management Server setup..."
+        cloudstack-setup-management 2>&1 | \
+            while IFS= read -r line; do
+                msg=$(echo "$line" | strip_ansi)
+                update_progress_bar "75" "Starting Management 
Server...\n\n$msg"
+            done
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "Management Server Setup" \
+               --gauge "Starting management server setup..." 10 72 0
+
+    sleep 5
+    set_tracker_field "$tracker_key" "yes"
+    show_dialog "info" "CloudStack Configuration" "CloudStack Management 
Server has been configured."
+}
+
+# Function to install MySQL Server and configure it for CloudStack
+install_mysql_server() {
+    local package_name="mysql-server"
+    local tracker_key="mysql_installed"
+    if is_step_tracked "$tracker_key"; then
+        log "MySQL is already installed. Skipping installation."
+        show_dialog "info" "MySQL Server Installation" "MySQL Server is 
already installed.\n\nSkipping installation."
+        return 0
+    fi
+
+    if is_package_installed "$package_name"; then
+        log "MySQL Server is already installed."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+    install_pkg_with_progress_bar "MySQL Server" "$package_name" "$tracker_key"
+    start_service_with_progress "$MYSQL_SERVICE" "MySQL" 60 90
+}
+
+configure_mysql_for_cloudstack() {
+    local tracker_key="mysql_configured"
+    if is_step_tracked "$tracker_key"; then
+        log "MySQL is already configured for CloudStack. Skipping 
configuration."
+        show_dialog "info" "MySQL Server Configuration" "MySQL Server is 
already configured.\n\nSkipping configuration."
+        return 0
+    fi
+    log "Starting MySQL configuration..."
+    local title="MySQL Configuration"
+    MYSQL_VERSION=$(mysql -V 2>/dev/null || echo "MySQL not found")
+    if [[ "$MYSQL_VERSION" == "MySQL not found" ]]; then
+        show_dialog "msg" "$title" "MySQL is not installed. Please install 
MySQL first."
+        return 1
+    fi
+  
+    local config_file="$MYSQL_CONF_DIR/cloudstack.cnf"
+    if [[ -f "$config_file" ]]; then
+        show_dialog "info" "$title" "Configuration already exists 
at:\n$config_file\n\nSkipping MySQL setup."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+
+    mkdir -p "$MYSQL_CONF_DIR"
+
+    if ! systemctl is-active --quiet $MYSQL_SERVICE; then
+        dialog --title "$title" --msgbox "MySQL service is not running. Please 
start MySQL before proceeding." 6 50
+        return 1
+    fi
+
+    sqlmode="$(mysql -B -e "show global variables like 'sql_mode'" 2>/dev/null 
| grep sql_mode | awk '{ print $2; }' | sed -e 's/ONLY_FULL_GROUP_BY,//')"
+
+    if [[ -z "$sqlmode" ]]; then
+        dialog --msgbox "Failed to fetch current SQL mode. Aborting." 6 50
+        return 1
+    fi
+  
+    cat > "$config_file" <<EOF
+[mysqld]
+server_id = 1
+sql_mode = $sqlmode
+innodb_rollback_on_timeout = 1
+innodb_lock_wait_timeout = 600
+max_connections = 1000
+log_bin = mysql-bin
+binlog_format = ROW
+EOF
+
+    systemctl restart $MYSQL_SERVICE && \
+    show_dialog "info" "$title" "MySQL has been configured and restarted 
successfully."|| \
+    show_dialog "info" "$title" "Failed to restart MySQL. Please check the 
service manually."
+    set_tracker_field "$tracker_key" "yes"
+}
+
+# Function to install NFS Server
+install_nfs_server() {
+    local tracker_key="nfs_installed"
+    if is_step_tracked "$tracker_key"; then
+        log "NFS Server is already installed. Skipping installation."
+        show_dialog "info" "NFS Server Installation" "NFS Server is already 
installed.\n\nSkipping installation."
+        return 0
+    fi
+
+    if command -v exportfs &>/dev/null; then
+        log "NFS Server is already installed."
+        set_tracker_field "$tracker_key" "yes"
+        show_dialog "info" "NFS Server Installation" "NFS Server is already 
installed.\n\nSkipping installation."
+        return 0
+    fi
+
+    local package_name=""
+    case "$PACKAGE_MANAGER" in
+        apt)
+            package_name="nfs-kernel-server nfs-common quota"
+            ;;
+        dnf)
+            package_name="nfs-utils quota"
+            ;;
+    esac
+    install_pkg_with_progress_bar "NFS Server" "$package_name" "$tracker_key"
+}
+
+# Get local CIDR for NFS export configuration
+get_local_cidr() {
+  local_ip=$(ip -4 addr show scope global | grep inet | awk '{print $2}' | 
head -n1)
+  echo "$local_ip"  # e.g., 10.1.1.53/24
+}
+
+get_export_cidr() {
+  ip_cidr=$(get_local_cidr)
+  # Convert from 10.1.1.53/24 β†’ 10.1.1.0/24
+  base_ip=$(echo "$ip_cidr" | cut -d/ -f1)
+  prefix=$(echo "$ip_cidr" | cut -d/ -f2)
+
+  # Use ipcalc or manual mask conversion
+  if command -v ipcalc &>/dev/null; then
+    network=$(ipcalc "$base_ip/$prefix" | grep -w 'Network' | awk '{print $2}')
+  else
+    # Fallback: simple /24 assumption
+    network="${base_ip%.*}.0/$prefix"
+  fi
+  echo "$network"
+}
+
+# Configure NFS Server
+configure_nfs_server() {
+    local tracker_key="nfs_configured"
+    local title="NFS Server Configuration"
+    if is_step_tracked "$tracker_key"; then
+        log "NFS Server is already configured. Skipping configuration."
+        show_dialog "info" "$title" "NFS Server is already 
configured.\n\nSkipping configuration."
+        return 0
+    fi
+    log "Starting NFS storage configuration..."
+
+    if [[ -d "/export" ]] && grep -q "^/export " /etc/exports; then
+        show_dialog "info" "$title" "NFS Server is already 
configured.\n\nSkipping configuration."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+
+    local export_cidr
+    export_cidr=$(get_export_cidr)
+    # Step 1: Create exports and directories
+    mkdir -p /export/primary /export/secondary
+    if ! grep -q "^/export " /etc/exports; then
+        echo "/export  
${export_cidr}(rw,async,no_root_squash,no_subtree_check)" >> /etc/exports
+    fi
+
+    exportfs -a
+
+    # Step 2: Configure ports and services based on distro
+    if [[ "$OS_TYPE" =~ ^(ubuntu|debian)$ ]]; then
+        sed -i -e 's/^RPCMOUNTDOPTS="--manage-gids"$/RPCMOUNTDOPTS="-p 892 
--manage-gids"/g' /etc/default/nfs-kernel-server
+        sed -i -e 's/^STATDOPTS=$/STATDOPTS="--port 662 --outgoing-port 
2020"/g' /etc/default/nfs-common
+        grep -q 'NEED_STATD=yes' /etc/default/nfs-common || echo 
"NEED_STATD=yes" >> /etc/default/nfs-common
+        sed -i -e 's/^RPCRQUOTADOPTS=$/RPCRQUOTADOPTS="-p 875"/g' 
/etc/default/quota
+
+        systemctl restart nfs-kernel-server
+        SERVICE_STATUS=$?
+    
+    elif [[ "$OS_TYPE" =~ ^(rhel|centos|ol|rocky|almalinux)$ ]]; then
+    # Set ports in /etc/sysconfig/nfs if needed
+    cat <<EOF >> /etc/sysconfig/nfs
+MOUNTD_PORT=892
+STATD_PORT=662
+STATD_OUTGOING_PORT=2020
+LOCKD_TCPPORT=32803
+LOCKD_UDPPORT=32769
+RQUOTAD_PORT=875
+EOF
+
+    systemctl enable --now rpcbind nfs-server
+    systemctl restart nfs-server
+    SERVICE_STATUS=$?
+
+    # Open firewall ports if firewalld is running
+    if systemctl is-active --quiet firewalld; then
+      firewall-cmd --permanent --add-service=nfs
+      firewall-cmd --permanent --add-service=mountd
+      firewall-cmd --permanent --add-service=rpc-bind
+      firewall-cmd --reload
+    fi
+  else
+    show_dialog "info" "$title"  "Unsupported distribution: $OS_TYPE"
+    return 1
+  fi
+
+  # Step 3: Final result
+  if [[ $SERVICE_STATUS -eq 0 ]]; then
+    exports_list=$(exportfs)
+    show_dialog "info" "$title" "NFS Server configured and restarted 
successfully.\n\nCurrent exports:\n$exports_list"
+    set_tracker_field "$tracker_key" "yes"
+    log "NFS server configured successfully."
+  else
+    show_dialog "info" "$title" "Failed to restart NFS server. Please check 
the service logs."
+  fi
+}
+
+# Function to install KVM Agent and configure KVM host
+install_kvm_agent() {
+    local package_name="cloudstack-agent"
+    local tracker_key="agent_installed"
+    if is_step_tracked "$tracker_key"; then
+        log "KVM Agent is already installed. Skipping installation."
+        show_dialog "info" "Cloudstack Agent Installation" "CloudStack Agent 
is already installed.\n\nSkipping installation."
+        return 0
+    fi
+    if is_package_installed "$package_name"; then
+        log "KVM Agent is already installed."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+    install_pkg_with_progress_bar "CloudStack Agent" "$package_name" 
"$tracker_key"
+}
+
+configure_kvm_agent() {
+    local tracker_key="agent_configured"
+    local title="CloudStack Agent Configuration"
+    if is_step_tracked "$tracker_key"; then
+        log "KVM Agent is already configured. Skipping configuration."
+        show_dialog "info" "$title" "CloudStack Agent is already 
configured.\n\nSkipping configuration."
+        return 0
+    fi
+    log "Starting CloudStack Agent configuration..."
+    show_dialog "info" "$title" "Starting CloudStack Agent configuration..."
+
+    # Configure VNC
+    {
+        update_progress_bar "10"  "Configuring VNC access..."
+        if sed -i -e 's/\#vnc_listen.*$/vnc_listen = "0.0.0.0"/g' 
/etc/libvirt/qemu.conf; then
+            update_progress_bar "25" "VNC configuration successful"
+        else
+            error_exit "Failed to configure VNC"
+        fi
+
+        if ! grep '^LIBVIRTD_ARGS="--listen"' /etc/default/libvirtd > 
/dev/null; then
+            echo 'LIBVIRTD_ARGS="--listen"' >> /etc/default/libvirtd
+        fi
+
+        if ! grep -q '^remote_mode="legacy"' /etc/libvirt/libvirtd.conf; then
+            echo 'remote_mode="legacy"' >> /etc/libvirt/libvirtd.conf
+        fi
+
+        update_progress_bar "40" "Setting up libvirt TCP access..."
+        LIBVIRT_CONF="/etc/libvirt/libvirtd.conf"
+        declare -A libvirt_settings=(
+            ["listen_tcp"]="1"
+            ["listen_tls"]="0"
+            ["tcp_port"]="\"16509\""
+            ["mdns_adv"]="0"
+            ["auth_tcp"]="\"none\""
+        )
+        touch "$LIBVIRT_CONF"
+
+        for key in "${!libvirt_settings[@]}"; do
+            # If key exists (commented or uncommented), replace it
+            if grep -Eq "^\s*#?\s*$key\s*=" "$LIBVIRT_CONF"; then
+                sed -i "s|^\s*#\?\s*$key\s*=.*|$key = 
${libvirt_settings[$key]}|" "$LIBVIRT_CONF"
+            else
+                # Key doesn't exist, append to file
+                echo "$key = ${libvirt_settings[$key]}" >> "$LIBVIRT_CONF"
+            fi
+        done
+
+        update_progress_bar "60" "Configuring libvirt sockets..."
+        systemctl mask libvirtd.socket \
+            libvirtd-ro.socket \
+            libvirtd-admin.socket \
+            libvirtd-tls.socket \
+            libvirtd-tcp.socket &>/dev/null
+
+        update_progress_bar "75" "Configuring security policies..."
+        case "$OS_TYPE" in
+            ubuntu|debian)
+                echo "# Configuring AppArmor..."
+                if command -v apparmor_parser >/dev/null; then
+                    # Check if profiles exist before trying to disable them
+                    local profiles=(
+                        "/etc/apparmor.d/usr.sbin.libvirtd"
+                        "/etc/apparmor.d/usr.lib.libvirt.virt-aa-helper"
+                    )
+                    
+                    for profile in "${profiles[@]}"; do
+                        if [[ -f "$profile" ]]; then
+                            if [[ ! -L "/etc/apparmor.d/disable/$(basename 
"$profile")" ]]; then
+                                ln -sf "$profile" "/etc/apparmor.d/disable/" 
2>/dev/null
+                                if [[ -f "$profile" ]]; then
+                                    if ! apparmor_parser -R "$profile" 
2>/dev/null; then
+                                        update_progress_bar "80" "# Warning: 
Failed to remove profile: $(basename "$profile")"
+                                    fi
+                                fi
+                            else
+                                echo "Profile $(basename "$profile") already 
disabled"
+                            fi
+                        else
+                            echo "Profile $profile not found, skipping"
+                        fi
+                    done
+                    
+                    # Restart AppArmor service to apply changes
+                    if systemctl is-active --quiet apparmor; then
+                        systemctl restart apparmor &>/dev/null || true
+                    fi
+                else
+                    echo "AppArmor not installed, skipping configuration"
+                fi
+                ;;
+            rhel|centos|ol|rocky|almalinux)
+                # SELinux configuration if needed
+                setsebool -P virt_use_nfs 1
+                ;;
+        esac
+
+        update_progress_bar "85" "Configuring firewall..."
+        ports=(
+            "22"           # SSH
+            "1798"         # CloudStack Management Server
+            "16509"        # Libvirt
+            "16514"        # Libvirt
+            "5900:6100"    # VNC
+            "49152:49216"  # Live Migration
+        )
+        case "$OS_TYPE" in
+            ubuntu|debian)
+                if command -v ufw >/dev/null; then
+                    for port in "${ports[@]}"; do
+                        ufw allow proto tcp from any to any port "$port"
+                    done
+                        ufw reload
+                else
+                    warn_msg "UFW not found, skipping firewall configuration"
+                fi
+                ;;
+            rhel|centos|ol|rocky|almalinux)
+                if command -v firewall-cmd >/dev/null; then
+                    for port in "${ports[@]}"; do
+                        firewall-cmd --permanent --add-port="$port"
+                    done
+                    firewall-cmd --reload
+                fi
+                ;;
+        esac
+        systemctl restart libvirtd
+        sleep 2
+
+        update_progress_bar "90" "Update agent.properties!"
+        AGENT_PROPERTIES="/etc/cloudstack/agent/agent.properties"
+        if [ -f "$AGENT_PROPERTIES" ]; then
+            local agent_guid=$(uuidgen)
+            sed -i '/^guid=/d' "$AGENT_PROPERTIES"
+            sed -i '/^private\.network\.device=/d' "$AGENT_PROPERTIES"
+            {
+                echo "guid=$agent_guid"
+                echo "private.network.device=$BRIDGE"
+            } >> "$AGENT_PROPERTIES"
+        else
+            error_exit "Agent properties file not found at $AGENT_PROPERTIES"
+        fi
+    
+        systemctl restart cloudstack-agent
+        sleep 5
+        update_progress_bar "100" "KVM host configuration completed!"
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "KVM Agent Configuration" \
+               --gauge "Configuring KVM Agent..." 10 70 0
+
+    if ! systemctl is-active --quiet libvirtd; then
+        show_dialog "msg" "$title" "Libvirt service is not running! Please 
check system logs."
+    fi
+    set_tracker_field "$tracker_key" "yes"
+}
+
+# Function to install CloudStack Usage Server and configure it
+install_usage_server() {
+    local package_name="cloudstack-usage"
+    local tracker_key="usage_installed"
+    if is_step_tracked "$tracker_key"; then
+        log "CloudStack Usage Server is already installed. Skipping 
installation."
+        show_dialog "info" "Usage Server Installation" "CloudStack Usage 
Server is already installed.\n\nSkipping installation."
+        return 0
+    fi
+    if is_package_installed "$package_name"; then
+        log "CloudStack Usage Server is already installed."
+        set_tracker_field "$tracker_key" "yes"
+        return 0
+    fi
+
+    # check if mysql and management server are installed
+    if ! is_package_installed "cloudstack-management" "mysql-server"; then
+        error_exit "CloudStack Management Server and MySQL Server must be 
installed before installing the Usage Server."
+    fi
+
+    install_pkg_with_progress_bar "CloudStack Usage Server" "$package_name" 
"$tracker_key"
+    start_service_with_progress "$package_name" "CloudStack Usage" 60 90
+}
+
+configure_usage_server() {
+    log "Configuring CloudStack Usage Server..."
+    local tracker_key="usage_configured"
+    if is_step_tracked "$tracker_key"; then
+        log "Usage Server is already configured. Skipping configuration."
+        show_dialog "info" "Usage Server Configuration" "CloudStack Usage 
Server is already configured.\n\nSkipping configuration."
+        return 0
+    fi
+    log "Starting Usage Server configuration..."
+    show_dialog "info" "Usage Server Configuration" "Starting Usage Server 
configuration..."
+    # confirm db.properties and key exist
+    local db_properties="/etc/cloudstack/usage/db.properties"
+    local key_file="/etc/cloudstack/usage/key"
+
+    if [[ ! -f "$db_properties" || ! -f "$key_file" ]]; then
+        show_dialog "msg" "Usage Server Configuration" "Database configuration 
files not found!\nPlease ensure CloudStack Management Server is configured."
+        return 1
+    fi
+    set_tracker_field "$tracker_key" "yes"
+    sleep 2
+}
+
+# Function to present component selection dialog
+select_components_to_setup() {
+    local title="CloudStack Component Selection"
+    local temp_file=$(mktemp)
+    
+    if dialog --clear --backtitle "$SCRIPT_NAME" \
+           --title "$title" \
+           --checklist "Select CloudStack components to install:" 15 70 6 \
+           "management" "CloudStack Management Server" on \
+           "usage" "CloudStack Usage Server" off \
+           "agent" "KVM Agent" on \
+           "nfs" "NFS Server" on \
+           2> "$temp_file"; then
+
+        mapfile -t SELECTED_COMPONENTS < <(tr ' ' '\n' < "$temp_file" | tr -d 
'"')  
+        if [[ ${#SELECTED_COMPONENTS[@]} -eq 0 ]]; then
+            error_exit "No components selected"
+        fi
+        log "Selected components: ${SELECTED_COMPONENTS[*]}"
+    else
+        log "Component selection cancelled by user"
+        show_dialog "msg" "$title" "Component selection cancelled by user."
+        return 1
+    fi
+    rm -f "$temp_file"
+}
+
+# Displays validation summary for SELECTED_COMPONENTS
+show_validation_summary() {
+    local summary=""
+    local status_ok=true
+
+    # 1. Network Validation
+    if is_cloudstack_pkg_selected; then
+        if ip link show "$BRIDGE" &>/dev/null; then
+            local bridge_ip=$(ip -4 addr show "$BRIDGE" | awk '/inet / {print 
$2}' | cut -d/ -f1)
+            if [[ -n "$bridge_ip" ]]; then
+                summary+="βœ“ Network: Bridge $BRIDGE configured with IP 
$bridge_ip\n"
+            else
+                summary+="βœ— Network: Bridge $BRIDGE has no IP address\n"
+                status_ok=false
+            fi
+        else
+            summary+="βœ— Network: Bridge $BRIDGE not found\n"
+            status_ok=false
+        fi
+    fi
+
+    # 2. MySQL Validation
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " mysql " ]]; then
+        if systemctl is-active --quiet "$MYSQL_SERVICE"; then
+            if [[ -f "$MYSQL_CONF_DIR/cloudstack.cnf" ]]; then
+                summary+="βœ“ MySQL: Running and configured\n"
+            else
+                summary+="βœ— MySQL: Running but missing CloudStack 
configuration\n"
+                status_ok=false
+            fi
+        else
+            summary+="βœ— MySQL: Not running\n"
+            status_ok=false
+        fi
+    fi
+
+    # 3. NFS Validation
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " nfs " ]]; then
+        if systemctl is-active --quiet nfs-server; then
+            if grep -q "^/export" /etc/exports; then
+                summary+="βœ“ NFS: Server running and exports configured\n"
+            else
+                summary+="βœ— NFS: Server running but exports not configured\n"
+                status_ok=false
+            fi
+        else
+            summary+="βœ— NFS: Server not running\n"
+            status_ok=false
+        fi
+    fi
+
+    # 4. Management Server Validation
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " management " ]]; then
+        if systemctl is-active --quiet cloudstack-management; then
+            if [[ -f "/etc/cloudstack/management/db.properties" ]]; then
+                summary+="βœ“ Management Server: Running and configured\n"
+            else
+                summary+="βœ— Management Server: Running but configuration 
incomplete\n"
+                status_ok=false
+            fi
+        else
+            summary+="βœ— Management Server: Not running\n"
+            status_ok=false
+        fi
+    fi
+
+    # 5. KVM Agent Validation
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " agent " ]]; then
+        if systemctl is-active --quiet libvirtd; then
+            if grep -q "^listen_tcp = 1" /etc/libvirt/libvirtd.conf; then
+                summary+="βœ“ KVM: Libvirt running and configured\n"
+                if systemctl is-active --quiet cloudstack-agent; then
+                    summary+="βœ“ KVM: CloudStack agent running\n"
+                else
+                    summary+="βœ— KVM: CloudStack agent not running\n"
+                    status_ok=false
+                fi
+            else
+                summary+="βœ— KVM: Libvirt TCP not configured\n"
+                status_ok=false
+            fi
+        else
+            summary+="βœ— KVM: Libvirt not running\n"
+            status_ok=false
+        fi
+    fi
+
+    # 6. Usage Server Validation
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " usage " ]]; then
+        if systemctl is-active --quiet cloudstack-usage; then
+            summary+="βœ“ Usage Server: Running\n"
+        else
+            summary+="βœ— Usage Server: Not running\n"
+            status_ok=true   # not critical for zone deployment

Review Comment:
   This should be `status_ok=false` instead of `status_ok=true`. When the Usage 
Server is not running, the validation should fail (false), not succeed (true). 
The comment indicates it's 'not critical' but setting status_ok=true 
contradicts the validation logic and will incorrectly report success when the 
service is down.
   ```suggestion
               status_ok=false   # not critical for zone deployment
   ```



##########
installer.sh:
##########
@@ -0,0 +1,2287 @@
+#!/usr/bin/env bash
+# c8k.in/installer.sh - Easiest Apache CloudStack Installer
+# Install with this command (from your Ubuntu/EL host):
+#
+# curl -sSfL https://c8k.in/installer.sh | bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -euo pipefail  # Exit on error, undefined vars, pipe failures
+
+# Global variables
+SCRIPT_NAME="Apache CloudStack Installer"
+CS_LOGFILE="$PWD/cloudstack-install.log"
+TRACKER_FILE="$PWD/cloudstack-installer-tracker.conf"
+
+OS_TYPE=""
+PACKAGE_MANAGER=""
+SELECTED_COMPONENTS=()
+ZONE_TYPE=""
+MYSQL_SERVICE=""
+MYSQL_CONF_DIR=""
+
+BRIDGE=cloudbr0
+HOST_IP=
+GATEWAY=
+DNS="8.8.8.8"
+NETMASK="255.255.255.0"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 1 = Prompt mode (interactive), 0 = Silent (non-interactive)
+PROMPT=1
+
+#-------------------------------------------------------------------------------
+# Utility functions
+#-------------------------------------------------------------------------------
+
+# checks if prompt mode is enabled
+is_interactive() { (( PROMPT )); }
+is_silent()      { (( !PROMPT )); }
+
+# Log related utilities
+#-------------------------------------------------------------------------------
+
+# Clean up ANSI escape sequences from logs
+strip_ansi() {
+    sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'
+}
+
+# Logging function
+log() {
+    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$CS_LOGFILE"
+}
+
+# Error handling function
+error_exit() {
+    echo -e "${RED}ERROR: $1${NC}" >&2
+    log "ERROR: $1"
+    exit 1
+}
+
+# Success message function
+success_msg() {
+    echo -e "${GREEN}SUCCESS: $1${NC}"
+    log "SUCCESS: $1"
+}
+
+# Warning message function
+warn_msg() {
+    echo -e "${YELLOW}WARNING: $1${NC}"
+    log "WARNING: $1"
+}
+
+# Info message function
+info_msg() {
+    echo -e "${BLUE}INFO: $1${NC}"
+    log "INFO: $1"
+}
+
+# Utilities for resource checks
+#-------------------------------------------------------------------------------
+
+# Check if running as root
+check_root() {
+    if [[ $EUID -ne 0 ]]; then
+        error_exit "This script must be run as root. Please use 'sudo $0'"
+    fi
+}
+
+# Check system resources (RAM and Disk)
+check_system_resources() {
+    MIN_RAM_KB=$((8 * 1024 * 1024))  # 8 GB in KB
+    TOTAL_RAM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
+
+    # Check if RAM is within the desired range
+    if [ "$TOTAL_RAM_KB" -ge "$MIN_RAM_KB" ]; then
+        success_msg "RAM check passed: $(awk "BEGIN {printf \"%.2f\", 
$TOTAL_RAM_KB/1024/1024}") GB"
+    else
+        error_exit "RAM check failed: System has $(awk "BEGIN {printf 
\"%.2f\", $TOTAL_RAM_KB/1024/1024}") GB RAM"
+    fi
+
+    MIN_DISK_GB=75  # Minimum disk space in GB
+    TOTAL_DISK_GB=$(df / | tail -1 | awk '{print $2}' | awk '{printf "%.0f", 
$1/1024/1024}')
+
+    # Check if disk space is within the desired range
+    if [ "$TOTAL_DISK_GB" -ge "$MIN_DISK_GB" ]; then
+        success_msg "Disk space check passed: $TOTAL_DISK_GB GB available"
+    else
+        error_exit "Disk space check failed: System has only $TOTAL_DISK_GB GB 
available"
+    fi
+}
+
+# Ensure kvm modules are loaded for KVM Agent
+check_kvm_support() {
+    info_msg "Checking KVM prerequisites..."
+    if ! grep -E 'vmx|svm' /proc/cpuinfo >/dev/null; then
+        error_exit "CPU does not support hardware virtualization (agent)"
+    fi
+    success_msg "βœ“ CPU virtualization support detected"
+
+    if ! lsmod | grep -q kvm; then
+        error_exit "KVM kernel module is not loaded"
+    fi
+    success_msg "βœ“ KVM kernel module loaded"
+}
+
+# Initialize OS_TYPE, PACKAGE_MANAGER, MYSQL_SERVICE, MYSQL_CONF_DIR
+detect_os() {
+    if [[ ! -f /etc/os-release ]]; then
+        error_exit "Cannot detect operating system. /etc/os-release not found."
+    fi
+    
+    source /etc/os-release
+    OS_TYPE=$ID
+    OS_VERSION=$VERSION_ID
+    VERSION_CODENAME=${VERSION_CODENAME:-}
+    
+    case "$OS_TYPE" in
+        ubuntu|debian)
+            PACKAGE_MANAGER="apt"
+            MYSQL_SERVICE="mysql"
+            MYSQL_CONF_DIR="/etc/mysql/mysql.conf.d"
+            ;;
+        rhel|centos|ol|rocky|almalinux)
+            PACKAGE_MANAGER="dnf"
+            MYSQL_SERVICE="mysqld"
+            MYSQL_CONF_DIR="/etc/my.cnf.d"
+            ;;
+        *)
+            echo "Unsupported OS: $OS_TYPE"
+            exit 1
+            ;;
+    esac
+
+    log "OS Detection: $OS_TYPE with package manager: $PACKAGE_MANAGER"
+}
+
+# Tracker file utilities
+#-------------------------------------------------------------------------------
+# maintains tracker values for installation and configuration steps
+
+declare -A tracker_values
+
+# loads tracker file data into tracker_values
+load_tracker() {
+    if [[ ! -f "$TRACKER_FILE" ]]; then
+        echo "# CloudStack Installer Tracker Config" > "$TRACKER_FILE"
+        echo "# Created on: $(date)" >> "$TRACKER_FILE"
+        return 0
+    fi
+
+    while IFS='=' read -r key value; do
+        [[ -z "$key" || "$key" =~ ^# ]] && continue
+        tracker_values["$key"]="$value"
+    done < "$TRACKER_FILE"
+}
+
+get_tracker_field() {
+    local key="$1"
+    echo "${tracker_values[$key]:-}"
+}
+
+# Save or update a field in the tracker file
+set_tracker_field() {
+    local key="$1"
+    local value="$2"
+
+    # Update associative array
+    tracker_values["$key"]="$value"
+
+    # Update or append key=value in tracker file
+    if grep -q "^$key=" "$TRACKER_FILE"; then
+        sed -i "s|^$key=.*|$key=$value|" "$TRACKER_FILE"
+    else
+        echo "$key=$value" >> "$TRACKER_FILE"
+    fi
+}
+
+# checks if a step is already tracked in tracker file
+is_step_tracked() {
+    local key="$1"
+    [[ -n "${tracker_values[$key]:-}" ]]
+}
+
+# Utility functions for Repository setup
+#-------------------------------------------------------------------------------
+
+# Map Debian codenames to Ubuntu codenames for CloudStack repo
+get_ubuntu_codename_for_debian() {
+    case "$1" in
+        buster|bullseye)
+            echo "focal"
+            ;;
+        bookworm|trixie)
+            echo "jammy"
+            ;;
+        *)
+            echo "ERROR: Unsupported Debian codename '$1'" >&2
+            return 1
+            ;;
+    esac
+}
+
+#  Determine repo_path for CloudStack repository based on OS type and version
+determine_rpm_distro_version() {
+    # Extract major version (8 or 9) from version string
+    local major_version=${OS_VERSION%%.*}
+    case "$OS_TYPE" in
+        centos)
+            echo "centos/$major_version"
+            ;;
+        rhel)
+            echo "rhel/$major_version"
+            ;;
+        rocky|almalinux|ol)
+            echo "el/$major_version"
+            ;;
+        *)
+            error_exit "Unsupported OS type: $OS_TYPE"
+            ;;
+    esac
+}
+
+# Validates repository entry format
+validate_repo_entry() {
+    local os_type="$1"
+    local entry="$2"
+
+    # Basic check: not empty
+    if [[ -z "$entry" ]]; then
+        error_exit "CloudStack Repository entry cannot be empty."
+        return 1
+    fi
+
+    # Debian/Ubuntu repo line example:
+    # deb [signed-by=...] https://download.cloudstack.org/ubuntu noble 4.20
+    if [[ "$os_type" =~ ^(ubuntu|debian)$ ]]; then
+        if [[ ! "$entry" =~ https?:// ]]; then
+            error_exit "Invalid Repository entry must include a valid URL 
(http or https)."
+            return 1
+        fi
+    fi
+
+    # RHEL-family example:
+    # https://download.cloudstack.org/centos/9/4.20/
+    if [[ "$os_type" =~ ^(rhel|centos|rocky|almalinux|ol)$ ]]; then
+        if [[ ! "$entry" =~ ^https?:// ]]; then
+            error_exit "Invalid Repository baseurl must start with http:// or 
https://.";
+            return 1
+        fi
+    fi
+
+    # Optional: check version (warn, not fatal)
+    if [[ ! "$entry" =~ 4\.([1-9][0-9]) ]]; then

Review Comment:
   The regex pattern `4\.([1-9][0-9])` will not match single-digit minor 
versions like '4.9'. It requires exactly two digits where the first digit must 
be 1-9. This means versions like 4.19, 4.20, 4.21 would match, but 4.9 would 
not. Consider using `4\.([1-9][0-9]?)` to match both single and double-digit 
minor versions.
   ```suggestion
       if [[ ! "$entry" =~ 4\.([1-9][0-9]?) ]]; then
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to