Files
cds-ai/atvm/scripts/atvm-setup-script.sh
anthony.wen 274b920b40 Reorganize ATVM workspace into scripts, docs, inventory, and archive
Restructure the ATVM folder to separate executable scripts from workflow documentation and long-form environment reference material.

Move setup and automation scripts into scripts/, move setup and automation guides into docs/, add top-level README and workflow conventions, and organize durable environment details into inventory/ while preserving the original long-form ATVM notes under archive/imported-notes/.

Update internal documentation paths to match the new layout and remove the archived Zone.Identifier metadata file.
2026-03-21 20:39:23 -04:00

1868 lines
66 KiB
Bash

#!/usr/bin/env bash
# ATVM VM Setup Script
# Combined script that:
# 0. Validates target host identity using expected IP + hostname
# 1. Fixes repository configuration (removes DVD/CD-ROM entries)
# 2. Configures Ubuntu root SSH/password workflow
# 3. Sets Oracle Linux default kernel to non-UEK
# 4. Disables Ubuntu unattended auto-upgrades
# 5. Installs cross-distro packages
# 6. Disables SELinux (Red Hat-based systems only)
# 7. Configures Static IP as the final step (All distributions)
# 8. Reboots SELinux-capable distros and verifies SELinux after boot
# 9. Keeps client powered on until controller-side log copy + hash verification complete
# 10. Requires controller-side power-off only after successful verification
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Global variables to track actions
SUDO_CMD=""
SUDO_INSTALLED=false
MULTIPATH_REMOVED=()
ISCSI_REMOVED=()
KERNEL_HEADERS_INSTALLED=false
PACKAGES_INSTALLED=()
DISTRO=""
PM=""
REPO_FIX_COMPLETED=false
PACKAGE_INSTALL_COMPLETED=false
AUTO_UPGRADES_DISABLED=false
SELINUX_DISABLED=false
SELINUX_WAS_ENFORCING=false
NETWORKMANAGER_COMPLETED=false
ROOT_SSH_CONFIGURED=false
HOST_IDENTITY_VALIDATED=false
INTERFACE=""
STATIC_IP_METHOD="not-set"
ORACLE_KERNEL_STEP_COMPLETED=false
ORACLE_KERNEL_DEFAULT_CHANGED=false
ORACLE_KERNEL_REBOOT_REQUIRED=false
ORACLE_KERNEL_TARGET=""
ORACLE_KERNEL_DEFAULT_BEFORE=""
ORACLE_KERNEL_DEFAULT_AFTER=""
ORACLE_TARGET_KERNEL_VERSION=""
ATVM_SCRIPT_DIR=""
LOG_FILE="atvm_setup_script.log"
WARNINGS_ENCOUNTERED=()
ERRORS_ENCOUNTERED=()
WORKAROUNDS_USED=()
SELINUX_POST_REBOOT_VERIFIED=false
SELINUX_POST_REBOOT_STATUS="not-run"
AUTO_REBOOT_FOR_SELINUX=false
EXPECTED_IP=""
EXPECTED_HOSTNAME=""
ACTUAL_HOSTNAME=""
#==============================================================================
# UTILITY FUNCTIONS
#==============================================================================
# Function to log to file (without color codes)
log_to_file() {
echo "$1" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE"
}
# Function to print colored output
print_info() {
echo -e "${GREEN}[INFO]${NC} $1"
log_to_file "[INFO] $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
log_to_file "[ERROR] $1"
ERRORS_ENCOUNTERED+=("$1")
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
log_to_file "[WARNING] $1"
WARNINGS_ENCOUNTERED+=("$1")
}
record_workaround() {
WORKAROUNDS_USED+=("$1")
print_info "Workaround used: $1"
}
is_selinux_distro() {
[[ "$DISTRO" == "rhel" || "$DISTRO" == "centos" || "$DISTRO" == "rocky" || \
"$DISTRO" == "almalinux" || "$DISTRO" == "fedora" || "$DISTRO" == "ol" ]]
}
setup_completed_without_errors() {
[[ "$HOST_IDENTITY_VALIDATED" == true ]] && \
[[ "$REPO_FIX_COMPLETED" == true ]] && \
[[ "$PACKAGE_INSTALL_COMPLETED" == true ]] && \
[[ ${#ERRORS_ENCOUNTERED[@]} -eq 0 ]]
}
setup_selinux_post_reboot_verifier() {
local verify_script="/usr/local/sbin/atvm_selinux_post_reboot_verify.sh"
local verify_service="/etc/systemd/system/atvm-selinux-postreboot.service"
if ! command -v systemctl &>/dev/null; then
print_warning "systemctl not available - cannot configure post-reboot SELinux verification"
return 1
fi
${SUDO_CMD} bash -c "cat > '$verify_script' <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
LOG_FILE_PATH='${LOG_FILE}'
RESULT='unavailable'
HAS_SUCCESS_MARKER='false'
HAS_ERRORS='false'
if command -v getenforce >/dev/null 2>&1; then
RESULT=\$(getenforce 2>/dev/null || echo unavailable)
fi
if grep -Fq 'SUCCESS: ATVM VM Setup Complete!' "\$LOG_FILE_PATH" 2>/dev/null; then
HAS_SUCCESS_MARKER='true'
fi
if grep -Eq '^\\[ERROR\\]' "\$LOG_FILE_PATH" 2>/dev/null; then
HAS_ERRORS='true'
fi
{
echo '[INFO] Post-reboot SELinux verification: '\$RESULT
echo '[INFO] Verification timestamp: '\"\$(date '+%Y-%m-%d %H:%M:%S %Z')\"
if [[ "\$HAS_SUCCESS_MARKER" == 'true' && "\$HAS_ERRORS" == 'false' ]]; then
echo '[INFO] Setup completed without logged errors - keeping client powered on for controller log collection.'
echo '[INFO] Required next step: after controller log copy + SHA256 verification succeed, power off the client from controller.'
else
echo '[WARNING] Setup reported errors or missing success marker - keeping client powered on for manual inspection.'
fi
} >> \"\$LOG_FILE_PATH\"
systemctl disable atvm-selinux-postreboot.service >/dev/null 2>&1 || true
rm -f /etc/systemd/system/atvm-selinux-postreboot.service
rm -f /usr/local/sbin/atvm_selinux_post_reboot_verify.sh
systemctl daemon-reload >/dev/null 2>&1 || true
EOF"
${SUDO_CMD} chmod 700 "$verify_script"
${SUDO_CMD} bash -c "cat > '$verify_service' <<'EOF'
[Unit]
Description=ATVM SELinux Post-Reboot Verification
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/atvm_selinux_post_reboot_verify.sh
[Install]
WantedBy=multi-user.target
EOF"
${SUDO_CMD} systemctl daemon-reload
${SUDO_CMD} systemctl enable atvm-selinux-postreboot.service >/dev/null
return 0
}
reboot_and_verify_selinux_if_needed() {
print_section "STEP 9: SELinux Reboot And Verification"
if ! is_selinux_distro; then
print_info "SELinux reboot/verification is not applicable to this distro"
return 0
fi
if [[ "$SELINUX_DISABLED" != true ]]; then
print_info "SELinux was not changed to disabled - no reboot required for SELinux verification"
return 0
fi
if setup_completed_without_errors; then
print_info "Run is currently error-free; post-reboot verifier will keep client on for controller log collection"
else
print_warning "Errors detected in this run; post-reboot verifier will keep client powered on for manual inspection"
fi
if setup_selinux_post_reboot_verifier; then
print_info "Configured one-time post-reboot SELinux verification"
else
print_warning "Proceeding with reboot without automated post-reboot verification service"
fi
AUTO_REBOOT_FOR_SELINUX=true
print_warning "Rebooting now to apply SELinux disabled state"
print_warning "After reboot, post-reboot verification will log SELinux runtime status"
sync
sleep 2
${SUDO_CMD} reboot
}
poweroff_client_if_successful() {
print_section "STEP 10: Final Power State"
if setup_completed_without_errors; then
print_info "Setup completed without errors - keeping client powered on for controller log collection"
print_info "Required next step: after controller copies log and confirms SHA256 match, power off the client"
else
print_warning "Setup has errors or incomplete required steps - keeping client powered on for manual inspection"
fi
}
print_section() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
log_to_file ""
log_to_file "========================================"
log_to_file "$1"
log_to_file "========================================"
log_to_file ""
}
print_usage() {
cat <<'EOF'
Usage:
atvm_setup_script.sh --expected-ip <ip> --expected-hostname <hostname>
Required:
--expected-ip Expected target host IPv4 address (the SSH destination)
--expected-hostname Expected target hostname for safety verification
(must be operator-provided; do not infer)
If hostname was not explicitly given by the operator,
do not run setup.
Connect directly to the operator-provided target IP.
Do not pre-probe alternate "likely" IP addresses.
EOF
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--expected-ip)
EXPECTED_IP="${2:-}"
shift 2
;;
--expected-hostname)
EXPECTED_HOSTNAME="${2:-}"
shift 2
;;
-h|--help)
print_usage
exit 0
;;
*)
print_error "Unknown argument: $1"
print_usage
exit 1
;;
esac
done
if [[ -z "$EXPECTED_IP" || -z "$EXPECTED_HOSTNAME" ]]; then
print_error "Safety check requires both --expected-ip and --expected-hostname"
print_error "Expected hostname must be explicitly operator-provided. Do not infer hostname from the target."
print_usage
exit 1
fi
}
validate_target_host_identity() {
print_section "STEP 0: Target Host Identity Validation"
local expected_norm actual_short actual_fqdn
expected_norm="$(echo "$EXPECTED_HOSTNAME" | tr '[:upper:]' '[:lower:]')"
actual_short="$(hostname -s 2>/dev/null || hostname)"
actual_fqdn="$(hostname -f 2>/dev/null || hostname)"
ACTUAL_HOSTNAME="$actual_short"
print_info "Expected IP: $EXPECTED_IP"
print_info "Expected hostname: $EXPECTED_HOSTNAME"
print_info "Detected hostname (short): $actual_short"
print_info "Detected hostname (fqdn): $actual_fqdn"
if ! ip -o -4 addr show 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | grep -Fxq "$EXPECTED_IP"; then
print_error "Expected IP $EXPECTED_IP is not assigned to this machine. Aborting for safety."
exit 1
fi
if [[ "$expected_norm" != "$(echo "$actual_short" | tr '[:upper:]' '[:lower:]')" ]] && \
[[ "$expected_norm" != "$(echo "$actual_fqdn" | tr '[:upper:]' '[:lower:]')" ]]; then
print_error "Hostname mismatch. Expected '$EXPECTED_HOSTNAME' but detected '$actual_short'. Aborting."
exit 1
fi
HOST_IDENTITY_VALIDATED=true
print_info "Target host identity validation passed"
}
#==============================================================================
# SECTION 1: REPOSITORY FIX
#==============================================================================
fix_repositories() {
print_section "STEP 1: Fixing Repository Configuration"
print_info "Detecting Linux distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
else
print_error "Cannot detect distribution"
exit 1
fi
print_info "Detected: $DISTRO"
case $DISTRO in
ubuntu|debian)
print_info "Processing Debian/Ubuntu repositories..."
REPO_FILE="/etc/apt/sources.list"
if [ -f "$REPO_FILE" ]; then
${SUDO_CMD} cp $REPO_FILE ${REPO_FILE}.backup.$(date +%Y%m%d_%H%M%S)
print_info "Backup created: ${REPO_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} sed -i 's/^deb cdrom:/#deb cdrom:/g' $REPO_FILE
${SUDO_CMD} sed -i 's/^deb-src cdrom:/#deb-src cdrom:/g' $REPO_FILE
fi
if [ -d /etc/apt/sources.list.d/ ]; then
for file in /etc/apt/sources.list.d/*.list; do
if [ -f "$file" ]; then
${SUDO_CMD} sed -i 's/^deb cdrom:/#deb cdrom:/g' "$file"
${SUDO_CMD} sed -i 's/^deb-src cdrom:/#deb-src cdrom:/g' "$file"
fi
done
fi
print_info "CD-ROM/DVD entries commented out"
print_info "Updating package lists..."
${SUDO_CMD} apt-get update
;;
rhel|centos|rocky|almalinux|ol)
print_info "Processing Red Hat/CentOS/Oracle Linux repositories..."
REPO_DIR="/etc/yum.repos.d"
for file in $REPO_DIR/*.repo; do
if [ -f "$file" ]; then
if grep -q "file:///media\|cdrom\|dvd" "$file"; then
print_info "Processing: $file"
${SUDO_CMD} cp "$file" "${file}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} sed -i 's/^baseurl=file:\/\/\/media/#baseurl=file:\/\/\/media/g' "$file"
${SUDO_CMD} sed -i 's/^baseurl=.*cdrom/#&/g' "$file"
${SUDO_CMD} sed -i 's/^baseurl=.*dvd/#&/g' "$file"
${SUDO_CMD} sed -i '/\[.*media.*\]/,/^\[/ s/^enabled=1/enabled=0/' "$file"
fi
fi
done
print_info "CD-ROM/DVD entries commented out"
print_info "Cleaning and updating cache..."
${SUDO_CMD} yum clean all
${SUDO_CMD} yum makecache
;;
fedora)
print_info "Processing Fedora repositories..."
REPO_DIR="/etc/yum.repos.d"
for file in $REPO_DIR/*.repo; do
if [ -f "$file" ]; then
if grep -q "file:///media\|cdrom\|dvd" "$file"; then
print_info "Processing: $file"
${SUDO_CMD} cp "$file" "${file}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} sed -i 's/^baseurl=file:\/\/\/media/#baseurl=file:\/\/\/media/g' "$file"
${SUDO_CMD} sed -i 's/^baseurl=.*cdrom/#&/g' "$file"
${SUDO_CMD} sed -i 's/^baseurl=.*dvd/#&/g' "$file"
${SUDO_CMD} sed -i '/\[.*media.*\]/,/^\[/ s/^enabled=1/enabled=0/' "$file"
fi
fi
done
print_info "CD-ROM/DVD entries commented out"
print_info "Cleaning and updating cache..."
${SUDO_CMD} dnf clean all
${SUDO_CMD} dnf makecache
;;
opensuse*|sles)
print_info "Processing openSUSE/SLES repositories..."
print_info "Disabling CD/DVD repositories..."
for repo in $(${SUDO_CMD} zypper lr | grep -i "cd\|dvd" | awk '{print $1}'); do
print_info "Disabling repository: $repo"
${SUDO_CMD} zypper mr -d $repo
done
print_info "Refreshing repositories..."
${SUDO_CMD} zypper refresh
;;
*)
print_warning "Unsupported distribution for repo fix: $DISTRO"
print_warning "Continuing with installation..."
;;
esac
print_info "Repository configuration updated"
REPO_FIX_COMPLETED=true
}
#==============================================================================
# SECTION 1B: UBUNTU ROOT SSH WORKFLOW
#==============================================================================
configure_ubuntu_root_ssh_access() {
print_section "STEP 2: Ubuntu Root SSH Access Configuration"
if [[ "$DISTRO" != "ubuntu" ]]; then
print_info "Ubuntu root SSH workflow is not applicable to this distro"
return 0
fi
print_warning "Ubuntu-specific workflow: configuring root account for SSH password login"
echo "root:cdsi2012" | ${SUDO_CMD} chpasswd
${SUDO_CMD} passwd -u root >/dev/null 2>&1 || true
print_info "Root password set to configured workflow value"
${SUDO_CMD} install -d -m 755 /etc/ssh/sshd_config.d
${SUDO_CMD} bash -c "cat > /etc/ssh/sshd_config.d/99-atvm-root-login.conf <<'EOF'
PermitRootLogin yes
PasswordAuthentication yes
KbdInteractiveAuthentication yes
UsePAM yes
EOF"
${SUDO_CMD} sshd -t
if ${SUDO_CMD} systemctl restart ssh 2>/dev/null; then
print_info "Restarted ssh service"
elif ${SUDO_CMD} systemctl restart sshd 2>/dev/null; then
print_info "Restarted sshd service"
else
print_warning "Could not restart ssh/sshd service automatically"
fi
local sshd_effective
sshd_effective="$(${SUDO_CMD} sshd -T 2>/dev/null | egrep 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|usepam' || true)"
if [[ -n "$sshd_effective" ]]; then
print_info "Effective sshd settings:"
while IFS= read -r line; do
print_info " $line"
done <<< "$sshd_effective"
fi
ROOT_SSH_CONFIGURED=true
print_info "Root SSH/password workflow configured for Ubuntu"
print_info "Next operator step: reconnect as root with password cdsi2012"
}
#==============================================================================
# SECTION 2: STATIC IP CONFIGURATION
#==============================================================================
# All check functions use ONLY: command -v, [ -d ], [ -f ], rpm -q.
# NO grep-in-pipe — those return exit code 1 on no-match and kill the script
# under `set -euo pipefail`.
# Interface is AUTO-DETECTED from the system rather than hardcoded.
#==============================================================================
check_netplan() {
command -v netplan &>/dev/null && return 0
[ -d /etc/netplan ] && [ -n "$(ls -A /etc/netplan 2>/dev/null)" ] && return 0
return 1
}
check_networkmanager() {
command -v nmcli &>/dev/null && return 0
[ -d /etc/NetworkManager ] && return 0
[ -d /etc/NetworkManager/system-connections ] && return 0
[ -f /usr/lib/systemd/system/NetworkManager.service ] && return 0
[ -f /lib/systemd/system/NetworkManager.service ] && return 0
command -v rpm &>/dev/null && rpm -q NetworkManager &>/dev/null 2>&1 && return 0
return 1
}
check_wicked() {
command -v wicked &>/dev/null && return 0
[ -f /usr/lib/systemd/system/wicked.service ] && return 0
[ -f /lib/systemd/system/wicked.service ] && return 0
return 1
}
check_ifcfg() {
[ -d /etc/sysconfig/network-scripts ] && \
ls /etc/sysconfig/network-scripts/ifcfg-* &>/dev/null 2>&1 && return 0
[ -f /etc/sysconfig/network ] && return 0
return 1
}
# Detect the active non-loopback interface that currently has an IP.
# Priority: interface with default route > interface with an IP > first UP non-lo interface.
detect_active_interface() {
local iface=""
# 1. Interface carrying the default route (most reliable)
iface=$(ip route 2>/dev/null | awk '/^default/ {print $5; exit}')
if [ -n "$iface" ] && ip link show "$iface" &>/dev/null 2>&1; then
echo "$iface"; return 0
fi
# 2. First non-loopback interface with an IPv4 address assigned
iface=$(ip -4 addr show 2>/dev/null \
| awk '/^[0-9]+:/ {gsub(/:$/,"",$2); iface=$2}
/inet / && iface != "lo" {print iface; exit}')
if [ -n "$iface" ] && ip link show "$iface" &>/dev/null 2>&1; then
echo "$iface"; return 0
fi
# 3. First non-loopback interface that is UP (may not have IP yet)
iface=$(ip link show 2>/dev/null \
| awk -F': ' '/^[0-9]+: / && $2 != "lo" {print $2; exit}')
if [ -n "$iface" ] && ip link show "$iface" &>/dev/null 2>&1; then
echo "$iface"; return 0
fi
return 1
}
# Shared helper: write a NetworkManager keyfile and activate it.
write_nm_keyfile() {
local nm_conn_dir="/etc/NetworkManager/system-connections"
local keyfile="${nm_conn_dir}/${INTERFACE}.nmconnection"
local uuid
if command -v uuidgen &>/dev/null; then
uuid=$(uuidgen)
else
uuid=$(cat /proc/sys/kernel/random/uuid)
fi
print_info "Writing NM keyfile: ${keyfile}"
${SUDO_CMD} mkdir -p "${nm_conn_dir}"
[ -f "${keyfile}" ] && \
${SUDO_CMD} cp "${keyfile}" "${keyfile}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "printf '%s\n' \
'[connection]' \
'id=${INTERFACE}' \
'uuid=${uuid}' \
'type=ethernet' \
'interface-name=${INTERFACE}' \
'autoconnect=true' \
'' \
'[ethernet]' \
'' \
'[ipv4]' \
'method=manual' \
'address1=${IP_ADDRESS}/${NETMASK},${GATEWAY}' \
'dns=${DNS1};${DNS2};' \
'' \
'[ipv6]' \
'method=disabled' \
'' \
'[proxy]' \
> '${keyfile}'"
${SUDO_CMD} chmod 600 "${keyfile}"
print_info "Keyfile written (permissions 600)"
${SUDO_CMD} systemctl enable NetworkManager 2>/dev/null || true
${SUDO_CMD} systemctl start NetworkManager 2>/dev/null || true
sleep 2
if command -v nmcli &>/dev/null; then
${SUDO_CMD} nmcli connection reload 2>/dev/null || true
${SUDO_CMD} nmcli connection up "${INTERFACE}" 2>/dev/null || true
print_info "Connection activated via nmcli"
else
local nm_pid
nm_pid=$(systemctl show -p MainPID NetworkManager 2>/dev/null | cut -d= -f2 || true)
[ -n "${nm_pid:-}" ] && [ "${nm_pid}" != "0" ] && \
${SUDO_CMD} kill -HUP "$nm_pid" 2>/dev/null || true
print_info "NM signalled to reload (will activate on next reboot if nmcli unavailable)"
fi
echo ""
print_info "Network verification:"
ip addr show "${INTERFACE}" 2>/dev/null | grep "inet " || true
ip route 2>/dev/null | grep default || true
STATIC_IP_METHOD="NetworkManager keyfile"
}
configure_static_ip() {
print_section "STEP 8: Static IP Configuration"
IP_ADDRESS="192.168.3.191"
NETMASK="22"
GATEWAY="192.168.0.1"
DNS1="8.8.8.8"
DNS2="8.8.4.4"
# Auto-detect the active network interface — no hardcoded name
print_info "Auto-detecting active network interface..."
INTERFACE=$(detect_active_interface 2>/dev/null || true)
if [ -z "${INTERFACE:-}" ]; then
print_warning "Could not detect an active network interface — skipping static IP"
print_info "Available interfaces:"
ip link show 2>/dev/null | awk -F': ' '/^[0-9]+: / {print " - " $2}'
return 0
fi
print_info "Detected active interface: ${INTERFACE}"
print_info "Configuring static IP for $DISTRO..."
print_info "Interface: $INTERFACE | IP: $IP_ADDRESS/$NETMASK | Gateway: $GATEWAY"
echo ""
# Check for network managers
print_info "Checking for network managers..."
FOUND_MANAGER=false
if check_netplan; then print_info "Found: netplan"; FOUND_MANAGER=true; fi
if check_networkmanager; then print_info "Found: NetworkManager"; FOUND_MANAGER=true; fi
if check_wicked; then print_info "Found: wicked"; FOUND_MANAGER=true; fi
if check_ifcfg; then print_info "Found: legacy ifcfg"; FOUND_MANAGER=true; fi
if [[ "$FOUND_MANAGER" == false ]]; then
print_warning "No network manager detected — skipping static IP"
print_warning "Fix: dnf install -y NetworkManager && systemctl enable --now NetworkManager"
return 0
fi
print_info "Network manager detected — proceeding with configuration"
echo ""
case $DISTRO in
ubuntu|debian) configure_debian_static_ip ;;
rhel|centos|rocky|almalinux|fedora|ol) configure_redhat_static_ip ;;
opensuse*|sles) configure_suse_static_ip ;;
arch) configure_arch_static_ip ;;
*)
print_warning "Unsupported distribution for static IP: $DISTRO"
return 0
;;
esac
}
configure_debian_static_ip() {
if check_netplan; then
local netplan_file="/etc/netplan/99-atvm-static.yaml"
[ -f "$netplan_file" ] && \
${SUDO_CMD} cp "$netplan_file" "${netplan_file}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "cat > '$netplan_file' <<EOF
network:
version: 2
ethernets:
${INTERFACE}:
dhcp4: false
addresses:
- ${IP_ADDRESS}/${NETMASK}
routes:
- to: default
via: ${GATEWAY}
nameservers:
addresses:
- ${DNS1}
- ${DNS2}
EOF"
${SUDO_CMD} chmod 600 "$netplan_file"
if ${SUDO_CMD} netplan generate && ${SUDO_CMD} netplan apply; then
print_info "netplan configured and applied"
STATIC_IP_METHOD="netplan"
else
print_warning "netplan apply failed — falling back to /etc/network/interfaces"
record_workaround "netplan apply failed; fallback to /etc/network/interfaces"
[ -f /etc/network/interfaces ] && \
${SUDO_CMD} cp /etc/network/interfaces \
"/etc/network/interfaces.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "printf '%s\n' \
'auto lo' \
'iface lo inet loopback' \
'' \
'auto ${INTERFACE}' \
'iface ${INTERFACE} inet static' \
' address ${IP_ADDRESS}' \
' netmask 255.255.252.0' \
' gateway ${GATEWAY}' \
' dns-nameservers ${DNS1} ${DNS2}' \
> /etc/network/interfaces"
${SUDO_CMD} systemctl restart networking 2>/dev/null || true
print_info "/etc/network/interfaces configured"
STATIC_IP_METHOD="ifupdown interfaces"
fi
elif command -v nmcli &>/dev/null; then
write_nm_keyfile
else
[ -f /etc/network/interfaces ] && \
${SUDO_CMD} cp /etc/network/interfaces \
"/etc/network/interfaces.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "printf '%s\n' \
'auto lo' \
'iface lo inet loopback' \
'' \
'auto ${INTERFACE}' \
'iface ${INTERFACE} inet static' \
' address ${IP_ADDRESS}' \
' netmask 255.255.252.0' \
' gateway ${GATEWAY}' \
' dns-nameservers ${DNS1} ${DNS2}' \
> /etc/network/interfaces"
${SUDO_CMD} systemctl restart networking 2>/dev/null || true
print_info "/etc/network/interfaces configured"
STATIC_IP_METHOD="ifupdown interfaces"
fi
echo ""
print_info "Network verification:"
ip addr show "${INTERFACE}" 2>/dev/null | grep "inet " || true
ip route 2>/dev/null | grep default || true
NETWORKMANAGER_COMPLETED=true
}
configure_redhat_static_ip() {
# Check NM service state safely (no pipe)
local nm_active=false
systemctl is-active --quiet NetworkManager 2>/dev/null && nm_active=true
if command -v nmcli &>/dev/null && [[ "$nm_active" == true ]]; then
print_info "Using nmcli (NetworkManager is running)"
# Delete stale connections one at a time — nmcli delete takes ONE argument
${SUDO_CMD} nmcli connection delete "${INTERFACE}" 2>/dev/null || true
${SUDO_CMD} nmcli connection delete "Wired connection 1" 2>/dev/null || true
${SUDO_CMD} nmcli connection delete "System ${INTERFACE}" 2>/dev/null || true
if ${SUDO_CMD} nmcli connection add \
type ethernet \
con-name "${INTERFACE}" \
ifname "${INTERFACE}" \
ipv4.method manual \
ipv4.addresses "${IP_ADDRESS}/${NETMASK}" \
ipv4.gateway "${GATEWAY}" \
ipv4.dns "${DNS1} ${DNS2}" \
connection.autoconnect yes 2>/dev/null; then
${SUDO_CMD} nmcli connection up "${INTERFACE}" 2>/dev/null || \
print_warning "Could not activate now — will apply on reboot"
print_info "NetworkManager configured via nmcli"
STATIC_IP_METHOD="NetworkManager (nmcli)"
else
print_warning "nmcli connection add failed — falling back to keyfile"
record_workaround "nmcli connection add failed; generated NetworkManager keyfile fallback"
write_nm_keyfile
fi
elif command -v nmcli &>/dev/null && [[ "$nm_active" == false ]]; then
print_info "NetworkManager installed but not running — starting it and writing keyfile"
record_workaround "NetworkManager was installed but inactive; started service and wrote keyfile"
${SUDO_CMD} systemctl enable NetworkManager 2>/dev/null || true
${SUDO_CMD} systemctl start NetworkManager 2>/dev/null || true
sleep 3
write_nm_keyfile
else
# nmcli not in PATH — write keyfile directly (Rocky/OL/RHEL 9 minimal install)
print_info "nmcli not found — writing NM keyfile directly"
record_workaround "nmcli not available; generated NetworkManager keyfile directly"
write_nm_keyfile
fi
echo ""
print_info "Network verification:"
ip addr show "${INTERFACE}" 2>/dev/null | grep "inet " || true
ip route 2>/dev/null | grep default || true
NETWORKMANAGER_COMPLETED=true
}
configure_suse_static_ip() {
if command -v wicked &>/dev/null; then
local ifcfg="/etc/sysconfig/network/ifcfg-${INTERFACE}"
[ -f "$ifcfg" ] && ${SUDO_CMD} cp "$ifcfg" "${ifcfg}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "printf '%s\n' \
\"BOOTPROTO='static'\" \
\"IPADDR='${IP_ADDRESS}/${NETMASK}'\" \
\"STARTMODE='auto'\" \
> '${ifcfg}'"
${SUDO_CMD} bash -c "echo 'default ${GATEWAY} - -' \
> /etc/sysconfig/network/ifroute-${INTERFACE}"
${SUDO_CMD} wicked ifreload "${INTERFACE}" 2>/dev/null || \
${SUDO_CMD} systemctl restart wicked 2>/dev/null || true
print_info "Wicked configured"
STATIC_IP_METHOD="wicked"
NETWORKMANAGER_COMPLETED=true
else
print_warning "wicked not found — cannot configure static IP on this SUSE system"
fi
}
configure_arch_static_ip() {
local nm_active=false
systemctl is-active --quiet NetworkManager 2>/dev/null && nm_active=true
if command -v nmcli &>/dev/null && [[ "$nm_active" == true ]]; then
${SUDO_CMD} nmcli connection delete "${INTERFACE}" 2>/dev/null || true
${SUDO_CMD} nmcli connection add \
type ethernet con-name "${INTERFACE}" ifname "${INTERFACE}" \
ipv4.method manual \
ipv4.addresses "${IP_ADDRESS}/${NETMASK}" \
ipv4.gateway "${GATEWAY}" \
ipv4.dns "${DNS1} ${DNS2}" \
connection.autoconnect yes 2>/dev/null || true
${SUDO_CMD} nmcli connection up "${INTERFACE}" 2>/dev/null || true
print_info "NetworkManager configured"
STATIC_IP_METHOD="NetworkManager (nmcli)"
else
local netfile="/etc/systemd/network/20-${INTERFACE}.network"
[ -f "$netfile" ] && ${SUDO_CMD} cp "$netfile" "${netfile}.backup.$(date +%Y%m%d_%H%M%S)"
${SUDO_CMD} bash -c "printf '%s\n' \
'[Match]' \
'Name=${INTERFACE}' \
'' \
'[Network]' \
'Address=${IP_ADDRESS}/${NETMASK}' \
'Gateway=${GATEWAY}' \
'DNS=${DNS1}' \
'DNS=${DNS2}' \
> '${netfile}'"
${SUDO_CMD} systemctl enable systemd-networkd 2>/dev/null || true
${SUDO_CMD} systemctl restart systemd-networkd 2>/dev/null || true
print_info "systemd-networkd configured"
STATIC_IP_METHOD="systemd-networkd"
fi
NETWORKMANAGER_COMPLETED=true
}
# SECTION 3: PACKAGE INSTALLATION
#==============================================================================
detect_package_manager() {
if command -v apt-get &> /dev/null; then
echo "apt"
elif command -v dnf &> /dev/null; then
echo "dnf"
elif command -v yum &> /dev/null; then
echo "yum"
elif command -v zypper &> /dev/null; then
echo "zypper"
elif command -v pacman &> /dev/null; then
echo "pacman"
elif command -v apk &> /dev/null; then
echo "apk"
else
echo "unknown"
fi
}
check_sudo() {
if command -v sudo &> /dev/null; then
SUDO_CMD="sudo"
print_info "sudo is available"
return 0
fi
if [[ $EUID -eq 0 ]]; then
SUDO_CMD=""
print_info "Running as root - will install sudo after fixing repositories"
return 0
fi
print_error "sudo is not installed and you are not root."
print_error "Please run as root: su -c './atvm_vm_setup.sh'"
exit 1
}
install_sudo_if_needed() {
# If sudo already exists, nothing to do
if command -v sudo &> /dev/null; then
return 0
fi
# Must be root to install sudo
if [[ $EUID -ne 0 ]]; then
print_error "Cannot install sudo without root privileges"
exit 1
fi
print_info "sudo not found - installing now..."
local pm=$(detect_package_manager)
case $pm in
apt)
apt-get update
apt-get install -y sudo
;;
dnf) dnf install -y sudo ;;
yum) yum install -y sudo ;;
zypper) zypper install -y sudo ;;
pacman) pacman -Sy --noconfirm sudo ;;
apk) apk add sudo ;;
*)
print_error "Cannot install sudo: unknown package manager"
exit 1
;;
esac
if command -v sudo &> /dev/null; then
SUDO_INSTALLED=true
SUDO_CMD="sudo"
print_info "sudo has been installed successfully"
# If we were running as root, we now want to use sudo for consistency
if [[ $EUID -eq 0 ]]; then
print_info "Will use sudo for all commands for consistency"
fi
else
print_error "Failed to install sudo"
exit 1
fi
}
install_kernel_headers() {
local pm=$1
local kernel_ver
local devel_pkg=""
local headers_pkg=""
kernel_ver=$(uname -r)
# On Oracle Linux, prefer headers for the selected non-UEK target kernel.
if [[ "$DISTRO" == "ol" ]] && [[ -n "${ORACLE_TARGET_KERNEL_VERSION:-}" ]]; then
kernel_ver="$ORACLE_TARGET_KERNEL_VERSION"
fi
print_info "Installing kernel headers for kernel version: $kernel_ver"
if [[ "$DISTRO" == "ol" && "$kernel_ver" == *"uek"* ]]; then
devel_pkg="kernel-uek-devel-${kernel_ver}"
headers_pkg="kernel-headers-${kernel_ver}"
else
devel_pkg="kernel-devel-${kernel_ver}"
headers_pkg="kernel-headers-${kernel_ver}"
fi
case $pm in
apt)
${SUDO_CMD} apt-get update
${SUDO_CMD} apt-get install -y "linux-headers-${kernel_ver}"
;;
dnf)
if ! ${SUDO_CMD} dnf install -y "$devel_pkg" "$headers_pkg"; then
print_warning "Version-locked kernel headers not found; falling back to latest kernel header packages"
if [[ "$DISTRO" == "ol" && "$kernel_ver" == *"uek"* ]]; then
${SUDO_CMD} dnf install -y kernel-uek-devel kernel-headers
else
${SUDO_CMD} dnf install -y kernel-devel kernel-headers
fi
fi
;;
yum)
if ! ${SUDO_CMD} yum install -y "$devel_pkg" "$headers_pkg"; then
print_warning "Version-locked kernel headers not found; falling back to latest kernel header packages"
if [[ "$DISTRO" == "ol" && "$kernel_ver" == *"uek"* ]]; then
${SUDO_CMD} yum install -y kernel-uek-devel kernel-headers
else
${SUDO_CMD} yum install -y kernel-devel kernel-headers
fi
fi
;;
zypper)
${SUDO_CMD} zypper install -n -y --type pattern devel_basis
${SUDO_CMD} zypper install -n -y -f "kernel-devel=${kernel_ver}" "kernel-source=${kernel_ver}" libselinux-devel
;;
pacman)
local pacman_ver=$(echo "$kernel_ver" | sed 's/-/./1')
${SUDO_CMD} pacman -Sy --noconfirm "linux-headers=${pacman_ver}" || ${SUDO_CMD} pacman -Sy --noconfirm linux-headers
;;
apk)
local flavor=$(echo "$kernel_ver" | grep -oP '\d+-\K[^-]+$' || echo "")
if [[ -n "$flavor" ]]; then
${SUDO_CMD} apk add "linux-${flavor}-dev" || ${SUDO_CMD} apk add linux-headers
else
${SUDO_CMD} apk add linux-headers
fi
;;
*)
print_error "Unknown package manager. Cannot install kernel headers."
exit 1
;;
esac
print_info "Kernel headers installed successfully"
KERNEL_HEADERS_INSTALLED=true
}
remove_multipath_iscsi() {
local pm=$1
print_info "Checking for multipath and iSCSI packages..."
case $pm in
apt)
local packages_to_remove=()
dpkg -l 2>/dev/null | grep -q "multipath-tools" && packages_to_remove+=("multipath-tools") && MULTIPATH_REMOVED+=("multipath-tools")
dpkg -l 2>/dev/null | grep -q "open-iscsi" && packages_to_remove+=("open-iscsi") && ISCSI_REMOVED+=("open-iscsi")
if [ ${#packages_to_remove[@]} -gt 0 ]; then
print_info "Removing packages: ${packages_to_remove[*]}"
${SUDO_CMD} systemctl stop multipathd iscsid open-iscsi 2>/dev/null || true
${SUDO_CMD} apt-get remove -y "${packages_to_remove[@]}" 2>/dev/null || true
${SUDO_CMD} apt-get autoremove -y 2>/dev/null || true
else
print_info "No multipath or iSCSI packages found"
fi
;;
dnf|yum)
local packages_to_remove=()
rpm -qa 2>/dev/null | grep -q "device-mapper-multipath" && packages_to_remove+=("device-mapper-multipath") && MULTIPATH_REMOVED+=("device-mapper-multipath")
rpm -qa 2>/dev/null | grep -q "iscsi-initiator-utils" && packages_to_remove+=("iscsi-initiator-utils") && ISCSI_REMOVED+=("iscsi-initiator-utils")
if [ ${#packages_to_remove[@]} -gt 0 ]; then
print_info "Removing packages: ${packages_to_remove[*]}"
${SUDO_CMD} systemctl stop multipathd iscsid 2>/dev/null || true
[[ "$pm" == "dnf" ]] && ${SUDO_CMD} dnf remove -y "${packages_to_remove[@]}" 2>/dev/null || ${SUDO_CMD} yum remove -y "${packages_to_remove[@]}" 2>/dev/null || true
else
print_info "No multipath or iSCSI packages found"
fi
;;
*)
print_info "No multipath or iSCSI packages found"
;;
esac
}
map_package_name() {
local pm=$1
local pkg=$2
case $pm in
apt)
case $pkg in
scsitools) echo "sg3-utils" ;;
elfutils-libelf-devel) echo "libelf-dev" ;;
*) echo "$pkg" ;;
esac
;;
dnf|yum)
case $pkg in
scsitools) echo "sg3_utils" ;;
*) echo "$pkg" ;;
esac
;;
zypper)
case $pkg in
scsitools) echo "sg3_utils" ;;
net-tools) echo "net-tools-deprecated" ;;
elfutils-libelf-devel) echo "libelf-devel" ;;
*) echo "$pkg" ;;
esac
;;
pacman)
case $pkg in
scsitools) echo "sg3_utils" ;;
elfutils-libelf-devel) echo "libelf" ;;
python3) echo "python" ;;
*) echo "$pkg" ;;
esac
;;
apk)
case $pkg in
scsitools) echo "sg3_utils" ;;
elfutils-libelf-devel) echo "elfutils-dev" ;;
*) echo "$pkg" ;;
esac
;;
*) echo "$pkg" ;;
esac
}
install_packages() {
local pm=$1
shift
local packages=("$@")
local mapped_packages=()
for pkg in "${packages[@]}"; do
mapped_packages+=("$(map_package_name "$pm" "$pkg")")
done
PACKAGES_INSTALLED=("${mapped_packages[@]}")
print_info "Using package manager: $pm"
print_info "Installing packages: ${mapped_packages[*]}"
case $pm in
apt)
${SUDO_CMD} apt-get update
${SUDO_CMD} apt-get install -y "${mapped_packages[@]}"
;;
dnf) ${SUDO_CMD} dnf install -y "${mapped_packages[@]}" ;;
yum) ${SUDO_CMD} yum install -y "${mapped_packages[@]}" ;;
zypper)
${SUDO_CMD} zypper refresh
${SUDO_CMD} zypper install -y "${mapped_packages[@]}"
;;
pacman) ${SUDO_CMD} pacman -Sy --noconfirm "${mapped_packages[@]}" ;;
apk)
${SUDO_CMD} apk update
${SUDO_CMD} apk add "${mapped_packages[@]}"
;;
*)
print_error "Unknown package manager. Cannot install packages."
exit 1
;;
esac
}
run_package_installation() {
print_section "STEP 6: Package Installation"
PM=$(detect_package_manager)
if [[ "$PM" == "unknown" ]]; then
print_error "Could not detect package manager."
exit 1
fi
remove_multipath_iscsi "$PM"
install_kernel_headers "$PM"
PACKAGES=("curl" "wget" "git" "vim" "perl" "gdb" "scsitools" "net-tools" "parted" "fio" "ca-certificates" "python3" "elfutils-libelf-devel")
install_packages "$PM" "${PACKAGES[@]}"
print_info "Package installation completed"
PACKAGE_INSTALL_COMPLETED=true
}
#==============================================================================
# SECTION 4: UBUNTU AUTO-UPGRADE DISABLE
#==============================================================================
disable_ubuntu_auto_upgrades() {
print_section "STEP 5: Ubuntu Auto Upgrade Configuration"
if [[ "$DISTRO" != "ubuntu" ]]; then
print_info "Auto-upgrade configuration is only applied on Ubuntu"
return 0
fi
local auto_upgrade_file="/etc/apt/apt.conf.d/20auto-upgrades"
print_info "Disabling Ubuntu periodic package list updates and unattended upgrades..."
local backup_dir="/var/backups/atvm"
local backup_file="${backup_dir}/20auto-upgrades.backup.$(date +%Y%m%d_%H%M%S)"
if [ -f "$auto_upgrade_file" ]; then
${SUDO_CMD} mkdir -p "$backup_dir"
${SUDO_CMD} cp "$auto_upgrade_file" "$backup_file"
print_info "Backup created: ${backup_file}"
fi
${SUDO_CMD} bash -c "cat > '$auto_upgrade_file' <<'EOF'
APT::Periodic::Update-Package-Lists \"0\";
APT::Periodic::Unattended-Upgrade \"0\";
EOF"
print_info "Updated auto-upgrade settings:"
echo -e "${CYAN}----------------------------------------${NC}"
${SUDO_CMD} cat "$auto_upgrade_file"
echo -e "${CYAN}----------------------------------------${NC}"
AUTO_UPGRADES_DISABLED=true
}
#==============================================================================
# SECTION 5: ORACLE KERNEL DEFAULT (NON-UEK)
#==============================================================================
configure_oracle_non_uek_kernel() {
print_section "STEP 3: Oracle Kernel Default Configuration"
if [[ "$DISTRO" != "ol" ]]; then
print_info "Oracle kernel configuration only applies to Oracle Linux"
return 0
fi
if ! command -v grubby &> /dev/null; then
print_warning "grubby not found - cannot change default kernel"
return 0
fi
ORACLE_KERNEL_DEFAULT_BEFORE=$(grubby --default-kernel 2>/dev/null || true)
ORACLE_KERNEL_TARGET=$(grubby --info=ALL 2>/dev/null \
| awk -F'"' '/^kernel=/{print $2}' \
| grep -v 'uek' \
| head -n 1 || true)
if [[ -z "${ORACLE_KERNEL_TARGET:-}" ]]; then
print_warning "No non-UEK kernel found in boot entries - leaving current default"
return 0
fi
ORACLE_TARGET_KERNEL_VERSION=$(basename "$ORACLE_KERNEL_TARGET" | sed 's/^vmlinuz-//')
print_info "Current default kernel: ${ORACLE_KERNEL_DEFAULT_BEFORE:-unknown}"
print_info "Target non-UEK kernel: $ORACLE_KERNEL_TARGET"
if [[ "$ORACLE_KERNEL_DEFAULT_BEFORE" != "$ORACLE_KERNEL_TARGET" ]]; then
print_info "Setting non-UEK kernel as default boot entry..."
${SUDO_CMD} grubby --set-default "$ORACLE_KERNEL_TARGET"
ORACLE_KERNEL_DEFAULT_CHANGED=true
else
print_info "Default kernel is already set to non-UEK"
fi
ORACLE_KERNEL_DEFAULT_AFTER=$(grubby --default-kernel 2>/dev/null || true)
print_info "New default kernel: ${ORACLE_KERNEL_DEFAULT_AFTER:-unknown}"
if [[ "$ORACLE_KERNEL_DEFAULT_AFTER" == "$ORACLE_KERNEL_TARGET" ]] && \
[[ "$ORACLE_KERNEL_DEFAULT_AFTER" != "/boot/vmlinuz-$(uname -r)" ]]; then
ORACLE_KERNEL_REBOOT_REQUIRED=true
print_warning "Kernel default changed but running kernel is still $(uname -r)"
print_warning "Reboot required to boot into non-UEK kernel"
fi
ORACLE_KERNEL_STEP_COMPLETED=true
}
#==============================================================================
# SECTION 6: SELINUX DISABLE
#==============================================================================
disable_selinux() {
print_section "STEP 7: SELinux Configuration"
if [[ "$DISTRO" != "rhel" && "$DISTRO" != "centos" && "$DISTRO" != "rocky" && "$DISTRO" != "almalinux" && "$DISTRO" != "fedora" && "$DISTRO" != "ol" ]]; then
print_info "SELinux configuration is only applicable to Red Hat-based systems"
return 0
fi
if ! command -v getenforce &> /dev/null; then
print_warning "SELinux tools not found"
return 0
fi
print_info "Checking current SELinux status..."
CURRENT_SELINUX=$(getenforce)
print_info "Current SELinux status: $CURRENT_SELINUX"
echo ""
if [[ "$CURRENT_SELINUX" == "Disabled" ]]; then
print_info "SELinux is already disabled"
return 0
fi
[[ "$CURRENT_SELINUX" == "Enforcing" ]] && SELINUX_WAS_ENFORCING=true
if [ ! -f /etc/selinux/config ]; then
print_warning "SELinux config file not found"
return 0
fi
print_info "Disabling SELinux..."
${SUDO_CMD} cp /etc/selinux/config /etc/selinux/config.backup.$(date +%Y%m%d_%H%M%S)
${SUDO_CMD} sed -i 's/=enforcing/=disabled/g; s/=permissive/=disabled/g' /etc/selinux/config
echo ""
print_info "Updated SELinux configuration:"
echo -e "${CYAN}----------------------------------------${NC}"
${SUDO_CMD} grep "^SELINUX=" /etc/selinux/config
echo -e "${CYAN}----------------------------------------${NC}"
echo ""
print_warning "SELinux disabled - REBOOT REQUIRED"
SELINUX_DISABLED=true
}
#==============================================================================
# FINAL SUMMARY
#==============================================================================
print_final_summary() {
local summary_hostname summary_safe_hostname summary_ts
local local_copy_target local_copy_cmd local_hash_cmd
summary_hostname="$(hostname)"
summary_safe_hostname="$(echo "$summary_hostname" | sed 's/[^A-Za-z0-9._-]/_/g')"
summary_ts="$(date +%Y%m%d_%H%M%S)"
local_copy_target="/home/aw/code/atvm/log/atvm_configuration_${summary_safe_hostname}_${summary_ts}.log"
local_copy_cmd="scp root@<client-ip>:${LOG_FILE} ${local_copy_target}"
local_hash_cmd="sha256sum ${local_copy_target}"
local summary_output=""
summary_output+="
"
summary_output+="========================================
"
summary_output+=" FINAL INSTALLATION SUMMARY
"
summary_output+="========================================
"
summary_output+="
"
summary_output+="*** System Information ***
"
summary_output+=" * Distribution: $DISTRO
"
summary_output+=" * Package Manager: $PM
"
summary_output+=" * Kernel: $(uname -r)
"
summary_output+=" * Hostname: $(hostname)
"
summary_output+="
"
summary_output+="*** Actions Performed ***
"
summary_output+="
"
if [[ "$HOST_IDENTITY_VALIDATED" == true ]]; then
summary_output+="[OK] Step 0: Target Host Identity Validation
"
summary_output+=" * Expected IP matched: ${EXPECTED_IP}
"
summary_output+=" * Expected hostname matched: ${EXPECTED_HOSTNAME}
"
else
summary_output+="[SKIP] Step 0: Target Host Identity Validation
"
fi
summary_output+="
"
if [[ "$REPO_FIX_COMPLETED" == true ]]; then
summary_output+="[OK] Step 1: Repository Configuration
"
summary_output+=" * CD-ROM/DVD repository entries commented out
"
summary_output+=" * Package lists updated
"
summary_output+=" * Backup files created with timestamps
"
else
summary_output+="[SKIP] Step 1: Repository Configuration
"
fi
summary_output+="
"
if [[ "$NETWORKMANAGER_COMPLETED" == true ]]; then
summary_output+="[OK] Step 8: Static IP Configuration
"
summary_output+=" * Interface: ${INTERFACE:-unknown}
"
summary_output+=" * IP Address: 192.168.3.191/22
"
summary_output+=" * Gateway: 192.168.0.1
"
summary_output+=" * DNS: 8.8.8.8, 8.8.4.4
"
summary_output+=" * Method: ${STATIC_IP_METHOD}
"
else
summary_output+="[SKIP] Step 8: Static IP Configuration
"
summary_output+=" * No network manager found or interface not available
"
fi
summary_output+="
"
if [[ "$SUDO_INSTALLED" == true ]]; then
summary_output+="[OK] sudo Installation
"
summary_output+=" * sudo package installed successfully
"
summary_output+="
"
fi
if [[ "$PACKAGE_INSTALL_COMPLETED" == true ]]; then
summary_output+="[OK] Step 6: Package Management
"
if [ ${#MULTIPATH_REMOVED[@]} -gt 0 ]; then
summary_output+=" * Removed multipath: ${MULTIPATH_REMOVED[*]}
"
fi
if [ ${#ISCSI_REMOVED[@]} -gt 0 ]; then
summary_output+=" * Removed iSCSI: ${ISCSI_REMOVED[*]}
"
fi
summary_output+=" * Kernel headers installed: $(uname -r)
"
summary_output+=" * Installed ${#PACKAGES_INSTALLED[@]} packages
"
else
summary_output+="[SKIP] Step 6: Package Management
"
fi
summary_output+="
"
if [[ "$DISTRO" == "ol" ]]; then
if [[ "$ORACLE_KERNEL_STEP_COMPLETED" == true ]]; then
summary_output+="[OK] Step 3: Oracle Kernel Default Configuration
"
summary_output+=" * Previous default: ${ORACLE_KERNEL_DEFAULT_BEFORE:-unknown}
"
summary_output+=" * Target non-UEK kernel: ${ORACLE_KERNEL_TARGET}
"
summary_output+=" * Active default: ${ORACLE_KERNEL_DEFAULT_AFTER:-unknown}
"
if [[ "$ORACLE_KERNEL_DEFAULT_CHANGED" == true ]]; then
summary_output+=" * Default kernel was changed during this run
"
else
summary_output+=" * Default kernel was already non-UEK
"
fi
if [[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]]; then
summary_output+=" * REBOOT REQUIRED to boot into non-UEK kernel
"
fi
else
summary_output+="[SKIP] Step 3: Oracle Kernel Default Configuration
"
summary_output+=" * Not applicable or non-UEK kernel not available
"
fi
summary_output+="
"
fi
if [[ "$DISTRO" == "ubuntu" ]]; then
if [[ "$ROOT_SSH_CONFIGURED" == true ]]; then
summary_output+="[OK] Step 2: Ubuntu Root SSH Access Configuration
"
summary_output+=" * Root password set to workflow value
"
summary_output+=" * PermitRootLogin enabled
"
summary_output+=" * PasswordAuthentication enabled
"
summary_output+=" * Reconnect as root/cdsi2012 for root-only workflow
"
else
summary_output+="[SKIP] Step 2: Ubuntu Root SSH Access Configuration
"
summary_output+=" * Not applied
"
fi
summary_output+="
"
if [[ "$AUTO_UPGRADES_DISABLED" == true ]]; then
summary_output+="[OK] Step 5: Ubuntu Auto Upgrade Configuration
"
summary_output+=" * Updated /etc/apt/apt.conf.d/20auto-upgrades
"
summary_output+=" * APT::Periodic::Update-Package-Lists set to 0
"
summary_output+=" * APT::Periodic::Unattended-Upgrade set to 0
"
else
summary_output+="[SKIP] Step 5: Ubuntu Auto Upgrade Configuration
"
summary_output+=" * Not applied
"
fi
summary_output+="
"
fi
if [[ "$DISTRO" == "rhel" || "$DISTRO" == "centos" || "$DISTRO" == "rocky" || "$DISTRO" == "almalinux" || "$DISTRO" == "fedora" || "$DISTRO" == "ol" ]]; then
if [[ "$SELINUX_DISABLED" == true ]]; then
summary_output+="[OK] Step 7: SELinux Configuration
"
[[ "$SELINUX_WAS_ENFORCING" == true ]] && summary_output+=" * Previous status: Enforcing
"
summary_output+=" * SELinux disabled in configuration
"
summary_output+=" * Backup: /etc/selinux/config.backup.*
"
summary_output+=" * REBOOT REQUIRED for changes to take effect
"
else
summary_output+="[SKIP] Step 7: SELinux Configuration
"
summary_output+=" * Already disabled or not applicable
"
fi
summary_output+="
"
fi
if is_selinux_distro; then
if [[ "$SELINUX_DISABLED" == true ]]; then
summary_output+="[OK] Step 9: SELinux Reboot And Verification
"
summary_output+=" * Automatic reboot will be triggered at end of script
"
summary_output+=" * Post-reboot verification service configured
"
else
summary_output+="[SKIP] Step 9: SELinux Reboot And Verification
"
summary_output+=" * SELinux change not pending reboot
"
fi
summary_output+="
"
fi
if setup_completed_without_errors; then
summary_output+="[OK] Step 10: Final Power State
"
summary_output+=" * Client remains powered on for controller log collection
"
summary_output+=" * Required: power off from controller only after log copy + SHA256 verification succeeds
"
else
summary_output+="[WARNING] Step 10: Final Power State
"
summary_output+=" * Client will remain powered on because errors were encountered
"
fi
summary_output+="
"
summary_output+="========================================
"
if setup_completed_without_errors; then
summary_output+=" SUCCESS: ATVM VM Setup Complete!
"
else
summary_output+=" WARNING: Setup completed with issues
"
fi
summary_output+="========================================
"
if [[ "$SELINUX_DISABLED" == true ]] || [[ "$NETWORKMANAGER_COMPLETED" == true ]] || [[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]]; then
summary_output+="
"
summary_output+="========================================
"
summary_output+=" REBOOT RECOMMENDED
"
summary_output+="========================================
"
summary_output+="
"
[[ "$SELINUX_DISABLED" == true ]] && summary_output+=" * SELinux changes require reboot
"
[[ "$NETWORKMANAGER_COMPLETED" == true ]] && summary_output+=" * Network changes may benefit from reboot
"
[[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]] && summary_output+=" * Oracle default kernel changed to non-UEK
"
summary_output+="
"
summary_output+="To reboot: sudo reboot
"
summary_output+="
"
fi
summary_output+="
"
summary_output+="Log saved to: $LOG_FILE
"
summary_output+="Note: this script runs on the client VM and cannot directly write files to controller filesystem paths.
"
summary_output+="
Controller-side required step (run from controller host):
"
summary_output+=" Preferred: /home/aw/code/atvm/run_atvm_setup_and_collect_log.sh --collect-after-complete
"
summary_output+=" If a prior run-and-collect session was interrupted, run --collect-after-complete only (do not rerun setup).
"
summary_output+=" (Wrapper checks REMOTE_IP_PRIMARY and REMOTE_IP_SECONDARY; defaults are 192.168.0.121 and 192.168.3.191)
"
summary_output+=" ${local_copy_cmd}
"
summary_output+="
Optional integrity check:
"
summary_output+=" ssh root@<client-ip> 'sha256sum ${LOG_FILE}'
"
summary_output+=" ${local_hash_cmd}
"
summary_output+="
After hash match is confirmed on controller:
"
summary_output+=" Preferred: run_atvm_setup_and_collect_log.sh handles automatic power-off when no [ERROR] is present
"
summary_output+=" Manual fallback: ssh root@<client-ip> 'shutdown -h now'
"
# Print to screen with colors
echo ""
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} FINAL INSTALLATION SUMMARY${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
echo -e "${BLUE}*** System Information ***${NC}"
echo " * Distribution: $DISTRO"
echo " * Package Manager: $PM"
echo " * Kernel: $(uname -r)"
echo " * Hostname: $(hostname)"
echo ""
echo -e "${BLUE}*** Actions Performed ***${NC}"
echo ""
if [[ "$HOST_IDENTITY_VALIDATED" == true ]]; then
echo -e "${GREEN}[OK] Step 0: Target Host Identity Validation${NC}"
echo " * Expected IP matched: ${EXPECTED_IP}"
echo " * Expected hostname matched: ${EXPECTED_HOSTNAME}"
else
echo -e "${YELLOW}[SKIP] Step 0: Target Host Identity Validation${NC}"
fi
echo ""
if [[ "$REPO_FIX_COMPLETED" == true ]]; then
echo -e "${GREEN}[OK] Step 1: Repository Configuration${NC}"
echo " * CD-ROM/DVD repository entries commented out"
echo " * Package lists updated"
echo " * Backup files created with timestamps"
else
echo -e "${YELLOW}[SKIP] Step 1: Repository Configuration${NC}"
fi
echo ""
if [[ "$NETWORKMANAGER_COMPLETED" == true ]]; then
echo -e "${GREEN}[OK] Step 8: Static IP Configuration${NC}"
echo " * Interface: ${INTERFACE:-unknown}"
echo " * IP Address: 192.168.3.191/22"
echo " * Gateway: 192.168.0.1"
echo " * DNS: 8.8.8.8, 8.8.4.4"
echo " * Method: ${STATIC_IP_METHOD}"
else
echo -e "${YELLOW}[SKIP] Step 8: Static IP Configuration${NC}"
echo " * No network manager found or interface not available"
fi
echo ""
if [[ "$SUDO_INSTALLED" == true ]]; then
echo -e "${GREEN}[OK] sudo Installation${NC}"
echo " * sudo package installed successfully"
echo ""
fi
if [[ "$PACKAGE_INSTALL_COMPLETED" == true ]]; then
echo -e "${GREEN}[OK] Step 6: Package Management${NC}"
[ ${#MULTIPATH_REMOVED[@]} -gt 0 ] && echo " * Removed multipath: ${MULTIPATH_REMOVED[*]}"
[ ${#ISCSI_REMOVED[@]} -gt 0 ] && echo " * Removed iSCSI: ${ISCSI_REMOVED[*]}"
echo " * Kernel headers installed: $(uname -r)"
echo " * Installed ${#PACKAGES_INSTALLED[@]} packages"
else
echo -e "${YELLOW}[SKIP] Step 6: Package Management${NC}"
fi
echo ""
if [[ "$DISTRO" == "ol" ]]; then
if [[ "$ORACLE_KERNEL_STEP_COMPLETED" == true ]]; then
echo -e "${GREEN}[OK] Step 3: Oracle Kernel Default Configuration${NC}"
echo " * Previous default: ${ORACLE_KERNEL_DEFAULT_BEFORE:-unknown}"
echo " * Target non-UEK kernel: ${ORACLE_KERNEL_TARGET}"
echo " * Active default: ${ORACLE_KERNEL_DEFAULT_AFTER:-unknown}"
if [[ "$ORACLE_KERNEL_DEFAULT_CHANGED" == true ]]; then
echo " * Default kernel was changed during this run"
else
echo " * Default kernel was already non-UEK"
fi
[[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]] && echo -e " ${YELLOW}* REBOOT REQUIRED to boot into non-UEK kernel${NC}"
else
echo -e "${YELLOW}[SKIP] Step 3: Oracle Kernel Default Configuration${NC}"
echo " * Not applicable or non-UEK kernel not available"
fi
echo ""
fi
if [[ "$DISTRO" == "ubuntu" ]]; then
if [[ "$ROOT_SSH_CONFIGURED" == true ]]; then
echo -e "${GREEN}[OK] Step 2: Ubuntu Root SSH Access Configuration${NC}"
echo " * Root password set to workflow value"
echo " * PermitRootLogin enabled"
echo " * PasswordAuthentication enabled"
echo " * Reconnect as root/cdsi2012 for root-only workflow"
else
echo -e "${YELLOW}[SKIP] Step 2: Ubuntu Root SSH Access Configuration${NC}"
echo " * Not applied"
fi
echo ""
if [[ "$AUTO_UPGRADES_DISABLED" == true ]]; then
echo -e "${GREEN}[OK] Step 5: Ubuntu Auto Upgrade Configuration${NC}"
echo " * Updated /etc/apt/apt.conf.d/20auto-upgrades"
echo " * APT::Periodic::Update-Package-Lists \"0\""
echo " * APT::Periodic::Unattended-Upgrade \"0\""
else
echo -e "${YELLOW}[SKIP] Step 5: Ubuntu Auto Upgrade Configuration${NC}"
echo " * Not applied"
fi
echo ""
fi
if [[ "$DISTRO" == "rhel" || "$DISTRO" == "centos" || "$DISTRO" == "rocky" || "$DISTRO" == "almalinux" || "$DISTRO" == "fedora" || "$DISTRO" == "ol" ]]; then
if [[ "$SELINUX_DISABLED" == true ]]; then
echo -e "${GREEN}[OK] Step 7: SELinux Configuration${NC}"
[[ "$SELINUX_WAS_ENFORCING" == true ]] && echo " * Previous status: Enforcing"
echo " * SELinux disabled in configuration"
echo " * Backup: /etc/selinux/config.backup.*"
echo -e " ${YELLOW}* REBOOT REQUIRED for changes to take effect${NC}"
else
echo -e "${YELLOW}[SKIP] Step 7: SELinux Configuration${NC}"
echo " * Already disabled or not applicable"
fi
echo ""
fi
if is_selinux_distro; then
if [[ "$SELINUX_DISABLED" == true ]]; then
echo -e "${GREEN}[OK] Step 9: SELinux Reboot And Verification${NC}"
echo " * Automatic reboot will be triggered at end of script"
echo " * Post-reboot verification service configured"
else
echo -e "${YELLOW}[SKIP] Step 9: SELinux Reboot And Verification${NC}"
echo " * SELinux change not pending reboot"
fi
echo ""
fi
if setup_completed_without_errors; then
echo -e "${GREEN}[OK] Step 10: Final Power State${NC}"
echo " * Client remains powered on for controller log collection"
echo " * Required: power off from controller only after log copy + SHA256 verification succeeds"
else
echo -e "${YELLOW}[WARNING] Step 10: Final Power State${NC}"
echo " * Client will remain powered on because errors were encountered"
fi
echo ""
echo -e "${CYAN}========================================${NC}"
if setup_completed_without_errors; then
echo -e "${GREEN} SUCCESS: ATVM VM Setup Complete!${NC}"
else
echo -e "${YELLOW} WARNING: Setup completed with issues${NC}"
fi
echo -e "${CYAN}========================================${NC}"
if [[ "$SELINUX_DISABLED" == true ]] || [[ "$NETWORKMANAGER_COMPLETED" == true ]] || [[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]]; then
echo ""
echo -e "${YELLOW}========================================${NC}"
echo -e "${YELLOW} REBOOT RECOMMENDED${NC}"
echo -e "${YELLOW}========================================${NC}"
echo ""
[[ "$SELINUX_DISABLED" == true ]] && echo -e "${YELLOW} * SELinux changes require reboot${NC}"
[[ "$NETWORKMANAGER_COMPLETED" == true ]] && echo -e "${YELLOW} * Network changes may benefit from reboot${NC}"
[[ "$ORACLE_KERNEL_REBOOT_REQUIRED" == true ]] && echo -e "${YELLOW} * Oracle default kernel changed to non-UEK${NC}"
echo ""
echo -e "${YELLOW}To reboot: sudo reboot${NC}"
echo ""
fi
echo ""
echo -e "${GREEN}Log saved to: $LOG_FILE${NC}"
echo -e "${YELLOW}Note:${NC} this script runs on the client VM and cannot directly write to controller filesystem paths."
echo -e "${GREEN}Create local identical copy on controller:${NC}"
echo " Preferred: /home/aw/code/atvm/run_atvm_setup_and_collect_log.sh --collect-after-complete"
echo " If a prior run-and-collect session was interrupted, run --collect-after-complete only (do not rerun setup)."
echo " (Wrapper checks REMOTE_IP_PRIMARY and REMOTE_IP_SECONDARY; defaults are 192.168.0.121 and 192.168.3.191)"
echo " ${local_copy_cmd}"
echo -e "${GREEN}Optional integrity check:${NC}"
echo " ssh root@<client-ip> 'sha256sum ${LOG_FILE}'"
echo " ${local_hash_cmd}"
echo ""
echo -e "${GREEN}After hash match is confirmed on controller:${NC}"
echo " Preferred: run_atvm_setup_and_collect_log.sh handles automatic power-off when no [ERROR] is present"
echo " Manual fallback: ssh root@<client-ip> 'shutdown -h now'"
# Write to log file (without colors)
echo "$summary_output" >> "$LOG_FILE"
}
#==============================================================================
# MAIN
#==============================================================================
main() {
ATVM_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ $EUID -eq 0 ]]; then
LOG_FILE="/root/atvm_setup_script.log"
else
LOG_FILE="${ATVM_SCRIPT_DIR}/atvm_setup_script.log"
fi
echo "ATVM VM Setup Script - $(date)" > "$LOG_FILE"
echo "========================================" >> "$LOG_FILE"
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN} ATVM VM Setup Script${NC}"
echo -e "${CYAN}========================================${NC}"
echo ""
parse_args "$@"
validate_target_host_identity
# Initial privilege check - verify we can run (as root or with sudo)
check_sudo
# Step 1: Fix repositories FIRST (before installing anything including sudo)
fix_repositories
# Step 2: Ubuntu-specific root SSH/password workflow
configure_ubuntu_root_ssh_access
# Step 3: Install sudo if needed (after repos are fixed)
if ! command -v sudo &> /dev/null; then
print_section "Installing sudo"
install_sudo_if_needed
# Verify sudo is now available
if ! command -v sudo &> /dev/null && [[ $EUID -ne 0 ]]; then
print_error "CRITICAL: sudo installation failed"
exit 1
fi
print_info "sudo is now available - proceeding with setup"
echo ""
fi
# Step 4: On Oracle Linux, set default boot kernel to non-UEK if available
configure_oracle_non_uek_kernel
# Step 5: Disable Ubuntu unattended auto-upgrades
disable_ubuntu_auto_upgrades
# Step 6: Install packages
run_package_installation
# Step 7: Disable SELinux
disable_selinux
# Step 8: Configure static IP at the very end (can interrupt SSH session)
configure_static_ip
# Final summary
print_final_summary
# Step 9: On SELinux distros, reboot and verify SELinux runtime state after boot
reboot_and_verify_selinux_if_needed
# Step 10: Keep client on so controller can collect/verify logs before manual power-off
poweroff_client_if_successful
}
main "$@"