This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack-installer.git


The following commit(s) were added to refs/heads/main by this push:
     new e9ef772  implement installer (#1)
e9ef772 is described below

commit e9ef7722f7ed2b1bb70ccc53b983342c7d584b70
Author: Manoj Kumar <[email protected]>
AuthorDate: Mon Dec 15 14:45:08 2025 +0530

    implement installer (#1)
    
    This adds new CloudStack one line Installer based on "dialog" utility, 
takes user through wizard like mode setting up CloudStack.
    
    It allows:
    
    - All-in-one box installation (Management, Agent, Usage, NFS, MySQL)
    - Custom Installation
    - Setting up CS repo
    - Deployment of Zone
    
    Notes:
    
    1. Management server and MySQL is installed in the same machine (Usage 
server as well)
    2. All-in-one box install is silent mode installation, default values are 
used for installation and zone deployment
    3. Installer is validated against Ubuntu 24.04, needs testing against OLE, 
Rocky etc.
    
    Screenshots:
    ![Screenshot 2025-10-27 at 10 42 48β€―
AM](https://github.com/user-attachments/assets/64c20e38-2d6a-44ba-8c6f-725f0c81b683)
---
 README.md    |  200 ++++-
 installer.sh | 2536 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2728 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 7976fa5..a98fea2 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,210 @@
 # [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 Apache CloudStack (version >= 4.20) installation on Ubuntu or EL 
based distro with x86_64 KVM. 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) |
+
+---
+
+## βœ… Validated Operating Systems
+
+| OS       | Version | Status |
+|----------|---------|--------|
+| Oracle   | 9       | βœ”οΈ Validated |
+| Oracle   | 8       | βœ”οΈ Validated |
+| Ubuntu   | 24.04   | βœ”οΈ Validated |
+| Ubuntu   | 22.04   | βœ”οΈ Validated |
+| Ubuntu   | 20.04   | βœ”οΈ Validated |
+| Rocky    | 9       | βœ”οΈ Validated |
+| Rocky    | 8       | βœ”οΈ Validated |
+| Alma     | 9       | βœ”οΈ Validated |
+| Alma     | 8       | βœ”οΈ Validated |
+
+### NOTE: EL10 Distributions (Experimental)
+Oracle Linux 10, AlmaLinux 10, and Rocky Linux 10 are supported **only** with 
CloudStack 4.22.0.0 or later.  
+#### Prerequisites - run before the installer
+
+##### AlmaLinux 10 / Rocky Linux 10
+
+```bash
+sudo dnf install -y epel-release
+sudo dnf install -y genisoimage
+```
+##### Oracle Linux 10
+
+```bash
+sudo dnf install -y oracle-epel-release-el10
+sudo dnf install -y genisoimage
+```
+
+---
+
+## πŸ“¦ 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.
+3. Experiment with configuration, template registration, Network and zone 
orchestration.
+4. Perfect for demo, workshops and PoCs.
+
+---
+
+## πŸ—‚οΈ Installation Logs & Tracking
+
+| File | Purpose |
+|------|----------|
+| `installer.log` | Detailed logs for all operations |
+| `cloudstack-installer-tracker.conf` | Tracks completed steps to allow safe 
re-runs |
+
+---
+
+## πŸ” Post-Install Access
+
+After successful setup, CloudStack is accessible at:
+
 ```
+URL: http://<management-server-ip>:8080/client
+Username: admin
+Password: password
+```
+---
+
+## ⚠️ Notes/Common Pitfalls
+
+- Avoid pre-installing Java before running this script. Exisiting java 
installation can cause version conflicts and may break the installer.
+- Use a proper base image when preparing the VM. The ISO should be  at least a 
Minimal Installation to ensure all required packages are available.
+- Installer can't continue if any other process has lock of package manager, 
you may see such error:
+
+    ```Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 
5774 (unattended-upgr)```
+
+- Zone Deployment form provides default parameters optimized for an all-in-one 
CloudStack installation. Review and adjust these values as needed for your 
deployment.
+
+---
+
+## 🧰 Utilities Used
+
+- `dialog` for interactive menus
+- `apt` or `dnf` for package management
+- `systemctl` for service management
+- `nmap` for IP discovery
+
+---
+
+## πŸͺͺ License
+
+Licensed under the **Apache License, Version 2.0**  
+See [LICENSE](http://www.apache.org/licenses/LICENSE-2.0) for details.
+
+---
+
 
 > [!IMPORTANT]
-> This deploys an all-in-a-box CloudStack installation that has the CloudStack 
management & usage server, CloudStack agent, MySQL DB, NFS and KVM on a single 
Linux host and is only useful for anyone who just wants to try CloudStack on a 
host or a VM, but does not want to [read the official 
docs](https://docs.cloudstack.apache.org). It makes several assumptions about 
the IaaS deployment, and tries to figure out host's network setup so the 
deployment could work out of the box. This is not  [...]
+> This is only useful for anyone who just wants to try CloudStack on a host or 
a VM, but does not want to [read the official 
docs](https://docs.cloudstack.apache.org). It makes several assumptions about 
the IaaS deployment, and tries to figure out host's network setup so the 
deployment could work out of the box. This is not advised for production 
deployment.
 
 Screenshot when the command finishes:
 
diff --git a/installer.sh b/installer.sh
new file mode 100644
index 0000000..7e36aa2
--- /dev/null
+++ b/installer.sh
@@ -0,0 +1,2536 @@
+#!/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/installer.log"
+TRACKER_FILE="$PWD/cloudstack-installer-tracker.conf"
+
+OS_TYPE=""
+PACKAGE_MANAGER=""
+SELECTED_COMPONENTS=()
+ZONE_TYPE=""
+MYSQL_PKG="mysql-server"
+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 grep -q vmx /proc/cpuinfo; then
+        MODULE="kvm_intel"
+    elif grep -q svm /proc/cpuinfo; then
+        MODULE="kvm_amd"
+    else
+        error_exit "Unable to determine CPU type for KVM module."
+    fi
+
+    modprobe "$MODULE" 2>/dev/null || true
+    # Validate loaded module
+    if ! grep -q "^${MODULE} " /proc/modules; then
+        error_exit "KVM module $MODULE is not loaded. Ensure virtualization is 
enabled in BIOS/UEFI."
+    fi
+    success_msg "βœ“ KVM kernel module ($MODULE) loaded"
+}
+
+validate_selinux() {
+    if command -v getenforce >/dev/null 2>&1; then
+        SELINUX_MODE=$(getenforce)
+        if [ "$SELINUX_MODE" = "Enforcing" ]; then
+            msg="SELinux is ENFORCING. Script cannot continue."
+            msg+="\nFix it by:"
+            msg+="\n  1) Edit /etc/selinux/config β†’ SELINUX=permissive"
+            msg+="\n  2) Run: setenforce 0"
+            msg+="\n  3) Reboot"
+            error_exit "$msg"
+        else
+            success_msg "βœ“ SELinux status: $SELINUX_MODE"
+        fi
+    else
+        success_msg "βœ“ SELinux not detected. Continuing..."
+    fi
+}
+
+validate_jre_version() {
+    if ! command -v java >/dev/null 2>&1; then
+        success_msg "βœ“ Java not found, good to go.."
+        return 0
+    fi
+    java_version=$(java -version 2>&1 | awk -F[\"_] 'NR==1{print $2}')
+    major_version=$(echo "$java_version" | cut -d. -f1)
+
+    if (( major_version < 17 )); then
+        error_exit "Looks like Java is pre-installed and may conflict with 
CloudStack. Java 17 or higher is required. Detected version: $java_version"
+    fi
+    success_msg "βœ“ Java version $java_version detected, good to go.."
+}
+
+# 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
+    OS_MAJOR="${OS_VERSION%%.*}"
+    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"
+            if [[ ("$OS_TYPE" == "rocky" || "$OS_TYPE" == "almalinux" || 
"$OS_TYPE" == "ol") && "$OS_MAJOR" =~ ^(10|11)$ ]]; then
+                MYSQL_PKG="mysql8.4-server"
+            fi
+            validate_selinux
+            ;;
+        *)
+            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 "$percent" "# 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}"
+    local verify="${5:-verify}"
+
+    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 [[ "$verify" == "verify" ]]; then
+            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 started (verification skipped)."
+        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.
+    "
+    log "$banner"
+
+    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..."
+        mkdir -p /etc/apt/keyrings
+
+        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"
+    mkdir -p /etc/yum.repos.d/
+    {
+        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
+}
+
+update_system_time() {
+    case $PACKAGE_MANAGER in
+        dnf)
+            if ! systemctl is-active chronyd >/dev/null 2>&1; then
+                systemctl enable --now chronyd >/dev/null 2>&1
+            fi
+            chronyc makestep >/dev/null 2>&1
+            ;;
+
+        apt)
+            # Detect what NTP mechanism is available
+            if systemctl is-active ntp >/dev/null 2>&1; then
+                info_msg ">>> ntp detected, forcing sync via ntpd..."
+                ntpd -gq >/dev/null 2>&1 || true
+            elif systemctl is-active chrony >/dev/null 2>&1; then
+                info_msg ">>> chrony detected, forcing sync via chronyc..."
+                chronyc makestep >/dev/null 2>&1  || true
+            else
+                info_msg ">>> Falling back to systemd-timesyncd..."
+                systemctl enable --now systemd-timesyncd >/dev/null 2>&1  || 
true
+                timedatectl set-ntp true >/dev/null 2>&1 || true
+                systemctl restart systemd-timesyncd >/dev/null 2>&1 || true
+            fi
+            ;;
+    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 gnupg 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_PKG" 2>/dev/null | 
awk '/Candidate:/ {print $2}')
+                elif [[ "$PACKAGE_MANAGER" == "dnf" || "$PACKAGE_MANAGER" == 
"yum" ]]; then
+                    version_info=$($PACKAGE_MANAGER info "$MYSQL_PKG" 
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
+}
+
+cmk_api_call() {
+    local jq_filter="$1"
+    shift 1
+    local cmd=(cmk "$@")     # remaining args are the CMK call
+
+    # Run the CMK API call (capture stdout+stderr)
+    local cmd_output
+    cmd_output="$("${cmd[@]}" 2>&1)"
+    if ! echo "$cmd_output" | jq empty >/dev/null 2>&1; then
+        log "cmk API returned non-JSON response: $cmd_output"
+        return 1
+    fi
+    
+    # Extract value
+    local value
+    value=$(echo "$cmd_output" | jq -r "$jq_filter // empty")
+    # if value is empty
+    if [[ -z "$value" ]]; then
+        log "cmk returned empty value for jq filter: '$jq_filter' on output: 
$cmd_output"
+        return 1
+    fi
+    echo "$value"
+    return 0
+}
+
+cmk_api_fail_dialog_msg() {
+    local msg="$1"
+    local progress="${2:-100}"
+    local sleep_time="${3:-4}"
+    update_progress_bar "$progress" "$msg"
+    sleep "$sleep_time"
+    return 1
+}
+
+# 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" ]] && is_step_tracked 
"$tracker_key"; 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
+
+    db_exit=${PIPESTATUS[0]}
+    if [ $db_exit -ne 0 ]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+            --title "Database Setup Failed" \
+            --msgbox "CloudStack database setup failed." 10 70
+        error_exit "CloudStack database setup failed."
+    fi
+
+    {
+        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
+    mgmt_exit=${PIPESTATUS[0]}
+
+    if [ $mgmt_exit -ne 0 ]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+            --title "Management Setup Failed" \
+            --msgbox "CloudStack management setup failed" 10 70
+        error_exit "CloudStack management setup failed."
+    fi
+
+    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 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 "$MYSQL_PKG"; then
+        log "MySQL Server is already installed."
+        set_tracker_field "$tracker_key" "yes"
+    else
+        install_pkg_with_progress_bar "MySQL Server" "$MYSQL_PKG" 
"$tracker_key"
+    fi
+    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
+
+    local package_name=""
+    local nfs_svc="nfs-server"
+    case "$PACKAGE_MANAGER" in
+        apt)
+            package_name="nfs-kernel-server nfs-common quota"
+            nfs_svc="nfs-kernel-server"
+            ;;
+        dnf)
+            package_name="nfs-utils quota"
+            ;;
+    esac
+    if is_package_installed "$package_name"; then
+        log "NFS Server is already installed."
+        show_dialog "info" "NFS Server Installation" "NFS Server is already 
installed.\n\nSkipping installation."
+        set_tracker_field "$tracker_key" "yes"
+    else
+        install_pkg_with_progress_bar "NFS Server" "$package_name" 
"$tracker_key"
+    fi
+    start_service_with_progress "$nfs_svc" "NFS" 60 90
+}
+
+get_export_cidr() {
+    if [[ -z "$BRIDGE" ]]; then
+        error_exit "Error: BRIDGE interface not defined"
+    fi
+    ip_cidr=$(ip -o -f inet addr show "$BRIDGE" | awk '{print $4}')
+    if [[ -z "$ip_cidr" ]]; then
+        error_exit "No IPv4 CIDR found on interface $BRIDGE for NFS export 
configuration"
+    fi
+    # Extract IP and prefix from CIDR (10.1.1.53/24 β†’ base_ip=10.1.1.53, 
prefix=24)
+    base_ip="${ip_cidr%/*}"
+    prefix="${ip_cidr#*/}"
+
+    # 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 -q '^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 systemctl is-active --quiet firewalld; then
+                    for port in "${ports[@]}"; do
+                        # Convert port ranges from 5900:6100 -> 5900-6100 for 
firewalld
+                        range_port="${port/:/-}"
+                        firewall-cmd --permanent --add-port="$range_port"/tcp
+                    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_PKG"; 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" 40 90 
"noverify"
+}
+
+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" "Management Server" on \
+           "usage" "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 -qE '^tcp_port\s*=\s*"16509"' /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
+        fi
+    fi
+
+    # Log the result
+    echo -e "Validation Summary:\n$summary" >> "$CS_LOGFILE"
+
+    # Display final result in dialog
+    if $status_ok; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Validation Summary" \
+               --colors \
+               --msgbox "\Z2βœ“ All components are properly 
configured!\n\n\ZnComponent Status:\n\n$summary" 20 70
+        return 0
+    else
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Validation Summary" \
+               --colors \
+               --msgbox "\Z1⚠ Some components need attention!\n\n\ZnComponent 
Status:\n\n$summary" 20 70
+        return 1
+    fi
+}
+
+# Functions related to Zone deployment
+#-------------------------------------------------------------------------------
+
+# Prompt user to select zone deployment
+select_zone_deployment() {
+    dialog --backtitle "$SCRIPT_NAME" \
+           --title "Zone Deployment" \
+           --yesno "Would you like to deploy a new CloudStack Zone?\n\nThis 
will:\n\n1. Create a new Zone\n2. Configure Network\n3. Add the first Pod\n4. 
Add the first Cluster\n5. Add the first Host\n\nDeploy Zone now?" 15 60
+
+    return $?
+}
+
+# Function to check and confirm Management Server readiness
+wait_for_management_server() {
+    local timeout=600  # 10 minutes timeout
+    local interval=10  # Check every 10 seconds
+    local elapsed=0
+    local url="http://$HOST_IP:8080/client/api";
+
+    {
+        while [ $elapsed -lt $timeout ]; do
+            update_progress_bar "$((elapsed * 100 / timeout))"  "Waiting for 
Management Server to be ready...\n\nElapsed time: ${elapsed}s"
+            
+            local status_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
+            if [[ "$status_code" == "200" || "$status_code" == "401" ]]; then
+                update_progress_bar "100" "Management Server is ready!"
+                return 0
+            fi
+            
+            sleep $interval
+            elapsed=$((elapsed + interval))
+        done
+
+        update_progress_bar "100" "Timeout occurred while waiting for 
Management Server!"
+        return 1
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "Management Server Check" \
+               --gauge "Waiting for Management Server to start..." 10 70 0
+}
+
+# Function to check CloudMonkey installation and initialize it
+check_cloudmonkey_availability() {
+    {
+        echo "10"
+        echo "# Checking CloudMonkey installation..."
+        if ! command -v cmk &>/dev/null; then
+            update_progress_bar "100" "CloudMonkey (cmk) not found!"
+            return 1
+        fi
+
+        echo "50"
+        echo "# Initializing CloudMonkey..."
+        if ! cmk sync &>/dev/null; then
+            update_progress_bar "100" "Failed to initialize CloudMonkey!"
+            return 1
+        fi
+
+        update_progress_bar "100" "# CloudMonkey is ready!"
+        return 0
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "CloudMonkey Check" \
+               --gauge "Checking CloudMonkey..." 8 60 0
+}
+
+# Perform Advance Zone Deployment
+deploy_zone() {
+    local title="Zone Deployment"
+    local tracker_key="zone_deployment"
+    if is_step_tracked "$tracker_key"; then
+        log "$title is already installed. Skipping."
+            show_dialog "info" "$title" "$title is already done. Skipping!"
+            return 0
+    fi
+    log "Starting zone deployment..."
+    # check if management server is installed
+    if ! is_package_installed "cloudstack-management"; then
+        show_dialog "msg" "$title" "CloudStack Management Server is not 
installed.\nPlease install it before deploying a zone."
+        error_exit "CloudStack Management Server must be installed before 
deploying a zone."
+    fi
+
+    if [[ -z "$BRIDGE" ]]; then
+        BRIDGE=$(dialog --backtitle "$SCRIPT_NAME" \
+            --title "KVM Host Configuration" \
+            --inputbox "Enter the bridge interface name:" \
+            8 60 "$BRIDGE" 3>&1 1>&2 2>&3)
+    fi
+
+    HOST_IP=$(ip -4 addr show "$BRIDGE" | grep -oP '(?<=inet\s)\d+(\.\d+){3}' 
| head -1)
+    GATEWAY=$(ip route | grep default | grep "$BRIDGE" | awk '{print $3}')
+
+    local input_ip=""
+    local input_gw=""
+    if is_interactive; then
+        input_ip=$(dialog --backtitle "$SCRIPT_NAME" \
+            --title "Host Configuration" \
+            --inputbox "KVM Host IP: $HOST_IP\n\nWant to modify." 10 60 
"$HOST_IP" 3>&1 1>&2 2>&3)
+        input_gw=$(dialog --backtitle "$SCRIPT_NAME" \
+            --title "Host Configuration" \
+            --inputbox "Detected Gateway: $GATEWAY\n\nWant to modify." 10 60 
"$GATEWAY" 3>&1 1>&2 2>&3)  
+    fi
+    KVM_HOST_IP=${input_ip:-$HOST_IP}
+    GATEWAY=${input_gw:-$GATEWAY}
+
+    if [[ -z "$KVM_HOST_IP" || -z "$GATEWAY" ]]; then
+        error_exit "Could not determine host IP or gateway. Is bridge $BRIDGE 
configured properly?"
+    fi
+
+    # Prompt for root password before starting zone deployment
+    local MAX_ATTEMPTS=3
+    local attempt=0
+    local root_pass
+    while true; do
+        root_pass=$(dialog --backtitle "$SCRIPT_NAME" \
+            --title "$title" \
+            --insecure \
+            --passwordbox "Enter root password for KVM host ($KVM_HOST_IP):" 8 
60 \
+            3>&1 1>&2 2>&3)
+        if [ $? -ne 0 ]; then
+            dialog --backtitle "$SCRIPT_NAME" \
+                --title "Cancelled" \
+                --msgbox "Zone deployment cancelled." 6 50
+            return 1
+        fi
+
+        if [[ -z "$root_pass" ]]; then
+            dialog --backtitle "$SCRIPT_NAME" \
+                --title "Error" \
+                --msgbox "Password cannot be empty. Please enter a valid 
password." 6 60
+            continue
+        fi
+        
+        if sshpass -p "$root_pass" ssh  -o StrictHostKeyChecking=no \
+                                        -o ConnectTimeout=5 \
+                                        -o LogLevel=ERROR \
+                                        -o UserKnownHostsFile=/dev/null \
+                                        root@"$KVM_HOST_IP" "echo 
2>/dev/null"; then
+            break
+        else
+            attempt=$((attempt + 1))
+            if [[ $attempt -ge $MAX_ATTEMPTS ]]; then
+                dialog --backtitle "$SCRIPT_NAME" \
+                    --title "Error" \
+                    --msgbox "Failed to connect to $KVM_HOST_IP after 
$MAX_ATTEMPTS attempts. Aborting zone deployment." 6 60
+                return 1
+            fi
+            dialog --backtitle "$SCRIPT_NAME" \
+                --title "Error" \
+                --msgbox "Failed to connect to KVM host. Please check the IP 
and password.\nAttempts left: $((MAX_ATTEMPTS - attempt))" 8 60    
+        fi
+    done
+
+    local network="${HOST_IP%.*}.0/24"
+    # Find IPs for different purposes
+    local public_ips=($(find_free_ip_range "$network" 11 20))  # 20 IPs for 
public
+    local pod_ips=($(find_free_ip_range "$network" 41 20))     # 20 IPs for pod
+
+    # Default values
+    local defaults=(
+        "Zone Name" "Zone1"
+        "Guest CIDR" "172.16.1.0/24"
+        "Public Start IP" "${public_ips[0]}"
+        "Public End IP" "${public_ips[-1]}"
+        "Pod Start IP" "${pod_ips[0]}"
+        "Pod End IP" "${pod_ips[-1]}"
+        "VLAN Range" "100-200"
+        "Physical Network" "Physical Network 1"
+        "Pod Name" "Pod1"
+        "Cluster Name" "Cluster1"
+        "Primary Storage Name" "Primary1"
+        "Secondary Storage Name" "Secondary1"
+        "NFS Server IP" "$HOST_IP"
+        "Primary Storage Path" "/export/primary"
+        "Secondary Storage Path" "/export/secondary"
+    )
+    
+    local results=()
+    # Create zone form entries
+    local form_height=$((${#defaults[@]} / 2 + 8))
+    local form_entries=()
+    local i=0
+    while [[ $i -lt ${#defaults[@]} ]]; do
+        form_entries+=("${defaults[i]}")     # Label
+        form_entries+=("$((i/2+1))")         # Row
+        form_entries+=("1")                  # Label column
+        form_entries+=("${defaults[i+1]}")   # Default value
+        form_entries+=("$((i/2+1))")         # Row for input
+        form_entries+=("35")                 # Input column
+        form_entries+=("30")                 # Field width
+        form_entries+=("0")                  # Max input length
+        ((i+=2))
+    done
+
+    form_args=(
+        --backtitle "$SCRIPT_NAME"
+        --title "Zone Configuration"
+        --form "Configure Zone Deployment Parameters:"
+        $form_height 70 0
+    )
+
+    for ((i=0; i<${#form_entries[@]}; i+=8)); do
+        form_args+=(
+            "${form_entries[i]}"      # Label
+            "${form_entries[i+1]}"    # Row
+            "${form_entries[i+2]}"    # Label column
+            "${form_entries[i+3]}"    # Default value
+            "${form_entries[i+4]}"    # Input row
+            "${form_entries[i+5]}"    # Input column
+            "${form_entries[i+6]}"    # Field width
+            "${form_entries[i+7]}"    # Max length
+        )
+    done
+    if ! results=$(dialog "${form_args[@]}" 2>&1 >/dev/tty); then
+        dialog --backtitle "$SCRIPT_NAME" \
+            --title "Cancelled" \
+            --msgbox "Zone configuration was cancelled." 6 50
+        return 1
+    fi
+
+    # Convert results string into array
+    mapfile -t results <<< "$results"
+    
+    # Map results to variables
+    local zone_name="${results[0]}"
+    local guest_cidr="${results[1]}"
+    local public_start="${results[2]}"
+    local public_end="${results[3]}"
+    local pod_start="${results[4]}"
+    local pod_end="${results[5]}"
+    local vlan_range="${results[6]}"
+    local phy_name="${results[7]}"
+    local pod_name="${results[8]}"
+    local cluster_name="${results[9]}"
+    local primary_name="${results[10]}"
+    local secondary_name="${results[11]}"
+    local nfs_server="${results[12]}"
+    local primary_path="${results[13]}"
+    local secondary_path="${results[14]}"
+    local network_type="Advanced"
+
+    # Show confirmation
+    local zone_details=""
+    zone_details+="Zone: $zone_name (${network_type})\n"
+    zone_details+="Guest CIDR: $guest_cidr\n"
+    zone_details+="Public IPs: $public_start - $public_end\n"
+    zone_details+="Pod IPs: $pod_start - $pod_end\n"
+    zone_details+="VLAN Range: $vlan_range\n"
+    zone_details+="Physical Network Name: $phy_name\n"
+    zone_details+="Pod Name: $pod_name\n"
+    zone_details+="Cluster Name: $cluster_name\n"
+    zone_details+="Primary Storage Name: $primary_name\n"
+    zone_details+="Primary Storage Path: nfs://$nfs_server$primary_path\n"
+    zone_details+="Secondary Storage Path: nfs://$nfs_server$secondary_path\n"
+    
+    log "$(printf "Zone details:\n%b" "$zone_details")"
+    if is_interactive; then
+        local confirm_msg="Please confirm the following 
configuration:\n\n$zone_details"
+        if ! dialog --backtitle "$SCRIPT_NAME" \
+                    --title "Confirm Configuration" \
+                    --yesno "$confirm_msg" 18 60; then
+            return 1
+        fi
+    fi
+
+    if ! wait_for_management_server; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Error" \
+               --msgbox "Management Server did not become ready in 
time.\nPlease check the server status and logs." 8 60
+        return 1
+    fi
+
+    if ! check_cloudmonkey_availability; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "Error" \
+               --msgbox "CloudMonkey is not available or failed to 
initialize.\nPlease install CloudMonkey and try again." 8 60
+        return 1
+    fi
+
+    local zone_id=""
+    local pod_id=""
+    local cluster_id="" 
+    {
+        zone_id=$(get_tracker_field "zone_id")
+        if [[ -n "$zone_id" ]]; then
+            log "Zone already deployed with ID: $zone_id"
+        else
+            update_progress_bar "10" "# Starting Zone deployment..."
+            zone_id=$(cmk_api_call '.zone.id' create zone name="${zone_name}" \
+                networktype="$network_type" \
+                dns1="$DNS" \
+                internaldns1="$DNS" \
+                localstorageenabled=true \
+                securitygroupenabled=false \
+                guestcidraddress="$guest_cidr") || \
+                cmk_api_fail_dialog_msg "Failed to create zone"
+        fi
+        set_tracker_field "zone_id" "$zone_id"
+        
+        local physicalnetwork_id=$(get_tracker_field "physicalnetwork_id")
+        if [[ -n "$physicalnetwork_id" ]]; then
+            log "Physical Network already created with ID: $physicalnetwork_id"
+        else
+            update_progress_bar "20" "# Creating Physical Network..."
+            physicalnetwork_id=$(cmk_api_call '.physicalnetwork.id' create 
physicalnetwork name="$phy_name" \
+                zoneid="$zone_id" \
+                isolationmethods="VLAN") || \
+                cmk_api_fail_dialog_msg "Failed to create physical network"
+
+            set_tracker_field "physicalnetwork_id" "$physicalnetwork_id"
+            # Add Traffic Types
+            update_progress_bar "30" "# Adding Traffic Types..."
+            cmk add traffictype traffictype=Management 
physicalnetworkid="$physicalnetwork_id" >/dev/null 2>&1
+            cmk add traffictype traffictype=Guest 
physicalnetworkid="$physicalnetwork_id" >/dev/null 2>&1
+            cmk add traffictype traffictype=Public 
physicalnetworkid="$physicalnetwork_id" >/dev/null 2>&1
+        fi
+
+        local vlan_ip_range_id=$(get_tracker_field "vlan_ip_range_id")
+        if [[ -n "$vlan_ip_range_id" ]]; then
+            log "VLAN IP Range already added with ID: $vlan_ip_range_id"
+        else
+            update_progress_bar "35" "# Adding IP Ranges..."
+            vlan_ip_range_id=$(cmk_api_call '.vlan.id' create vlaniprange \
+                zoneid="$zone_id" \
+                vlan=untagged \
+                gateway="$GATEWAY" \
+                netmask="$NETMASK" \
+                startip="$public_start" \
+                endip="$public_end" \
+                forvirtualnetwork=true) || \
+                fail_with_dialog_msg "Failed to add Public IP range"
+            set_tracker_field "vlan_ip_range_id" "$vlan_ip_range_id"
+        fi
+
+        update_progress_bar "38" "# Configuring Physical network with VLAN 
range..."
+        cmk update physicalnetwork id=$physicalnetwork_id vlan=$vlan_range 
>/dev/null 2>&1
+
+        update_progress_bar "40" "# Configuring Physical network state..."
+        cmk update physicalnetwork state=Enabled id="$physicalnetwork_id" 
>/dev/null 2>&1
+        local nsp_id=$(cmk list networkserviceproviders name=VirtualRouter 
physicalnetworkid="$physicalnetwork_id" | jq -r '.networkserviceprovider[0].id')
+        local vre_id=$(cmk list virtualrouterelements nspid="$nsp_id" | jq -r 
'.virtualrouterelement[0].id')
+        
+        update_progress_bar "45" "# Configuring Physical network services..."
+        cmk configure virtualrouterelement enabled=true id="$vre_id" 
>/dev/null 2>&1
+        cmk update networkserviceprovider state=Enabled id="$nsp_id" 
>/dev/null 2>&1
+        
+        pod_id=$(get_tracker_field "pod_id")
+        if [[ -n "$pod_id" ]]; then
+            log "Pod already created with ID: $pod_id"
+        else
+            update_progress_bar "50" "# Creating Pod..."
+            pod_id=$(cmk_api_call '.pod.id' create pod name="$pod_name" \
+                zoneid="$zone_id" \
+                gateway="$GATEWAY" \
+                netmask="$NETMASK" \
+                startip="$pod_start" \
+                endip="$pod_end") || \
+                fail_with_dialog_msg "Failed to create Pod"
+            set_tracker_field "pod_id" "$pod_id"
+        fi
+        
+        cluster_id=$(get_tracker_field "cluster_id")
+        if [[ -n "$cluster_id" ]]; then
+            log "Cluster already created with ID: $cluster_id"
+        else
+            update_progress_bar "60" "# Adding Cluster..."
+            cluster_id=$(cmk_api_call '.cluster[0].id' add cluster \
+                zoneid="$zone_id" \
+                podid="$pod_id" \
+                clustername="$cluster_name" \
+                clustertype=CloudManaged \
+                hypervisor=KVM)|| \
+                fail_with_dialog_msg "Failed to add Cluster"
+            set_tracker_field "cluster_id" "$cluster_id"
+        fi
+        
+        local host_id=$(get_tracker_field "host_id")
+        if [[ -n "$host_id" ]]; then
+            log "Host already created with ID: $host_id"
+        else
+            update_progress_bar "70" "# Adding Host..."
+            host_id=$(cmk_api_call '.host[0].id' add host zoneid="$zone_id" \
+                podid="$pod_id" \
+                clusterid="$cluster_id" \
+                hypervisor=KVM \
+                username=root \
+                password="$root_pass" \
+                url="http://$KVM_HOST_IP";)|| \
+                cmk_api_fail_dialog_msg "Failed to add Host"
+            set_tracker_field "host_id" "$host_id"
+        fi
+        
+        local primary_storage_id=$(get_tracker_field "primary_storage_id")
+        if [[ -n "$primary_storage_id" ]]; then
+            log "Primary Storage already added with ID: $primary_storage_id"
+        else
+            update_progress_bar "80" "# Adding Primary Storage..."
+            primary_storage_id=$(cmk_api_call '.storagepool.id' create 
storagepool name="$primary_name" \
+                zoneid="$zone_id" \
+                podid="$pod_id" \
+                clusterid="$cluster_id" \
+                url="nfs://$nfs_server$primary_path" \
+                hypervisor=KVM \
+                scope=zone) || \
+                cmk_api_fail_dialog_msg "Failed to add Primary Storage"
+            set_tracker_field "primary_storage_id" "$primary_storage_id"
+        fi
+        
+        local secondary_storage_id=$(get_tracker_field "secondary_storage_id")
+        if [[ -n "$secondary_storage_id" ]]; then
+            log "Secondary Storage already added with ID: 
$secondary_storage_id"
+        else
+            update_progress_bar "90" "# Add Secondary Storage..."
+            secondary_storage_id=$(cmk_api_call '.imagestore.id' add 
imagestore name="$secondary_name" \
+                zoneid="$zone_id" \
+                url="nfs://$nfs_server$secondary_path" \
+                provider=NFS) || \
+                cmk_api_fail_dialog_msg "Failed to add Secondary Storage"
+            set_tracker_field "secondary_storage_id" "$secondary_storage_id"
+        fi
+
+        update_progress_bar "95" "# Enabling Zone..."
+        cmk update zone allocationstate=Enabled id="$zone_id" >/dev/null 2>&1
+        
+        update_progress_bar "100" "# Zone deployment completed successfully!"
+    } | dialog --backtitle "$SCRIPT_NAME" \
+               --title "$title" \
+               --gauge "Deploying CloudStack Zone..." 10 70 0
+    set_tracker_field "$tracker_key" "yes"
+    show_dialog "info" "$title" "Zone deployment completed successfully."
+    show_cloudstack_banner
+}
+
+# Function to setup bridge used for CS
+configure_network() {
+    if is_interactive; then
+        local new_bridge=$(dialog --inputbox "Enter the name for the bridge:" 
8 60 "$BRIDGE" 3>&1 1>&2 2>&3)
+        if [[ -n "$new_bridge" && "$new_bridge" =~ ^[a-zA-Z0-9_.-]{1,15}$ ]]; 
then
+            BRIDGE="$new_bridge"
+        fi
+    fi
+
+    # First check if bridge already exists
+    if ip link show "$BRIDGE" &>/dev/null; then
+        local bridge_ip=$(ip -4 addr show "$BRIDGE" | awk '/inet / {print $2}' 
| cut -d/ -f1)
+        show_dialog \
+            "info" \
+            "Network Configuration" "Bridge interface $BRIDGE already exists 
with IP $bridge_ip\nSkipping network configuration."
+        return 0
+    fi
+
+    # Gather interface, IP, gateway
+    interface=$(ip -o link show | awk -F': ' '/state UP/ && $2!~/^lo/ {print 
$2; exit}')
+    [[ -n "$interface" ]] || error_exit "No active non-loopback interface 
found."
+
+    hostipandsub=$(ip -4 addr show dev "$interface" | awk '/inet / {print $2; 
exit}')
+    gateway=$(ip route show default | awk '/default/ {print $3; exit}')
+
+    # Rest of the existing configure_network code follows...
+    if [[ "$OS_TYPE" == "ubuntu" ]]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+               --infobox "Configuring bridge $BRIDGE using Netplan..." 5 50
+        sleep 1
+
+        cfgfile="/etc/netplan/01-bridge-$BRIDGE.yaml"
+        cat > "$cfgfile" <<EOF
+network:
+  version: 2
+  renderer: networkd
+  ethernets:
+    $interface:
+      dhcp4: false
+      dhcp6: false
+      optional: true
+  bridges:
+    $BRIDGE:
+      interfaces: [$interface]
+      addresses: [$hostipandsub]
+      routes:
+        - to: default
+          via: $gateway
+      nameservers:
+        addresses: [$DNS]
+      parameters:
+        stp: false
+        forward-delay: 0
+EOF
+        chmod 600 "$cfgfile"
+        rm -f /etc/netplan/50-cloud-init.yaml
+
+        if netplan generate && netplan apply; then
+            show_dialog \
+                "info" \
+                "Network Configuration" \
+                "Bridge $BRIDGE configured successfully with IP $hostipandsub."
+        else
+            error_exit "Failed to apply Netplan configuration"
+        fi
+    elif [[ "$OS_TYPE" == "debian" ]]; then
+        dialog --backtitle "$SCRIPT_NAME" \
+                   --infobox "Configuring bridge $BRIDGE using 
/etc/network/interfaces..." 5 60
+            sleep 1
+
+            cfgfile="/etc/network/interfaces.d/01-bridge-$BRIDGE"
+            cat > "$cfgfile" <<EOF
+auto $BRIDGE
+iface $BRIDGE inet static
+    address $hostipandsub
+    netmask $NETMASK
+    gateway $gateway
+    dns-nameservers $DNS
+    bridge_ports $interface
+    bridge_stp off
+    bridge_fd 0
+EOF
+            chmod 600 "$cfgfile"
+
+            if command -v ifreload &>/dev/null; then
+                ifreload -a
+            else
+                ifdown $interface || true
+                ifup $BRIDGE
+            fi
+
+    elif [[ "$OS_TYPE" =~ ^(rhel|centos|ol|rocky|almalinux)$ ]]; then
+        {
+            update_progress_bar "5" "# Configuring bridge interface $BRIDGE..."
+
+            # First, check if bridge already exists and remove it
+            if nmcli connection show "$BRIDGE" &>/dev/null; then
+                update_progress_bar "10" "# Removing existing bridge 
connection..."
+                nmcli connection delete "$BRIDGE" &>/dev/null || true
+            fi
+            
+            # Remove any existing slave connections for this interface
+            local slave_name="${interface}-slave-$BRIDGE"
+            if nmcli connection show "$slave_name" &>/dev/null; then
+                update_progress_bar "15" "# Removing existing slave 
connection..."
+                nmcli connection delete "$slave_name" &>/dev/null || true
+            fi
+            sleep 2
+
+            # Get the original connection name for the interface
+            update_progress_bar "20" "# Detecting current network 
configuration..."
+            local original_conn=$(nmcli -t -f NAME,DEVICE connection show 
--active | grep ":$interface$" | cut -d: -f1)
+            
+            # Create bridge
+            if output=$(nmcli connection add type bridge \
+                con-name "$BRIDGE" \
+                ifname "$BRIDGE" \
+                bridge.stp no \
+                ipv4.addresses "$hostipandsub" \
+                ipv4.gateway "$gateway" \
+                ipv4.dns "$DNS" \
+                ipv4.method manual \
+                ipv6.method disabled \
+                connection.autoconnect yes \
+                connection.autoconnect-slaves 1 2>&1); then
+                update_progress_bar "30" "# Bridge created successfully"
+            else
+                update_progress_bar "100" "# Failed to create bridge: $output"
+                error_exit "Failed to create bridge: $output"
+            fi
+            sleep 2
+            
+            # Add ethernet interface as slave
+            update_progress_bar "50" "# Adding interface $interface to 
bridge..."
+            if output=$(nmcli connection add type ethernet \
+                slave-type bridge \
+                con-name "$slave_name" \
+                ifname "$interface" \
+                master "$BRIDGE" \
+                connection.autoconnect yes 2>&1); then
+                update_progress_bar "60" "# Interface added as bridge slave"
+            else
+                update_progress_bar "100" "# Failed to add interface: $output"
+                error_exit "Failed to add interface: $output"
+            fi
+            sleep 2
+
+            # Bring down original connection if it exists
+            if [[ -n "$original_conn" ]]; then
+                update_progress_bar "70" "# Deactivating original 
connection..."
+                nmcli connection down "$original_conn" &>/dev/null || true
+                sleep 2
+            fi
+
+            # Activate connection
+            update_progress_bar "90" "# Activating network connection..."
+            if output=$(nmcli connection up "$BRIDGE" 2>&1); then
+                update_progress_bar "92" "# Bridge activated successfully"
+            else
+                update_progress_bar "100" "# Failed to activate bridge: 
$output"
+                sleep 1
+                error_exit "Failed to activate bridge: $output"
+            fi
+            sleep 5
+
+            update_progress_bar "95" "# Verifying bridge configuration..."
+            if ! ip link show "$BRIDGE" &>/dev/null; then
+                error_exit "Bridge $BRIDGE was not created successfully"
+            fi
+
+            # Wait for IP assignment
+            local max_wait=10
+            local count=0
+            local expected_ip=$(echo "$hostipandsub" | cut -d'/' -f1)
+            while [[ $count -lt $max_wait ]]; do
+                if ip addr show "$BRIDGE" | grep -q "inet.*$expected_ip"; then
+                    break
+                fi
+                sleep 1
+                ((count++))
+            done
+            
+            update_progress_bar "100" "# Bridge configuration complete"
+            sleep 2
+
+        } | dialog --backtitle "$SCRIPT_NAME" \
+                --title "Network Configuration" \
+                --gauge "Configuring network with NetworkManager..." 10 70 0
+
+        # Verify the configuration
+        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
+                show_dialog \
+                    "info" \
+                    "Network Configuration" \
+                    "Bridge $BRIDGE configured successfully with IP 
$bridge_ip." 5
+            else
+                dialog --backtitle "$SCRIPT_NAME" \
+                    --title "Warning" \
+                    --msgbox "Bridge $BRIDGE created but IP address not 
detected.\n\nPlease verify with: ip addr show $BRIDGE" 8 70
+                return 1
+            fi
+        else
+            dialog --backtitle "$SCRIPT_NAME" \
+                --title "Error" \
+                --msgbox "Failed to configure bridge $BRIDGE.\n\nCheck logs 
with:\njournalctl -xeu NetworkManager" 8 70
+            return 1
+        fi
+
+    else
+        error_exit "Unsupported OS type: $OS_TYPE"
+    fi
+}
+
+#-------------------------------------------------------------------------------
+# Driver functions to setup and install components
+#-------------------------------------------------------------------------------
+
+setup_network() {
+    log "Starting network configuration"
+    if is_step_tracked "network_name"; then
+        BRIDGE=$(get_tracker_field "network_name")
+        show_dialog "info" "Network Configuration" "Network already configured 
with bridge $BRIDGE.\n\n Skipping network configuration."
+        return 0
+    fi
+    configure_network
+    set_tracker_field "network_name" "$BRIDGE"
+    log "Network configured with bridge $BRIDGE"
+}
+
+configure_repo() {
+    log "Setting up CloudStack repository"
+    if is_step_tracked "repo_url"; then
+        local repo_url=$(get_tracker_field "repo_url")
+        show_dialog "info" "CloudStack Repo Setup" "CloudStack repository 
already configured with $repo_url\n\n Skipping repository setup."
+        return 0
+    fi
+    configure_cloudstack_repo
+}
+
+install_configure_mgmt() {
+    install_mysql_server
+    install_management_server
+    configure_mysql_for_cloudstack
+    configure_management_server_database
+}
+
+install_configure_agent() {
+    install_kvm_agent
+    configure_kvm_agent
+}
+
+install_configure_usage() {
+    install_usage_server
+    configure_usage_server
+}
+
+# Install and configure selected components
+install_configure_components() {
+    show_components_versions
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " nfs " ]]; then
+        install_nfs_server
+        configure_nfs_server
+    fi
+
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " management " ]]; then
+        install_configure_mgmt
+    fi
+
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " agent " ]]; then
+        install_configure_agent
+    fi
+    if [[ " ${SELECTED_COMPONENTS[@]} " =~ " usage " ]]; then
+        install_configure_usage
+    fi
+}
+
+# Validate system resources before setup
+validate_system_resources() {
+    check_root
+    check_kvm_support
+    check_system_resources
+    validate_jre_version
+}
+
+# Configure prerequisites before installation
+configure_prerequisites() {
+    detect_os
+    update_system_time
+    install_base_dependencies
+}
+
+# Driver function for custom installation
+custom_install() {
+    if [ ${#SELECTED_COMPONENTS[@]} -eq 0 ]; then
+        select_components_to_setup
+    fi
+    log "Selected components for installation: ${SELECTED_COMPONENTS[*]}"
+    if is_cloudstack_pkg_selected; then
+        setup_network
+        configure_repo
+    fi
+    update_system_packages
+    install_configure_components
+}
+
+# Driver function for all-in-one installation
+all_in_one_install() {
+    PROMPT=0    # configure silent mode for all-in-one
+    if dialog --backtitle "$SCRIPT_NAME" \
+           --title "All-in-One Installation" \
+           --yesno "You have selected all components for installation. This 
will configure a complete CloudStack setup on this single machine.\n\nProceed 
with All-in-One installation?" 12 60; then
+        SELECTED_COMPONENTS=("nfs" "management" "agent" "usage")
+        custom_install
+    else
+        dialog --backtitle "$SCRIPT_NAME" \
+               --title "All-in-One Installation" \
+               --msgbox "Installation cancelled by user." 6 60
+        exit 0
+    fi
+}
+
+# Cleanup function for trap
+cleanup() {
+    local exit_code=${1:-$?}
+    clear
+    if [[ $exit_code -eq 0 ]]; then
+        success_msg "Script completed successfully. Check $CS_LOGFILE for 
details."
+    else
+        warn_msg "Script interrupted. Check $CS_LOGFILE for details."
+    fi
+    exit $exit_code
+}
+
+#-------------------------------------------------------------------------------
+# Main function
+#-------------------------------------------------------------------------------
+
+main() {
+    log "CloudStack Installer Script Started"
+    load_tracker
+    validate_system_resources
+    configure_prerequisites
+    local temp_file=$(mktemp)
+    if ! dialog --backtitle "$SCRIPT_NAME" \
+           --title "Installation Options" \
+           --menu "Select an option:" 12 50 4 \
+           1 "All-in-One Installation" \
+           2 "Custom Installation" \
+           3 "Configure CloudStack Repository" \
+           4 "Deploy CloudStack Zone" 2> "$temp_file"; then
+        show_dialog "msg" "Installation Options" "Installation option 
selection cancelled by user."
+        error_exit "Installation option selection cancelled by user"
+    fi
+    local option=$(< "$temp_file")
+    case $option in
+        1)
+            all_in_one_install
+            ;;
+        2)
+            custom_install
+            ;;
+        3)
+            configure_cloudstack_repo
+            ;;
+        4)
+            deploy_zone
+            ;;
+    esac
+    
+    if [[ ${#SELECTED_COMPONENTS[@]} -gt 0 ]]; then
+        if show_validation_summary; then
+            if [[ " ${SELECTED_COMPONENTS[@]} " =~ " management " ]]; then
+                if is_interactive; then
+                    if ! select_zone_deployment; then
+                        dialog --backtitle "$SCRIPT_NAME" \
+                            --title "Zone Deployment" \
+                            --msgbox "Zone deployment skipped. You can deploy 
a zone later using CloudStack UI." 8 60
+                    else
+                        deploy_zone
+                    fi
+                else
+                    deploy_zone
+                fi
+            fi
+        else
+            show_dialog "msg" "Zone Deployment" "Warning: Some components are 
not properly configured.\n\nZone deployment is not available until all 
components are fixed." 5 10
+        fi
+    fi
+    rm -f "$temp_file"
+    cleanup 0 
+}
+
+# Set trap for cleanup
+trap 'cleanup 1' INT TERM
+
+# Run main function
+main "$@"

Reply via email to