- remove hardcoded credentials, tokens, registration codes, and similar secret values from tracked ATVM and CDS MCP docs - replace those values with references to /home/aw/code/cds/.env.credentials.local and the corresponding environment variable names - update current operator guides to instruct sourcing .env.credentials.local before credential-dependent setup and automation workflows - update the ATVM setup scripts to consume ATVM_TARGET_PASSWORD from the environment instead of hardcoding the Ubuntu root SSH password - scrub the remaining tracked artifact log entry that still included the old CMC registration code - keep the local-only credential inventory in .env.credentials.local while leaving that file untracked
1875 lines
66 KiB
Bash
1875 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
|
|
|
|
ATVM_ROOT_SSH_PASSWORD="${ATVM_TARGET_PASSWORD:-}"
|
|
|
|
# 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"
|
|
|
|
if [[ -z "$ATVM_ROOT_SSH_PASSWORD" ]]; then
|
|
print_error "ATVM_TARGET_PASSWORD must be set before running the Ubuntu root SSH workflow"
|
|
exit 1
|
|
fi
|
|
|
|
echo "root:${ATVM_ROOT_SSH_PASSWORD}" | ${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 using the ATVM_TARGET_PASSWORD value"
|
|
}
|
|
|
|
#==============================================================================
|
|
# 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 using the ATVM_TARGET_PASSWORD value 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 using the ATVM_TARGET_PASSWORD value 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 "$@"
|