Initial commit: loc_az_hci (smom-dbis-138 excluded via .gitignore)
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
74
scripts/infrastructure/add-ssh-key-to-r630.sh
Executable file
74
scripts/infrastructure/add-ssh-key-to-r630.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Add SSH key to R630 (192.168.1.49) to enable key-based authentication
|
||||
# This script attempts to add the SSH key via Proxmox API or provides instructions
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Load environment variables
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
|
||||
SSH_KEY_PUB="${SSH_KEY}.pub"
|
||||
R630_IP="192.168.1.49"
|
||||
|
||||
if [ ! -f "$SSH_KEY_PUB" ]; then
|
||||
log_error "SSH public key not found: $SSH_KEY_PUB"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SSH_KEY_CONTENT=$(cat "$SSH_KEY_PUB")
|
||||
|
||||
log_info "Adding SSH key to R630 (192.168.1.49)..."
|
||||
log_info "SSH Key: $SSH_KEY_PUB"
|
||||
echo ""
|
||||
|
||||
# Try to add key via ssh-copy-id if password auth works
|
||||
log_info "Attempting to add SSH key using ssh-copy-id..."
|
||||
if ssh-copy-id -i "$SSH_KEY_PUB" -o StrictHostKeyChecking=no "root@${R630_IP}" 2>/dev/null; then
|
||||
log_info "✓ SSH key added successfully via ssh-copy-id"
|
||||
log_info "Testing SSH connection..."
|
||||
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o ConnectTimeout=5 "root@${R630_IP}" "echo 'SSH key authentication working!'" 2>/dev/null; then
|
||||
log_info "✓ SSH key authentication confirmed!"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# If ssh-copy-id failed, provide manual instructions
|
||||
log_warn "ssh-copy-id failed (password auth may be disabled)"
|
||||
echo ""
|
||||
log_info "Manual steps to add SSH key:"
|
||||
echo ""
|
||||
log_info "1. Access R630 console/web terminal:"
|
||||
log_info " - Open https://192.168.1.49:8006"
|
||||
log_info " - Go to: Shell (or use console)"
|
||||
echo ""
|
||||
log_info "2. Run this command on R630:"
|
||||
echo ""
|
||||
echo "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '${SSH_KEY_CONTENT}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && echo 'SSH key added'"
|
||||
echo ""
|
||||
log_info "3. Or copy this one-liner and paste on R630:"
|
||||
echo ""
|
||||
echo "echo '${SSH_KEY_CONTENT}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && chmod 700 ~/.ssh"
|
||||
echo ""
|
||||
log_info "4. After adding the key, test connection:"
|
||||
log_info " ssh -i $SSH_KEY root@${R630_IP}"
|
||||
|
||||
126
scripts/infrastructure/auto-complete-template-setup.sh
Executable file
126
scripts/infrastructure/auto-complete-template-setup.sh
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Auto-Complete Template Setup and VM Recreation
|
||||
# Monitors for template creation and automatically recreates VMs
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
log_header() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN}$1${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
|
||||
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
|
||||
PROXMOX_URL="https://192.168.1.206:8006"
|
||||
PROXMOX_NODE="pve"
|
||||
TEMPLATE_ID=9000
|
||||
|
||||
check_template() {
|
||||
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
|
||||
"$PROXMOX_URL/api2/json/access/ticket" 2>/dev/null)
|
||||
|
||||
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
|
||||
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local config=$(curl -k -s \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID/config" 2>&1)
|
||||
|
||||
# Check if it exists and is a template
|
||||
if echo "$config" | grep -q '"name"' && echo "$config" | grep -q '"template".*1'; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
log_header "Auto-Complete Template Setup"
|
||||
echo ""
|
||||
|
||||
log_step "Step 1: Template Creation (Manual - Required)"
|
||||
echo ""
|
||||
log_info "Please complete these steps in Proxmox Web UI:"
|
||||
echo ""
|
||||
echo "1. Upload Cloud Image:"
|
||||
echo " • Proxmox → Storage → local → Upload"
|
||||
echo " • File: ./downloads/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
echo ""
|
||||
echo "2. Create VM 9000:"
|
||||
echo " • Create VM (ID: 9000, Name: ubuntu-24.04-cloudinit)"
|
||||
echo " • Import disk from uploaded image"
|
||||
echo " • Configure Cloud-Init (User: ubuntu, SSH key)"
|
||||
echo ""
|
||||
echo "3. Convert to Template:"
|
||||
echo " • Right-click VM 9000 → Convert to Template"
|
||||
echo ""
|
||||
log_info "See: QUICK_TEMPLATE_GUIDE.md for detailed steps"
|
||||
echo ""
|
||||
|
||||
log_step "Step 2: Monitoring for Template"
|
||||
log_info "Checking every 10 seconds for template creation..."
|
||||
echo ""
|
||||
|
||||
local check_count=0
|
||||
local max_checks=180 # 30 minutes
|
||||
|
||||
while [ $check_count -lt $max_checks ]; do
|
||||
check_count=$((check_count + 1))
|
||||
|
||||
if check_template; then
|
||||
echo ""
|
||||
log_info "✓ Template detected! Proceeding with VM recreation..."
|
||||
echo ""
|
||||
|
||||
# Run VM recreation
|
||||
export SSH_KEY="$HOME/.ssh/id_rsa"
|
||||
export SSH_USER="ubuntu"
|
||||
./scripts/recreate-vms-from-template.sh
|
||||
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [ $((check_count % 6)) -eq 0 ]; then
|
||||
echo -n "."
|
||||
fi
|
||||
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo ""
|
||||
log_info "Template not detected after 30 minutes"
|
||||
log_info "Please create template manually, then run:"
|
||||
echo " ./scripts/check-and-recreate.sh"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
174
scripts/infrastructure/automate-all-setup.sh
Executable file
174
scripts/infrastructure/automate-all-setup.sh
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Complete Automation Script
|
||||
# Handles all setup steps with prerequisite checking
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
log_header() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN}$1${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
}
|
||||
|
||||
# VM configurations
|
||||
declare -A VMS=(
|
||||
["100"]="cloudflare-tunnel:192.168.1.60:scripts/setup-cloudflare-tunnel.sh"
|
||||
["101"]="k3s-master:192.168.1.188:scripts/setup-k3s.sh"
|
||||
["102"]="git-server:192.168.1.121:scripts/setup-git-server.sh"
|
||||
["103"]="observability:192.168.1.82:scripts/setup-observability.sh"
|
||||
)
|
||||
|
||||
# Check VM is ready
|
||||
check_vm_ready() {
|
||||
local ip=$1
|
||||
local name=$2
|
||||
|
||||
# Ping test
|
||||
if ! ping -c 1 -W 2 "$ip" >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# SSH test
|
||||
if ! timeout 2 bash -c "echo >/dev/tcp/$ip/22" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Setup VM service
|
||||
setup_vm_service() {
|
||||
local name=$1
|
||||
local ip=$2
|
||||
local script=$3
|
||||
|
||||
log_step "Setting up $name on $ip..."
|
||||
|
||||
# Check if VM is ready
|
||||
if ! check_vm_ready "$ip" "$name"; then
|
||||
log_warn "$name ($ip) is not ready yet. Skipping..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Copying setup script to $name..."
|
||||
|
||||
# Try to copy script (may need password or SSH key)
|
||||
if scp -o ConnectTimeout=5 -o StrictHostKeyChecking=no "$script" "ubuntu@$ip:/tmp/setup.sh" 2>/dev/null; then
|
||||
log_info "Running setup script on $name..."
|
||||
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no "ubuntu@$ip" "sudo bash /tmp/setup.sh" 2>&1 | while read line; do
|
||||
log_info " $line"
|
||||
done
|
||||
|
||||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||||
log_info "✓ $name setup completed"
|
||||
return 0
|
||||
else
|
||||
log_error "Setup failed on $name"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_warn "Could not copy script to $name"
|
||||
log_info "Manual steps for $name:"
|
||||
echo " 1. SSH to $name: ssh ubuntu@$ip"
|
||||
echo " 2. Copy $script to VM"
|
||||
echo " 3. Run: sudo bash /path/to/script"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
log_header "Complete Setup Automation"
|
||||
echo ""
|
||||
|
||||
log_step "Phase 1: Checking Prerequisites"
|
||||
echo ""
|
||||
|
||||
# Check VM configurations
|
||||
log_info "Verifying VM configurations..."
|
||||
if ! ./scripts/check-vm-status.sh > /dev/null 2>&1; then
|
||||
log_warn "Some VMs may not be fully configured"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
log_step "Phase 2: Checking VM Readiness"
|
||||
echo ""
|
||||
|
||||
local all_ready=true
|
||||
for vmid in "${!VMS[@]}"; do
|
||||
IFS=':' read -r name ip script <<< "${VMS[$vmid]}"
|
||||
if check_vm_ready "$ip" "$name"; then
|
||||
log_info "✓ $name is ready"
|
||||
else
|
||||
log_warn "✗ $name is not ready (Ubuntu may not be installed)"
|
||||
all_ready=false
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [ "$all_ready" != true ]; then
|
||||
log_error "Not all VMs are ready. Please:"
|
||||
echo " 1. Complete Ubuntu installation on all VMs"
|
||||
echo " 2. Ensure static IPs are configured"
|
||||
echo " 3. Ensure SSH access works"
|
||||
echo " 4. Run this script again"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Phase 3: Running Setup Scripts"
|
||||
echo ""
|
||||
|
||||
local success_count=0
|
||||
for vmid in "${!VMS[@]}"; do
|
||||
IFS=':' read -r name ip script <<< "${VMS[$vmid]}"
|
||||
if setup_vm_service "$name" "$ip" "$script"; then
|
||||
success_count=$((success_count + 1))
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
log_header "Setup Complete"
|
||||
echo ""
|
||||
log_info "Successfully configured: $success_count/4 VMs"
|
||||
echo ""
|
||||
|
||||
if [ $success_count -eq 4 ]; then
|
||||
log_info "✅ All services are set up!"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo " - Configure Cloudflare Tunnel (see docs/cloudflare-integration.md)"
|
||||
echo " - Deploy services to K3s cluster"
|
||||
echo " - Configure GitOps repository"
|
||||
echo " - Set up monitoring dashboards"
|
||||
else
|
||||
log_warn "Some services need manual setup"
|
||||
log_info "See VM_STATUS_REPORT.md for details"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
107
scripts/infrastructure/complete-r630-cluster-join.sh
Executable file
107
scripts/infrastructure/complete-r630-cluster-join.sh
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Complete R630 Cluster Join
|
||||
# This script provides instructions and attempts automated join
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Load environment variables
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_step() { echo -e "\n${BLUE}=== $1 ===${NC}"; }
|
||||
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
|
||||
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
|
||||
ML110_IP="192.168.1.206"
|
||||
R630_IP="192.168.1.49"
|
||||
ROOT_PASS="${PVE_ROOT_PASS:-L@kers2010}"
|
||||
|
||||
log_step "Completing R630 Cluster Join"
|
||||
|
||||
# Check current cluster status
|
||||
log_info "Checking cluster status on ML110..."
|
||||
ML110_STATUS=$(ssh $SSH_OPTS "root@${ML110_IP}" "pvecm nodes 2>&1" || echo "")
|
||||
echo "$ML110_STATUS"
|
||||
|
||||
log_info "Checking cluster status on R630..."
|
||||
R630_STATUS=$(ssh $SSH_OPTS "root@${R630_IP}" "pvecm status 2>&1" || echo "")
|
||||
echo "$R630_STATUS"
|
||||
|
||||
if echo "$R630_STATUS" | grep -q "hc-cluster"; then
|
||||
log_info "✓ R630 is already in the cluster!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_step "Method 1: Join via Proxmox Web UI (Recommended)"
|
||||
log_info "1. Open https://${ML110_IP}:8006"
|
||||
log_info "2. Login as root"
|
||||
log_info "3. Go to: Datacenter → Cluster → Join Information"
|
||||
log_info "4. Copy the join command"
|
||||
log_info "5. Or go to: Datacenter → Cluster → Add"
|
||||
log_info "6. Enter R630 IP: ${R630_IP}"
|
||||
log_info "7. Enter root password: ${ROOT_PASS}"
|
||||
log_info "8. Click 'Join'"
|
||||
|
||||
log_step "Method 2: Join via SSH (Manual)"
|
||||
log_info "SSH to R630 and run:"
|
||||
echo ""
|
||||
echo "ssh -i $SSH_KEY root@${R630_IP}"
|
||||
echo "pvecm add ${ML110_IP}"
|
||||
echo "# Enter password when prompted: ${ROOT_PASS}"
|
||||
echo ""
|
||||
|
||||
log_step "Method 3: Automated Join Attempt"
|
||||
log_info "Attempting automated join..."
|
||||
|
||||
# Try using expect or similar approach
|
||||
if command -v expect &>/dev/null; then
|
||||
log_info "Using expect for password automation..."
|
||||
expect <<EOF
|
||||
spawn ssh $SSH_OPTS root@${R630_IP} "pvecm add ${ML110_IP}"
|
||||
expect {
|
||||
"password:" {
|
||||
send "${ROOT_PASS}\r"
|
||||
exp_continue
|
||||
}
|
||||
"yes/no" {
|
||||
send "yes\r"
|
||||
exp_continue
|
||||
}
|
||||
eof
|
||||
}
|
||||
EOF
|
||||
else
|
||||
log_warn "expect not installed. Install with: sudo apt-get install expect"
|
||||
log_info "Or use Method 1 (Web UI) or Method 2 (Manual SSH)"
|
||||
fi
|
||||
|
||||
# Verify join
|
||||
sleep 10
|
||||
log_info "Verifying cluster join..."
|
||||
if ssh $SSH_OPTS "root@${R630_IP}" "pvecm status 2>&1" | grep -q "hc-cluster"; then
|
||||
log_info "✓ R630 successfully joined the cluster!"
|
||||
ssh $SSH_OPTS "root@${ML110_IP}" "pvecm nodes"
|
||||
else
|
||||
log_warn "Cluster join may still be in progress or needs manual approval"
|
||||
log_info "Check cluster status:"
|
||||
log_info " ssh root@${ML110_IP} 'pvecm nodes'"
|
||||
log_info " ssh root@${R630_IP} 'pvecm status'"
|
||||
fi
|
||||
|
||||
88
scripts/infrastructure/download-ubuntu-cloud-image.sh
Executable file
88
scripts/infrastructure/download-ubuntu-cloud-image.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Download Ubuntu Cloud-Init Image for Proxmox Template
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Ubuntu versions
|
||||
UBUNTU_24_04_URL="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
UBUNTU_22_04_URL="https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
|
||||
VERSION="${1:-24.04}"
|
||||
DOWNLOAD_DIR="${2:-./downloads}"
|
||||
|
||||
main() {
|
||||
echo "========================================="
|
||||
echo "Download Ubuntu Cloud-Init Image"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
case "$VERSION" in
|
||||
24.04)
|
||||
URL="$UBUNTU_24_04_URL"
|
||||
FILENAME="ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
;;
|
||||
22.04)
|
||||
URL="$UBUNTU_22_04_URL"
|
||||
FILENAME="ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unsupported version. Use 22.04 or 24.04"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$DOWNLOAD_DIR"
|
||||
OUTPUT="$DOWNLOAD_DIR/$FILENAME"
|
||||
|
||||
log_step "Downloading Ubuntu $VERSION Cloud Image..."
|
||||
log_info "URL: $URL"
|
||||
log_info "Output: $OUTPUT"
|
||||
echo ""
|
||||
|
||||
if [ -f "$OUTPUT" ]; then
|
||||
log_info "File already exists: $OUTPUT"
|
||||
read -p "Overwrite? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "Skipping download"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Download with progress
|
||||
if command -v wget &> /dev/null; then
|
||||
wget --progress=bar:force -O "$OUTPUT" "$URL"
|
||||
elif command -v curl &> /dev/null; then
|
||||
curl -L --progress-bar -o "$OUTPUT" "$URL"
|
||||
else
|
||||
log_error "Neither wget nor curl found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "✓ Download complete: $OUTPUT"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
log_info " 1. Upload to Proxmox storage"
|
||||
log_info " 2. Convert to template"
|
||||
log_info " 3. Use for cloning VMs"
|
||||
echo ""
|
||||
log_info "See: docs/proxmox-ubuntu-images.md for details"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
109
scripts/infrastructure/enable-ssh-r630.sh
Executable file
109
scripts/infrastructure/enable-ssh-r630.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Enable SSH on R630 Proxmox Host (192.168.1.49)
|
||||
# This script attempts to enable SSH via Proxmox API
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Load environment variables
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
PROXMOX_URL="${PROXMOX_R630_URL:-https://192.168.1.49:8006}"
|
||||
PROXMOX_USER="${PVE_USERNAME:-root@pam}"
|
||||
PROXMOX_PASS="${PVE_ROOT_PASS:-}"
|
||||
PROXMOX_NODE="${PROXMOX_R630_NODE:-pve}"
|
||||
|
||||
if [ -z "$PROXMOX_PASS" ]; then
|
||||
log_error "PVE_ROOT_PASS not set in .env file"
|
||||
log_info "Please set PVE_ROOT_PASS in .env or provide password:"
|
||||
read -sp "Password: " PROXMOX_PASS
|
||||
echo ""
|
||||
fi
|
||||
|
||||
log_info "Attempting to enable SSH on R630 (192.168.1.49) via Proxmox API..."
|
||||
|
||||
# Get API token
|
||||
log_info "Authenticating with Proxmox API..."
|
||||
RESPONSE=$(curl -s -k --connect-timeout 10 --max-time 15 \
|
||||
-d "username=${PROXMOX_USER}&password=${PROXMOX_PASS}" \
|
||||
"${PROXMOX_URL}/api2/json/access/ticket" 2>&1)
|
||||
|
||||
if ! echo "$RESPONSE" | grep -q '"data"'; then
|
||||
log_error "Failed to authenticate with Proxmox API"
|
||||
log_warn "Response: $RESPONSE"
|
||||
log_info ""
|
||||
log_info "Alternative: Enable SSH via Proxmox Web UI:"
|
||||
log_info " 1. Open ${PROXMOX_URL} in browser"
|
||||
log_info " 2. Login as root"
|
||||
log_info " 3. Go to: System → Services → ssh"
|
||||
log_info " 4. Click 'Enable' and 'Start'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TICKET=$(echo "$RESPONSE" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
|
||||
CSRF=$(echo "$RESPONSE" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$TICKET" ] || [ -z "$CSRF" ]; then
|
||||
log_error "Failed to get API token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "✓ Authenticated successfully"
|
||||
|
||||
# Enable SSH service
|
||||
log_info "Enabling SSH service..."
|
||||
SSH_RESPONSE=$(curl -s -k -X POST \
|
||||
-H "Cookie: PVEAuthCookie=${TICKET}" \
|
||||
-H "CSRFPreventionToken: ${CSRF}" \
|
||||
"${PROXMOX_URL}/api2/json/nodes/${PROXMOX_NODE}/services/ssh/start" 2>&1)
|
||||
|
||||
if echo "$SSH_RESPONSE" | grep -q '"data"'; then
|
||||
log_info "✓ SSH service started"
|
||||
else
|
||||
log_warn "SSH service start response: $SSH_RESPONSE"
|
||||
fi
|
||||
|
||||
# Enable SSH on boot
|
||||
log_info "Enabling SSH on boot..."
|
||||
ENABLE_RESPONSE=$(curl -s -k -X POST \
|
||||
-H "Cookie: PVEAuthCookie=${TICKET}" \
|
||||
-H "CSRFPreventionToken: ${CSRF}" \
|
||||
-d "enable=1" \
|
||||
"${PROXMOX_URL}/api2/json/nodes/${PROXMOX_NODE}/services/ssh" 2>&1)
|
||||
|
||||
if echo "$ENABLE_RESPONSE" | grep -q '"data"'; then
|
||||
log_info "✓ SSH service enabled on boot"
|
||||
else
|
||||
log_warn "SSH enable response: $ENABLE_RESPONSE"
|
||||
fi
|
||||
|
||||
# Test SSH access
|
||||
log_info "Testing SSH access..."
|
||||
sleep 2
|
||||
if ssh -i "${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}" -o StrictHostKeyChecking=no -o ConnectTimeout=5 "root@192.168.1.49" "echo 'SSH OK'" &>/dev/null; then
|
||||
log_info "✓ SSH access confirmed!"
|
||||
log_info "You can now SSH to R630:"
|
||||
log_info " ssh -i ~/.ssh/id_ed25519_proxmox root@192.168.1.49"
|
||||
else
|
||||
log_warn "SSH test failed. SSH may need a moment to start."
|
||||
log_info "Try manually: ssh root@192.168.1.49"
|
||||
fi
|
||||
|
||||
172
scripts/infrastructure/fix-corrupted-image.sh
Executable file
172
scripts/infrastructure/fix-corrupted-image.sh
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Fix Corrupted Proxmox Cloud Image
|
||||
# This script removes corrupted images and helps re-upload a fresh copy
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_ML110_URL#https://}"
|
||||
PROXMOX_HOST="${PROXMOX_HOST%%:*}"
|
||||
IMAGE_NAME="ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
LOCAL_IMAGE="${1:-./downloads/${IMAGE_NAME}}"
|
||||
REMOTE_PATH="/var/lib/vz/template/iso/${IMAGE_NAME}"
|
||||
REMOTE_IMPORT_PATH="/var/lib/vz/import/${IMAGE_NAME}.raw"
|
||||
|
||||
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
|
||||
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
|
||||
|
||||
main() {
|
||||
echo "========================================="
|
||||
echo "Fix Corrupted Proxmox Cloud Image"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
if [ -z "$PVE_PASSWORD" ]; then
|
||||
log_error "PVE_ROOT_PASS not set in .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$PROXMOX_HOST" ]; then
|
||||
log_error "PROXMOX_ML110_URL not set in .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Target Proxmox host: $PROXMOX_HOST"
|
||||
log_info "Image name: $IMAGE_NAME"
|
||||
echo ""
|
||||
|
||||
# Check if local image exists
|
||||
if [ ! -f "$LOCAL_IMAGE" ]; then
|
||||
log_warn "Local image not found: $LOCAL_IMAGE"
|
||||
log_info "Downloading image..."
|
||||
./scripts/download-ubuntu-cloud-image.sh 24.04
|
||||
LOCAL_IMAGE="./downloads/${IMAGE_NAME}"
|
||||
|
||||
if [ ! -f "$LOCAL_IMAGE" ]; then
|
||||
log_error "Failed to download image"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify local image
|
||||
log_step "1. Verifying local image..."
|
||||
if qemu-img info "$LOCAL_IMAGE" > /dev/null 2>&1; then
|
||||
IMAGE_SIZE=$(du -h "$LOCAL_IMAGE" | cut -f1)
|
||||
log_info "✓ Local image is valid (Size: $IMAGE_SIZE)"
|
||||
else
|
||||
log_error "✗ Local image appears corrupted"
|
||||
log_info "Re-downloading..."
|
||||
rm -f "$LOCAL_IMAGE"
|
||||
./scripts/download-ubuntu-cloud-image.sh 24.04
|
||||
fi
|
||||
|
||||
# Check SSH access
|
||||
log_step "2. Testing SSH access to Proxmox host..."
|
||||
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@$PROXMOX_HOST "echo 'Connected'" > /dev/null 2>&1; then
|
||||
log_info "✓ SSH access confirmed"
|
||||
else
|
||||
log_error "✗ Cannot connect to Proxmox host via SSH"
|
||||
log_info "Make sure:"
|
||||
log_info " 1. SSH is enabled on Proxmox host"
|
||||
log_info " 2. Root login is allowed (or use SSH keys)"
|
||||
log_info " 3. Host is reachable from this machine"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove corrupted remote files
|
||||
log_step "3. Removing corrupted image files on Proxmox host..."
|
||||
ssh root@$PROXMOX_HOST "
|
||||
if [ -f '$REMOTE_PATH' ]; then
|
||||
echo 'Removing: $REMOTE_PATH'
|
||||
rm -f '$REMOTE_PATH'
|
||||
fi
|
||||
if [ -f '$REMOTE_IMPORT_PATH' ]; then
|
||||
echo 'Removing: $REMOTE_IMPORT_PATH'
|
||||
rm -f '$REMOTE_IMPORT_PATH'
|
||||
fi
|
||||
echo 'Cleanup complete'
|
||||
"
|
||||
|
||||
# Upload fresh image
|
||||
log_step "4. Uploading fresh image to Proxmox host..."
|
||||
log_info "This may take several minutes depending on your network speed..."
|
||||
log_info "Uploading: $LOCAL_IMAGE"
|
||||
log_info "To: root@$PROXMOX_HOST:$REMOTE_PATH"
|
||||
echo ""
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
ssh root@$PROXMOX_HOST "mkdir -p /var/lib/vz/template/iso"
|
||||
|
||||
# Upload with progress
|
||||
if command -v rsync &> /dev/null; then
|
||||
log_info "Using rsync (with progress)..."
|
||||
rsync -avz --progress "$LOCAL_IMAGE" root@$PROXMOX_HOST:"$REMOTE_PATH"
|
||||
else
|
||||
log_info "Using scp..."
|
||||
scp "$LOCAL_IMAGE" root@$PROXMOX_HOST:"$REMOTE_PATH"
|
||||
fi
|
||||
|
||||
# Verify uploaded image
|
||||
log_step "5. Verifying uploaded image on Proxmox host..."
|
||||
if ssh root@$PROXMOX_HOST "qemu-img info '$REMOTE_PATH' > /dev/null 2>&1"; then
|
||||
REMOTE_SIZE=$(ssh root@$PROXMOX_HOST "du -h '$REMOTE_PATH' | cut -f1")
|
||||
log_info "✓ Image uploaded successfully (Size: $REMOTE_SIZE)"
|
||||
else
|
||||
log_error "✗ Uploaded image verification failed"
|
||||
log_warn "The file may still be uploading or there may be storage issues"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set proper permissions
|
||||
log_step "6. Setting file permissions..."
|
||||
ssh root@$PROXMOX_HOST "chmod 644 '$REMOTE_PATH'"
|
||||
log_info "✓ Permissions set"
|
||||
|
||||
echo ""
|
||||
log_info "========================================="
|
||||
log_info "Image Fix Complete!"
|
||||
log_info "========================================="
|
||||
log_info ""
|
||||
log_info "The image has been successfully uploaded to:"
|
||||
log_info " $REMOTE_PATH"
|
||||
log_info ""
|
||||
log_info "Next steps:"
|
||||
log_info " 1. Verify the image in Proxmox Web UI:"
|
||||
log_info " Storage → local → Content"
|
||||
log_info " 2. Follow CREATE_VM_9000_STEPS.md to create VM 9000"
|
||||
log_info " 3. Or run: ./scripts/verify-proxmox-image.sh"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
336
scripts/infrastructure/improve-template-9000.sh
Executable file
336
scripts/infrastructure/improve-template-9000.sh
Executable file
@@ -0,0 +1,336 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Improve Template VM 9000 with Recommended Enhancements
|
||||
# This script applies all recommended improvements to template 9000
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Load environment variables
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
|
||||
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
|
||||
TEMPLATE_VMID=9000
|
||||
TEMP_VMID=9999
|
||||
TEMP_VM_NAME="template-update-temp"
|
||||
VM_USER="${VM_USER:-ubuntu}"
|
||||
|
||||
# Check if running on Proxmox host or remotely
|
||||
if command -v qm >/dev/null 2>&1; then
|
||||
RUN_LOCAL=true
|
||||
PROXMOX_CMD=""
|
||||
else
|
||||
RUN_LOCAL=false
|
||||
PROXMOX_CMD="ssh $SSH_OPTS root@$PROXMOX_HOST"
|
||||
fi
|
||||
|
||||
run_proxmox_cmd() {
|
||||
if [ "$RUN_LOCAL" = true ]; then
|
||||
eval "$1"
|
||||
else
|
||||
ssh $SSH_OPTS "root@$PROXMOX_HOST" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_ssh() {
|
||||
local ip="$1"
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
log_info "Waiting for SSH to be available on $ip..."
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if ssh $SSH_OPTS -o ConnectTimeout=5 "${VM_USER}@${ip}" "echo 'SSH ready'" &>/dev/null; then
|
||||
log_info "✓ SSH is ready"
|
||||
return 0
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
log_error "SSH not available after $max_attempts attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
get_vm_ip() {
|
||||
local vmid="$1"
|
||||
local ip=""
|
||||
|
||||
# Try to use helper library if available (when running on Proxmox host)
|
||||
if [ "$RUN_LOCAL" = true ] && [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
|
||||
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" 2>/dev/null || true
|
||||
if command -v get_vm_ip_from_guest_agent &>/dev/null; then
|
||||
ip=$(get_vm_ip_from_guest_agent "$vmid" 2>/dev/null || echo "")
|
||||
if [[ -n "$ip" && "$ip" != "null" ]]; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try to get IP from guest agent using jq directly (suppress errors)
|
||||
if run_proxmox_cmd "command -v jq >/dev/null 2>&1" 2>/dev/null; then
|
||||
ip=$(run_proxmox_cmd "qm guest cmd $vmid network-get-interfaces 2>/dev/null | jq -r '.[]?.\"ip-addresses\"[]? | select(.[\"ip-address-type\"] == \"ipv4\" and .\"ip-address\" != \"127.0.0.1\") | .\"ip-address\"' | head -n1" 2>/dev/null || echo "")
|
||||
if [[ -n "$ip" && "$ip" != "null" && "$ip" != "" ]]; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try MAC-based discovery: get VM MAC and match with ARP table
|
||||
local mac
|
||||
mac=$(run_proxmox_cmd "qm config $vmid 2>/dev/null | grep -E '^net0:' | cut -d',' -f1 | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ':'" 2>/dev/null || echo "")
|
||||
if [[ -n "$mac" ]]; then
|
||||
# Format MAC for matching (with colons)
|
||||
local mac_formatted="${mac:0:2}:${mac:2:2}:${mac:4:2}:${mac:6:2}:${mac:8:2}:${mac:10:2}"
|
||||
# Try to find IP in ARP table
|
||||
ip=$(run_proxmox_cmd "ip neigh show 2>/dev/null | grep -i '$mac_formatted' | grep -oE '192\.168\.1\.[0-9]+' | head -n1" 2>/dev/null || echo "")
|
||||
if [[ -n "$ip" ]]; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Return empty string (not a warning message)
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
main() {
|
||||
log_step "Template 9000 Improvement Script"
|
||||
|
||||
log_warn "This script will:"
|
||||
log_warn " 1. Clone template 9000 to temporary VM 9999"
|
||||
log_warn " 2. Boot the temporary VM"
|
||||
log_warn " 3. Apply all recommended improvements"
|
||||
log_warn " 4. Convert back to template"
|
||||
log_warn " 5. Replace original template 9000"
|
||||
echo ""
|
||||
|
||||
# Check if template exists
|
||||
if ! run_proxmox_cmd "qm config $TEMPLATE_VMID &>/dev/null"; then
|
||||
log_error "Template VM $TEMPLATE_VMID not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if temp VM already exists
|
||||
if run_proxmox_cmd "qm config $TEMP_VMID &>/dev/null" 2>/dev/null; then
|
||||
log_warn "Temporary VM $TEMP_VMID already exists. Destroying it..."
|
||||
run_proxmox_cmd "qm stop $TEMP_VMID" 2>/dev/null || true
|
||||
sleep 2
|
||||
run_proxmox_cmd "qm destroy $TEMP_VMID --purge" 2>/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Step 1: Clone template
|
||||
log_step "Step 1: Cloning Template to Temporary VM"
|
||||
log_info "Cloning template $TEMPLATE_VMID to VM $TEMP_VMID..."
|
||||
run_proxmox_cmd "qm clone $TEMPLATE_VMID $TEMP_VMID --name $TEMP_VM_NAME"
|
||||
log_info "✓ Template cloned"
|
||||
|
||||
# Step 2: Boot temporary VM
|
||||
log_step "Step 2: Booting Temporary VM"
|
||||
log_info "Starting VM $TEMP_VMID..."
|
||||
run_proxmox_cmd "qm start $TEMP_VMID"
|
||||
log_info "Waiting for VM to boot and get DHCP IP (this may take 60-90 seconds)..."
|
||||
sleep 60
|
||||
|
||||
# Step 3: Get IP and wait for SSH
|
||||
log_step "Step 3: Getting VM IP and Waiting for SSH"
|
||||
local vm_ip=""
|
||||
|
||||
# Try multiple times to get IP (VM may still be booting)
|
||||
log_info "Attempting to discover VM IP (may take a few attempts)..."
|
||||
for attempt in {1..10}; do
|
||||
vm_ip=$(get_vm_ip "$TEMP_VMID" 2>/dev/null || echo "")
|
||||
if [[ -n "$vm_ip" ]]; then
|
||||
log_info "✓ Discovered IP: $vm_ip"
|
||||
break
|
||||
fi
|
||||
if [ $attempt -lt 10 ]; then
|
||||
log_info "Attempt $attempt/10: Waiting for VM to finish booting..."
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
# If still no IP, try to get from Proxmox API or prompt user
|
||||
if [[ -z "$vm_ip" ]]; then
|
||||
log_warn "Could not automatically discover IP via guest agent."
|
||||
log_info "Please check Proxmox web UI or router DHCP leases for VM $TEMP_VMID IP address."
|
||||
log_info "You can also check with: ssh root@$PROXMOX_HOST 'qm config $TEMP_VMID'"
|
||||
echo ""
|
||||
read -p "Enter the VM IP address (or press Enter to skip and try again later): " vm_ip
|
||||
if [[ -z "$vm_ip" ]]; then
|
||||
log_error "IP address required. Exiting."
|
||||
log_info "VM $TEMP_VMID is running. You can manually:"
|
||||
log_info " 1. Get the IP from Proxmox UI or router"
|
||||
log_info " 2. SSH into the VM and apply improvements manually"
|
||||
log_info " 3. Run this script again with the IP"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
wait_for_ssh "$vm_ip" || {
|
||||
log_error "Failed to connect to VM. Please check:"
|
||||
log_error " 1. VM is booted: qm status $TEMP_VMID"
|
||||
log_error " 2. IP address is correct: $vm_ip"
|
||||
log_error " 3. SSH key is correct: $SSH_KEY"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 4: Apply improvements
|
||||
log_step "Step 4: Applying Template Improvements"
|
||||
|
||||
log_info "Installing essential packages and QEMU Guest Agent..."
|
||||
ssh $SSH_OPTS "${VM_USER}@${vm_ip}" <<'EOF'
|
||||
set -e
|
||||
sudo apt-get update -qq
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq
|
||||
|
||||
# Install essential packages
|
||||
sudo apt-get install -y \
|
||||
jq \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
vim \
|
||||
nano \
|
||||
net-tools \
|
||||
htop \
|
||||
unattended-upgrades \
|
||||
apt-transport-https \
|
||||
ca-certificates \
|
||||
qemu-guest-agent \
|
||||
ufw
|
||||
|
||||
# Enable and start QEMU Guest Agent
|
||||
sudo systemctl enable qemu-guest-agent
|
||||
sudo systemctl start qemu-guest-agent
|
||||
|
||||
# Configure automatic security updates
|
||||
echo 'Unattended-Upgrade::Automatic-Reboot "false";' | sudo tee -a /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null
|
||||
echo 'Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";' | sudo tee -a /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null
|
||||
|
||||
# Set timezone
|
||||
sudo timedatectl set-timezone UTC
|
||||
|
||||
# Configure locale
|
||||
sudo locale-gen en_US.UTF-8
|
||||
sudo update-locale LANG=en_US.UTF-8
|
||||
|
||||
# SSH hardening (disable root login, password auth)
|
||||
sudo sed -i 's/#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||
sudo sed -i 's/#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||
sudo sed -i 's/#PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
|
||||
sudo systemctl restart sshd
|
||||
|
||||
# Install UFW (firewall) but don't enable it - let VMs configure as needed
|
||||
# UFW is installed but not enabled, so VMs can configure firewall rules per their needs
|
||||
|
||||
# Clean up disk
|
||||
sudo apt-get autoremove -y -qq
|
||||
sudo apt-get autoclean -qq
|
||||
sudo rm -rf /tmp/*
|
||||
sudo rm -rf /var/tmp/*
|
||||
sudo truncate -s 0 /var/log/*.log 2>/dev/null || true
|
||||
sudo journalctl --vacuum-time=1d --quiet
|
||||
|
||||
# Create template version file
|
||||
echo "template-9000-v1.1.0-$(date +%Y%m%d)" | sudo tee /etc/template-version > /dev/null
|
||||
|
||||
echo "✓ All improvements applied"
|
||||
EOF
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Failed to apply improvements"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "✓ All improvements applied successfully"
|
||||
|
||||
# Step 5: Stop VM and convert to template
|
||||
log_step "Step 5: Converting Back to Template"
|
||||
log_info "Stopping VM $TEMP_VMID..."
|
||||
run_proxmox_cmd "qm stop $TEMP_VMID"
|
||||
sleep 5
|
||||
|
||||
log_info "Converting VM $TEMP_VMID to template..."
|
||||
run_proxmox_cmd "qm template $TEMP_VMID"
|
||||
log_info "✓ VM converted to template"
|
||||
|
||||
# Step 6: Replace original template
|
||||
log_step "Step 6: Replacing Original Template"
|
||||
log_warn "This will destroy the original template 9000 and replace it with the improved version"
|
||||
echo ""
|
||||
|
||||
if [ -t 0 ]; then
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
log_info "Cancelled. Improved template is available as VM $TEMP_VMID"
|
||||
log_info "You can manually:"
|
||||
log_info " 1. Destroy template 9000: qm destroy 9000"
|
||||
log_info " 2. Change VMID: qm set $TEMP_VMID --vmid 9000"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
log_info "Non-interactive mode: auto-confirming"
|
||||
fi
|
||||
|
||||
log_info "Destroying original template 9000..."
|
||||
run_proxmox_cmd "qm destroy $TEMPLATE_VMID --purge" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
log_info "Changing VMID from $TEMP_VMID to $TEMPLATE_VMID..."
|
||||
run_proxmox_cmd "qm set $TEMP_VMID --vmid $TEMPLATE_VMID"
|
||||
|
||||
log_step "Template Improvement Complete!"
|
||||
log_info "✓ Template 9000 has been improved with:"
|
||||
log_info " - QEMU Guest Agent pre-installed and enabled"
|
||||
log_info " - Essential utilities (jq, curl, wget, git, vim, nano, htop, net-tools, etc.)"
|
||||
log_info " - Automatic security updates configured (unattended-upgrades)"
|
||||
log_info " - Timezone set to UTC"
|
||||
log_info " - Locale configured (en_US.UTF-8)"
|
||||
log_info " - SSH hardened (no root login, no password auth, pubkey only)"
|
||||
log_info " - UFW firewall installed (not enabled - VMs configure as needed)"
|
||||
log_info " - Disk optimized and cleaned"
|
||||
log_info " - Template version tracking (/etc/template-version)"
|
||||
log_info ""
|
||||
log_info "You can now clone VMs from template 9000 and they will have all these improvements!"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
117
scripts/infrastructure/install-qemu-guest-agent.sh
Executable file
117
scripts/infrastructure/install-qemu-guest-agent.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Install QEMU Guest Agent in All VMs
|
||||
# Uses guest-agent IP discovery (with fallback for bootstrap)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Load environment variables
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
VM_USER="${VM_USER:-ubuntu}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
|
||||
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
|
||||
|
||||
# VMID NAME (no IP - discovered via guest agent)
|
||||
VMS=(
|
||||
"100 cloudflare-tunnel"
|
||||
"101 k3s-master"
|
||||
"102 git-server"
|
||||
"103 observability"
|
||||
)
|
||||
|
||||
# Fallback IPs for bootstrap (when guest agent not yet installed)
|
||||
# Format: VMID:IP
|
||||
declare -A FALLBACK_IPS=(
|
||||
["100"]="192.168.1.60"
|
||||
["101"]="192.168.1.188"
|
||||
["102"]="192.168.1.121"
|
||||
["103"]="192.168.1.82"
|
||||
)
|
||||
|
||||
# Import helper library
|
||||
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
|
||||
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
|
||||
else
|
||||
log_error "Helper library not found. Run this script on Proxmox host or via SSH."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
main() {
|
||||
log_info "Installing QEMU Guest Agent in all VMs"
|
||||
echo ""
|
||||
|
||||
for vm_spec in "${VMS[@]}"; do
|
||||
read -r vmid name <<< "$vm_spec"
|
||||
echo "=== VM $vmid: $name ==="
|
||||
|
||||
# Make sure agent is enabled in Proxmox VM config
|
||||
ensure_guest_agent_enabled "$vmid" || true
|
||||
|
||||
# Get IP - try guest agent first, fallback to hardcoded for bootstrap
|
||||
local ip
|
||||
ip="$(get_vm_ip_or_fallback "$vmid" "$name" "${FALLBACK_IPS[$vmid]:-}" || true)"
|
||||
|
||||
if [[ -z "$ip" ]]; then
|
||||
log_warn "Skipping: no IP available for VM $vmid ($name)"
|
||||
echo
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " Using IP: $ip – installing qemu-guest-agent inside guest (idempotent)..."
|
||||
|
||||
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 "${VM_USER}@${ip}" <<'EOF'
|
||||
set -e
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y qemu-guest-agent > /dev/null 2>&1
|
||||
sudo systemctl enable --now qemu-guest-agent
|
||||
systemctl is-active qemu-guest-agent && echo "✓ QEMU Guest Agent is running"
|
||||
EOF
|
||||
then
|
||||
log_info " ✓ QEMU Guest Agent installed and started"
|
||||
|
||||
# Wait a moment for agent to be ready, then verify
|
||||
sleep 3
|
||||
local discovered_ip
|
||||
discovered_ip="$(get_vm_ip_from_guest_agent "$vmid" || true)"
|
||||
if [[ -n "$discovered_ip" ]]; then
|
||||
log_info " ✓ Guest agent IP discovery working: $discovered_ip"
|
||||
fi
|
||||
else
|
||||
log_error " ✗ Failed to install QEMU Guest Agent"
|
||||
fi
|
||||
|
||||
echo
|
||||
done
|
||||
|
||||
log_info "Done. All VMs should now support guest-agent IP discovery."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
354
scripts/infrastructure/recreate-vms-from-template.sh
Executable file
354
scripts/infrastructure/recreate-vms-from-template.sh
Executable file
@@ -0,0 +1,354 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Destroy Existing VMs and Recreate from Ubuntu Cloud-Init Template
|
||||
# This script creates a template from Ubuntu Cloud Image and recreates all VMs
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_header() {
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
echo -e "${CYAN}$1${NC}"
|
||||
echo -e "${CYAN}========================================${NC}"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
|
||||
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
|
||||
PROXMOX_URL="https://192.168.1.206:8006"
|
||||
PROXMOX_NODE="pve"
|
||||
STORAGE="${STORAGE:-local-lvm}"
|
||||
TEMPLATE_ID=9000
|
||||
TEMPLATE_NAME="ubuntu-24.04-cloudinit"
|
||||
|
||||
# VM Configuration
|
||||
declare -A VMS=(
|
||||
[100]="cloudflare-tunnel:2:4096:40G:192.168.1.60:192.168.1.1"
|
||||
[101]="k3s-master:4:8192:80G:192.168.1.188:192.168.1.1"
|
||||
[102]="git-server:2:4096:100G:192.168.1.121:192.168.1.1"
|
||||
[103]="observability:4:8192:200G:192.168.1.82:192.168.1.1"
|
||||
)
|
||||
|
||||
# Get authentication ticket
|
||||
get_ticket() {
|
||||
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
|
||||
"$PROXMOX_URL/api2/json/access/ticket")
|
||||
|
||||
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
|
||||
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
|
||||
log_error "Failed to authenticate with Proxmox"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$ticket|$csrf"
|
||||
}
|
||||
|
||||
# Check if template exists
|
||||
template_exists() {
|
||||
local auth=$1
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
local response=$(curl -k -s \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID/config" 2>&1)
|
||||
|
||||
if echo "$response" | grep -q '"name"'; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Download Ubuntu Cloud Image
|
||||
download_cloud_image() {
|
||||
local image_url="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
local image_file="/tmp/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
|
||||
log_step "Downloading Ubuntu 24.04 Cloud Image..."
|
||||
|
||||
if [ -f "$image_file" ]; then
|
||||
log_info "Cloud image already exists: $image_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Downloading from: $image_url"
|
||||
log_warn "This may take several minutes (image is ~2GB)..."
|
||||
|
||||
if command -v wget &> /dev/null; then
|
||||
wget --progress=bar:force -O "$image_file" "$image_url" || return 1
|
||||
elif command -v curl &> /dev/null; then
|
||||
curl -L --progress-bar -o "$image_file" "$image_url" || return 1
|
||||
else
|
||||
log_error "Neither wget nor curl found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "✓ Cloud image downloaded"
|
||||
echo "$image_file"
|
||||
}
|
||||
|
||||
# Create template from cloud image
|
||||
create_template() {
|
||||
local auth=$1
|
||||
local image_file=$2
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
log_step "Creating template from cloud image..."
|
||||
|
||||
# Check if template already exists
|
||||
if template_exists "$auth"; then
|
||||
log_info "Template $TEMPLATE_ID already exists, skipping creation"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "Template creation requires manual steps in Proxmox Web UI:"
|
||||
echo ""
|
||||
log_info "1. Upload cloud image to Proxmox:"
|
||||
log_info " - Go to: Datacenter → $PROXMOX_NODE → Storage → local"
|
||||
log_info " - Click 'Upload' → Select: $image_file"
|
||||
log_info " - Wait for upload to complete"
|
||||
echo ""
|
||||
log_info "2. Create VM from image:"
|
||||
log_info " - Create VM (ID: $TEMPLATE_ID)"
|
||||
log_info " - Import disk from uploaded image"
|
||||
log_info " - Set CPU: 2, Memory: 2048MB"
|
||||
log_info " - Add network device"
|
||||
log_info " - Enable Cloud-Init in Options"
|
||||
log_info " - Convert to template"
|
||||
echo ""
|
||||
log_warn "After template is created, press Enter to continue..."
|
||||
read -p "Press Enter when template is ready..."
|
||||
}
|
||||
|
||||
# Destroy existing VM
|
||||
destroy_vm() {
|
||||
local auth=$1
|
||||
local vmid=$2
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
log_step "Destroying VM $vmid..."
|
||||
|
||||
# Stop VM if running
|
||||
curl -k -s -X POST \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/stop" > /dev/null 2>&1
|
||||
|
||||
sleep 2
|
||||
|
||||
# Delete VM
|
||||
local response=$(curl -k -s -X DELETE \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid" 2>&1)
|
||||
|
||||
if echo "$response" | grep -q '"errors"'; then
|
||||
log_error "Failed to destroy VM: $response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "✓ VM $vmid destroyed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Create VM from template
|
||||
create_vm_from_template() {
|
||||
local auth=$1
|
||||
local vmid=$2
|
||||
local name=$3
|
||||
local cores=$4
|
||||
local memory=$5
|
||||
local disk_size=$6
|
||||
local ip_address=$7
|
||||
local gateway=$8
|
||||
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
log_step "Creating VM $vmid: $name from template..."
|
||||
|
||||
# Clone template
|
||||
local clone_response=$(curl -k -s -X POST \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
-d "newid=$vmid" \
|
||||
-d "name=$name" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID/clone" 2>&1)
|
||||
|
||||
if echo "$clone_response" | grep -q '"errors"'; then
|
||||
log_error "Failed to clone template: $clone_response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Template cloned, waiting for completion..."
|
||||
sleep 5
|
||||
|
||||
# Configure VM
|
||||
log_info "Configuring VM..."
|
||||
|
||||
# Set CPU, memory, disk
|
||||
curl -k -s -X PUT \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
-d "cores=$cores" \
|
||||
-d "memory=$memory" \
|
||||
-d "net0=virtio,bridge=vmbr0" \
|
||||
-d "agent=1" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
|
||||
|
||||
# Resize disk if needed
|
||||
local current_disk=$(curl -k -s \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" | \
|
||||
grep -o '"scsi0":"[^"]*' | cut -d'"' -f4 | cut -d',' -f2 | cut -d'=' -f2)
|
||||
|
||||
if [ "$current_disk" != "$disk_size" ]; then
|
||||
log_info "Resizing disk to $disk_size..."
|
||||
curl -k -s -X PUT \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
-d "scsi0=${STORAGE}:${disk_size}" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/resize" > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Configure Cloud-Init
|
||||
log_info "Configuring Cloud-Init..."
|
||||
curl -k -s -X PUT \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
-d "ipconfig0=ip=${ip_address}/24,gw=${gateway}" \
|
||||
-d "ciuser=ubuntu" \
|
||||
-d "cipassword=" \
|
||||
-d "sshkeys=$(cat ~/.ssh/id_rsa.pub 2>/dev/null | base64 -w 0 || echo '')" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
|
||||
|
||||
log_info "✓ VM $vmid created and configured"
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
log_header "Recreate VMs from Cloud-Init Template"
|
||||
echo ""
|
||||
|
||||
if [ -z "$PVE_PASSWORD" ]; then
|
||||
log_error "PVE_ROOT_PASS not set in .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_warn "This will DESTROY existing VMs (100, 101, 102, 103)"
|
||||
log_warn "And recreate them from a Cloud-Init template"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
log_info "Cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Authenticate
|
||||
auth=$(get_ticket)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 1: Download cloud image
|
||||
image_file=$(download_cloud_image)
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Failed to download cloud image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 2: Create template (manual steps required)
|
||||
create_template "$auth" "$image_file"
|
||||
|
||||
# Verify template exists
|
||||
if ! template_exists "$auth"; then
|
||||
log_error "Template does not exist. Please create it first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Destroy existing VMs
|
||||
log_header "Destroying Existing VMs"
|
||||
for vmid in 100 101 102 103; do
|
||||
destroy_vm "$auth" "$vmid" || log_warn "Failed to destroy VM $vmid"
|
||||
done
|
||||
|
||||
sleep 3
|
||||
|
||||
# Step 4: Create VMs from template
|
||||
log_header "Creating VMs from Template"
|
||||
for vmid in 100 101 102 103; do
|
||||
IFS=':' read -r name cores memory disk_size ip_address gateway <<< "${VMS[$vmid]}"
|
||||
|
||||
if create_vm_from_template "$auth" "$vmid" "$name" "$cores" "$memory" "$disk_size" "$ip_address" "$gateway"; then
|
||||
log_info "✓ VM $vmid created"
|
||||
else
|
||||
log_error "✗ Failed to create VM $vmid"
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
# Step 5: Start VMs
|
||||
log_header "Starting VMs"
|
||||
for vmid in 100 101 102 103; do
|
||||
log_info "Starting VM $vmid..."
|
||||
ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
curl -k -s -X POST \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start" > /dev/null 2>&1
|
||||
|
||||
log_info "✓ VM $vmid started"
|
||||
done
|
||||
|
||||
log_header "VM Recreation Complete!"
|
||||
echo ""
|
||||
log_info "VMs are being created from template with Cloud-Init"
|
||||
log_info "They will boot automatically and configure themselves"
|
||||
log_info "No manual installation required!"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo " 1. Wait 2-3 minutes for VMs to boot"
|
||||
echo " 2. Check readiness: ./scripts/check-vm-readiness.sh"
|
||||
echo " 3. Run tasks: ./scripts/complete-all-vm-tasks.sh"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
164
scripts/infrastructure/setup-cloudflare-tunnel.sh
Executable file
164
scripts/infrastructure/setup-cloudflare-tunnel.sh
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Complete Cloudflare Tunnel Setup Script
|
||||
# Run this on the Cloudflare Tunnel VM after OS installation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Please run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Step 1: Installing cloudflared..."
|
||||
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
|
||||
chmod +x /usr/local/bin/cloudflared
|
||||
cloudflared --version
|
||||
log_info "cloudflared installed successfully"
|
||||
|
||||
log_step "Step 2: Creating cloudflared user..."
|
||||
useradd -r -s /bin/false cloudflared || log_warn "User cloudflared may already exist"
|
||||
mkdir -p /etc/cloudflared
|
||||
chown cloudflared:cloudflared /etc/cloudflared
|
||||
|
||||
log_step "Step 3: Authenticating cloudflared..."
|
||||
log_warn "You need to authenticate cloudflared manually:"
|
||||
echo ""
|
||||
echo "Run this command:"
|
||||
echo " cloudflared tunnel login"
|
||||
echo ""
|
||||
echo "This will open a browser for authentication."
|
||||
echo "After authentication, press Enter to continue..."
|
||||
read -p "Press Enter after completing authentication..."
|
||||
|
||||
log_step "Step 4: Creating tunnel..."
|
||||
log_warn "Creating tunnel 'azure-stack-hci'..."
|
||||
log_warn "If tunnel already exists, you can skip this step."
|
||||
read -p "Create new tunnel? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
cloudflared tunnel create azure-stack-hci || log_warn "Tunnel may already exist"
|
||||
fi
|
||||
|
||||
# Get tunnel ID
|
||||
TUNNEL_ID=$(cloudflared tunnel list | grep azure-stack-hci | awk '{print $1}' | head -1)
|
||||
if [ -z "$TUNNEL_ID" ]; then
|
||||
log_error "Could not find tunnel ID. Please create tunnel manually."
|
||||
exit 1
|
||||
fi
|
||||
log_info "Tunnel ID: $TUNNEL_ID"
|
||||
|
||||
log_step "Step 5: Creating tunnel configuration..."
|
||||
cat > /etc/cloudflared/config.yml <<EOF
|
||||
tunnel: $TUNNEL_ID
|
||||
credentials-file: /etc/cloudflared/$TUNNEL_ID.json
|
||||
|
||||
ingress:
|
||||
# Proxmox UI
|
||||
- hostname: proxmox.yourdomain.com
|
||||
service: https://192.168.1.206:8006
|
||||
originRequest:
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 30
|
||||
connectTimeout: 30s
|
||||
|
||||
# Proxmox R630
|
||||
- hostname: proxmox-r630.yourdomain.com
|
||||
service: https://192.168.1.49:8006
|
||||
originRequest:
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 30
|
||||
connectTimeout: 30s
|
||||
|
||||
# Grafana Dashboard
|
||||
- hostname: grafana.yourdomain.com
|
||||
service: http://192.168.1.82:3000
|
||||
originRequest:
|
||||
connectTimeout: 30s
|
||||
|
||||
# Git Server
|
||||
- hostname: git.yourdomain.com
|
||||
service: https://192.168.1.121:443
|
||||
originRequest:
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 30
|
||||
connectTimeout: 30s
|
||||
|
||||
# K3s Dashboard (if exposed)
|
||||
- hostname: k3s.yourdomain.com
|
||||
service: https://192.168.1.188:6443
|
||||
originRequest:
|
||||
noHappyEyeballs: true
|
||||
tcpKeepAlive: 30
|
||||
connectTimeout: 30s
|
||||
|
||||
# Catch-all (must be last)
|
||||
- service: http_status:404
|
||||
EOF
|
||||
|
||||
chmod 600 /etc/cloudflared/config.yml
|
||||
chown cloudflared:cloudflared /etc/cloudflared/config.yml
|
||||
log_info "Configuration file created: /etc/cloudflared/config.yml"
|
||||
log_warn "Update hostnames in config.yml to match your domain!"
|
||||
|
||||
log_step "Step 6: Creating systemd service..."
|
||||
cat > /etc/systemd/system/cloudflared.service <<EOF
|
||||
[Unit]
|
||||
Description=Cloudflare Tunnel
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=cloudflared
|
||||
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
log_step "Step 7: Enabling and starting service..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable cloudflared
|
||||
systemctl start cloudflared
|
||||
sleep 2
|
||||
systemctl status cloudflared --no-pager
|
||||
|
||||
log_info "========================================="
|
||||
log_info "Cloudflare Tunnel Setup Complete!"
|
||||
log_info "========================================="
|
||||
echo ""
|
||||
log_warn "Next steps:"
|
||||
echo " 1. Update /etc/cloudflared/config.yml with your actual domain"
|
||||
echo " 2. Configure DNS records in Cloudflare Dashboard"
|
||||
echo " 3. Set up Zero Trust policies in Cloudflare Dashboard"
|
||||
echo " 4. Test tunnel connectivity: cloudflared tunnel info azure-stack-hci"
|
||||
echo ""
|
||||
log_info "Tunnel status: systemctl status cloudflared"
|
||||
log_info "Tunnel logs: journalctl -u cloudflared -f"
|
||||
|
||||
119
scripts/infrastructure/setup-git-server.sh
Executable file
119
scripts/infrastructure/setup-git-server.sh
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Git Server Setup Script (Gitea)
|
||||
# Run this on the Git Server VM after OS installation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Please run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITEA_VERSION="${GITEA_VERSION:-1.21.0}"
|
||||
GITEA_USER="${GITEA_USER:-git}"
|
||||
GITEA_HOME="${GITEA_HOME:-/var/lib/gitea}"
|
||||
GITEA_DOMAIN="${GITEA_DOMAIN:-192.168.1.121}"
|
||||
GITEA_PORT="${GITEA_PORT:-3000}"
|
||||
|
||||
log_step "Step 1: Installing dependencies..."
|
||||
apt-get update
|
||||
apt-get install -y git sqlite3
|
||||
|
||||
log_step "Step 2: Creating Gitea user..."
|
||||
useradd -r -s /bin/bash -m -d "$GITEA_HOME" "$GITEA_USER" || log_warn "User $GITEA_USER may already exist"
|
||||
|
||||
log_step "Step 3: Downloading and installing Gitea..."
|
||||
mkdir -p "$GITEA_HOME"
|
||||
cd "$GITEA_HOME"
|
||||
wget -O gitea "https://dl.gitea.io/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64"
|
||||
chmod +x gitea
|
||||
chown -R "$GITEA_USER:$GITEA_USER" "$GITEA_HOME"
|
||||
|
||||
log_step "Step 4: Creating systemd service..."
|
||||
cat > /etc/systemd/system/gitea.service <<EOF
|
||||
[Unit]
|
||||
Description=Gitea (Git with a cup of tea)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$GITEA_USER
|
||||
Group=$GITEA_USER
|
||||
WorkingDirectory=$GITEA_HOME
|
||||
ExecStart=$GITEA_HOME/gitea web --config $GITEA_HOME/custom/conf/app.ini
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
log_step "Step 5: Creating initial configuration..."
|
||||
mkdir -p "$GITEA_HOME/custom/conf"
|
||||
cat > "$GITEA_HOME/custom/conf/app.ini" <<EOF
|
||||
[server]
|
||||
DOMAIN = $GITEA_DOMAIN
|
||||
HTTP_PORT = $GITEA_PORT
|
||||
ROOT_URL = http://$GITEA_DOMAIN:$GITEA_PORT/
|
||||
DISABLE_SSH = false
|
||||
SSH_PORT = 22
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = $GITEA_HOME/data/gitea.db
|
||||
|
||||
[repository]
|
||||
ROOT = $GITEA_HOME/gitea-repositories
|
||||
|
||||
[log]
|
||||
MODE = file
|
||||
LEVEL = Info
|
||||
EOF
|
||||
|
||||
chown -R "$GITEA_USER:$GITEA_USER" "$GITEA_HOME"
|
||||
|
||||
log_step "Step 6: Enabling and starting Gitea..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable gitea
|
||||
systemctl start gitea
|
||||
sleep 3
|
||||
systemctl status gitea --no-pager
|
||||
|
||||
log_info "========================================="
|
||||
log_info "Gitea Installation Complete!"
|
||||
log_info "========================================="
|
||||
echo ""
|
||||
log_info "Access Gitea at: http://$GITEA_DOMAIN:$GITEA_PORT"
|
||||
log_info "Initial setup will be required on first access"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo " 1. Complete initial Gitea setup via web UI"
|
||||
echo " 2. Create GitOps repository"
|
||||
echo " 3. Configure SSH keys for Git access"
|
||||
echo " 4. Set up Flux GitOps (if using)"
|
||||
|
||||
261
scripts/infrastructure/setup-guest-agent.sh
Executable file
261
scripts/infrastructure/setup-guest-agent.sh
Executable file
@@ -0,0 +1,261 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Install and Configure QEMU Guest Agent on All VMs
|
||||
# This script installs qemu-guest-agent on Ubuntu VMs and enables it in Proxmox
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
|
||||
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
|
||||
PROXMOX_URL="https://192.168.1.206:8006"
|
||||
PROXMOX_NODE="pve"
|
||||
|
||||
# VM Configuration
|
||||
declare -A VMS=(
|
||||
[100]="cloudflare-tunnel:192.168.1.60"
|
||||
[101]="k3s-master:192.168.1.188"
|
||||
[102]="git-server:192.168.1.121"
|
||||
[103]="observability:192.168.1.82"
|
||||
)
|
||||
|
||||
SSH_USER="${SSH_USER:-ubuntu}"
|
||||
SSH_KEY="${SSH_KEY:-~/.ssh/id_rsa}"
|
||||
|
||||
# Get authentication ticket
|
||||
get_ticket() {
|
||||
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
|
||||
"$PROXMOX_URL/api2/json/access/ticket")
|
||||
|
||||
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
|
||||
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
|
||||
log_error "Failed to authenticate with Proxmox"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$ticket|$csrf"
|
||||
}
|
||||
|
||||
# Check if VM is reachable
|
||||
check_vm_reachable() {
|
||||
local ip=$1
|
||||
if ping -c 1 -W 2 "$ip" > /dev/null 2>&1; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check SSH connectivity
|
||||
check_ssh() {
|
||||
local ip=$1
|
||||
local user=$2
|
||||
|
||||
if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -i "$SSH_KEY" "${user}@${ip}" "echo 'SSH OK'" > /dev/null 2>&1; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Install guest agent on VM
|
||||
install_guest_agent_on_vm() {
|
||||
local vmid=$1
|
||||
local name=$2
|
||||
local ip=$3
|
||||
|
||||
log_step "Installing QEMU Guest Agent on VM $vmid: $name"
|
||||
|
||||
# Check if VM is reachable
|
||||
if ! check_vm_reachable "$ip"; then
|
||||
log_error "VM at $ip is not reachable, skipping..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check SSH
|
||||
if ! check_ssh "$ip" "$SSH_USER"; then
|
||||
log_error "SSH not available on $ip, skipping..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Installing qemu-guest-agent via SSH..."
|
||||
|
||||
# Install qemu-guest-agent
|
||||
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY" "${SSH_USER}@${ip}" <<EOF
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y qemu-guest-agent
|
||||
sudo systemctl enable qemu-guest-agent
|
||||
sudo systemctl start qemu-guest-agent
|
||||
sudo systemctl status qemu-guest-agent --no-pager | head -5
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "✓ Guest agent installed and started on VM $vmid"
|
||||
return 0
|
||||
else
|
||||
log_error "✗ Failed to install guest agent on VM $vmid"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Enable guest agent in Proxmox VM configuration
|
||||
enable_guest_agent_in_proxmox() {
|
||||
local auth=$1
|
||||
local vmid=$2
|
||||
local name=$3
|
||||
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
log_step "Enabling guest agent in Proxmox for VM $vmid: $name"
|
||||
|
||||
# Enable agent in VM config
|
||||
local response=$(curl -k -s -X PUT \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
-d "agent=1" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
|
||||
|
||||
if echo "$response" | grep -q '"errors"'; then
|
||||
log_error "Failed to enable agent: $response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "✓ Guest agent enabled in Proxmox for VM $vmid"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Verify guest agent is working
|
||||
verify_guest_agent() {
|
||||
local auth=$1
|
||||
local vmid=$2
|
||||
local name=$3
|
||||
|
||||
local ticket=$(echo "$auth" | cut -d'|' -f1)
|
||||
local csrf=$(echo "$auth" | cut -d'|' -f2)
|
||||
|
||||
log_step "Verifying guest agent for VM $vmid: $name"
|
||||
|
||||
# Check agent status via Proxmox API
|
||||
local response=$(curl -k -s \
|
||||
-H "Cookie: PVEAuthCookie=$ticket" \
|
||||
-H "CSRFPreventionToken: $csrf" \
|
||||
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/agent/get-fsinfo" 2>&1)
|
||||
|
||||
if echo "$response" | grep -q '"result"'; then
|
||||
log_info "✓ Guest agent is responding"
|
||||
return 0
|
||||
else
|
||||
log_warn "⚠ Guest agent may not be fully ready yet"
|
||||
log_info " This is normal if VM was just configured"
|
||||
log_info " Agent may take a few minutes to initialize"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
echo "========================================="
|
||||
echo "Setup QEMU Guest Agent on All VMs"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
if [ -z "$PVE_PASSWORD" ]; then
|
||||
log_error "PVE_ROOT_PASS not set in .env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SSH_KEY" ]; then
|
||||
log_error "SSH key not found: $SSH_KEY"
|
||||
log_info "Set SSH_KEY environment variable or create key pair"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Using SSH key: $SSH_KEY"
|
||||
log_info "SSH user: $SSH_USER"
|
||||
echo ""
|
||||
|
||||
# Authenticate with Proxmox
|
||||
auth=$(get_ticket)
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Process each VM
|
||||
for vmid in 100 101 102 103; do
|
||||
IFS=':' read -r name ip <<< "${VMS[$vmid]}"
|
||||
|
||||
echo "----------------------------------------"
|
||||
log_step "Processing VM $vmid: $name"
|
||||
echo ""
|
||||
|
||||
# Step 1: Install guest agent on VM
|
||||
if install_guest_agent_on_vm "$vmid" "$name" "$ip"; then
|
||||
log_info "✓ Guest agent installed on VM"
|
||||
else
|
||||
log_error "✗ Failed to install guest agent"
|
||||
echo ""
|
||||
continue
|
||||
fi
|
||||
|
||||
# Step 2: Enable agent in Proxmox
|
||||
if enable_guest_agent_in_proxmox "$auth" "$vmid" "$name"; then
|
||||
log_info "✓ Agent enabled in Proxmox"
|
||||
else
|
||||
log_error "✗ Failed to enable agent in Proxmox"
|
||||
fi
|
||||
|
||||
# Step 3: Verify (optional, may take time)
|
||||
sleep 2
|
||||
verify_guest_agent "$auth" "$vmid" "$name" || true
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
log_info "========================================="
|
||||
log_info "Guest Agent Setup Complete"
|
||||
log_info "========================================="
|
||||
echo ""
|
||||
log_info "Benefits of QEMU Guest Agent:"
|
||||
echo " • Proper VM shutdown/reboot from Proxmox"
|
||||
echo " • Automatic IP address detection"
|
||||
echo " • Better VM status reporting"
|
||||
echo " • File system information"
|
||||
echo ""
|
||||
log_warn "Note: Guest agent may take a few minutes to fully initialize"
|
||||
log_info "You can verify in Proxmox Web UI:"
|
||||
echo " VM → Monitor → QEMU Guest Agent"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
83
scripts/infrastructure/setup-k3s.sh
Executable file
83
scripts/infrastructure/setup-k3s.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# K3s Installation Script
|
||||
# Run this on the K3s VM after OS installation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Please run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Step 1: Installing K3s..."
|
||||
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644" sh -
|
||||
|
||||
log_step "Step 2: Verifying K3s installation..."
|
||||
systemctl status k3s --no-pager || log_error "K3s service not running"
|
||||
|
||||
log_step "Step 3: Waiting for K3s to be ready..."
|
||||
sleep 10
|
||||
kubectl get nodes || log_warn "K3s may still be initializing"
|
||||
|
||||
log_step "Step 4: Installing kubectl (if not present)..."
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
chmod +x kubectl
|
||||
mv kubectl /usr/local/bin/
|
||||
fi
|
||||
|
||||
log_step "Step 5: Configuring kubectl..."
|
||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||
mkdir -p ~/.kube
|
||||
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
|
||||
chmod 600 ~/.kube/config
|
||||
|
||||
log_step "Step 6: Verifying cluster..."
|
||||
kubectl cluster-info
|
||||
kubectl get nodes
|
||||
|
||||
log_info "========================================="
|
||||
log_info "K3s Installation Complete!"
|
||||
log_info "========================================="
|
||||
echo ""
|
||||
log_info "K3s is ready to use!"
|
||||
echo ""
|
||||
log_info "Useful commands:"
|
||||
echo " kubectl get nodes"
|
||||
echo " kubectl get pods --all-namespaces"
|
||||
echo " kubectl cluster-info"
|
||||
echo ""
|
||||
log_warn "Next steps:"
|
||||
echo " 1. Create namespaces: kubectl create namespace blockchain"
|
||||
echo " 2. Deploy ingress controller"
|
||||
echo " 3. Deploy cert-manager"
|
||||
echo " 4. Deploy HC Stack services"
|
||||
echo ""
|
||||
log_info "Kubeconfig location: /etc/rancher/k3s/k3s.yaml"
|
||||
log_info "Copy this file to access cluster remotely"
|
||||
|
||||
146
scripts/infrastructure/setup-observability.sh
Executable file
146
scripts/infrastructure/setup-observability.sh
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Observability Stack Setup Script (Prometheus + Grafana)
|
||||
# Run this on the Observability VM after OS installation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "Please run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROMETHEUS_VERSION="${PROMETHEUS_VERSION:-2.45.0}"
|
||||
GRAFANA_VERSION="${GRAFANA_VERSION:-10.0.0}"
|
||||
PROMETHEUS_USER="${PROMETHEUS_USER:-prometheus}"
|
||||
GRAFANA_USER="${GRAFANA_USER:-grafana}"
|
||||
|
||||
log_step "Step 1: Installing dependencies..."
|
||||
apt-get update
|
||||
apt-get install -y wget curl
|
||||
|
||||
log_step "Step 2: Installing Prometheus..."
|
||||
|
||||
# Create Prometheus user
|
||||
useradd -r -s /bin/false "$PROMETHEUS_USER" || log_warn "User $PROMETHEUS_USER may already exist"
|
||||
|
||||
# Download and install Prometheus
|
||||
cd /tmp
|
||||
wget "https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
|
||||
tar xzf "prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
|
||||
mv "prometheus-${PROMETHEUS_VERSION}.linux-amd64" /opt/prometheus
|
||||
mkdir -p /etc/prometheus
|
||||
mkdir -p /var/lib/prometheus
|
||||
chown -R "$PROMETHEUS_USER:$PROMETHEUS_USER" /opt/prometheus /etc/prometheus /var/lib/prometheus
|
||||
|
||||
# Create Prometheus configuration
|
||||
cat > /etc/prometheus/prometheus.yml <<EOF
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
- targets: ['localhost:9100']
|
||||
EOF
|
||||
|
||||
# Create systemd service
|
||||
cat > /etc/systemd/system/prometheus.service <<EOF
|
||||
[Unit]
|
||||
Description=Prometheus
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$PROMETHEUS_USER
|
||||
ExecStart=/opt/prometheus/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
log_step "Step 3: Installing Node Exporter..."
|
||||
cd /tmp
|
||||
wget "https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz"
|
||||
tar xzf node_exporter-1.6.1.linux-amd64.tar.gz
|
||||
mv node_exporter-1.6.1.linux-amd64/node_exporter /usr/local/bin/
|
||||
chmod +x /usr/local/bin/node_exporter
|
||||
|
||||
cat > /etc/systemd/system/node-exporter.service <<EOF
|
||||
[Unit]
|
||||
Description=Node Exporter
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/node_exporter
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
log_step "Step 4: Installing Grafana..."
|
||||
apt-get install -y apt-transport-https software-properties-common
|
||||
wget -q -O - https://packages.grafana.com/gpg.key | apt-key add -
|
||||
echo "deb https://packages.grafana.com/oss/deb stable main" > /etc/apt/sources.list.d/grafana.list
|
||||
apt-get update
|
||||
apt-get install -y grafana
|
||||
|
||||
log_step "Step 5: Starting services..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable prometheus node-exporter grafana-server
|
||||
systemctl start prometheus node-exporter grafana-server
|
||||
sleep 3
|
||||
|
||||
log_info "========================================="
|
||||
log_info "Observability Stack Installation Complete!"
|
||||
log_info "========================================="
|
||||
echo ""
|
||||
log_info "Services:"
|
||||
echo " - Prometheus: http://192.168.1.82:9090"
|
||||
echo " - Grafana: http://192.168.1.82:3000"
|
||||
echo " - Node Exporter: http://192.168.1.82:9100"
|
||||
echo ""
|
||||
log_info "Grafana default credentials:"
|
||||
echo " Username: admin"
|
||||
echo " Password: admin (change on first login)"
|
||||
echo ""
|
||||
log_info "Next steps:"
|
||||
echo " 1. Access Grafana and change default password"
|
||||
echo " 2. Add Prometheus as data source (http://localhost:9090)"
|
||||
echo " 3. Import dashboards from grafana.com/dashboards"
|
||||
echo " 4. Configure alerting rules"
|
||||
|
||||
118
scripts/infrastructure/verify-proxmox-image.sh
Executable file
118
scripts/infrastructure/verify-proxmox-image.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/bin/bash
|
||||
source ~/.bashrc
|
||||
# Verify Proxmox Cloud Image Integrity
|
||||
# Usage: ./scripts/verify-proxmox-image.sh [proxmox-host] [image-path]
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a
|
||||
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
|
||||
set +a
|
||||
fi
|
||||
|
||||
PROXMOX_HOST="${1:-${PROXMOX_ML110_URL#https://}}"
|
||||
PROXMOX_HOST="${PROXMOX_HOST%%:*}"
|
||||
IMAGE_PATH="${2:-/var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img}"
|
||||
|
||||
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
|
||||
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
|
||||
|
||||
main() {
|
||||
echo "========================================="
|
||||
echo "Verify Proxmox Cloud Image Integrity"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
if [ -z "$PVE_PASSWORD" ]; then
|
||||
log_error "PVE_ROOT_PASS not set in .env"
|
||||
log_info "Set PVE_ROOT_PASS in .env file or pass as argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Connecting to Proxmox host: $PROXMOX_HOST"
|
||||
log_info "Checking image: $IMAGE_PATH"
|
||||
echo ""
|
||||
|
||||
# Check if file exists
|
||||
log_step "1. Checking if file exists..."
|
||||
if ssh -o StrictHostKeyChecking=no root@$PROXMOX_HOST "[ -f '$IMAGE_PATH' ]"; then
|
||||
log_info "✓ File exists"
|
||||
else
|
||||
log_error "✗ File not found: $IMAGE_PATH"
|
||||
log_info "Alternative locations to check:"
|
||||
log_info " - /var/lib/vz/import/ubuntu-24.04-server-cloudimg-amd64.img.raw"
|
||||
log_info " - /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get file size
|
||||
log_step "2. Checking file size..."
|
||||
FILE_SIZE=$(ssh root@$PROXMOX_HOST "ls -lh '$IMAGE_PATH' | awk '{print \$5}'")
|
||||
log_info "File size: $FILE_SIZE"
|
||||
|
||||
# Check file type
|
||||
log_step "3. Checking file type..."
|
||||
FILE_TYPE=$(ssh root@$PROXMOX_HOST "file '$IMAGE_PATH'")
|
||||
log_info "$FILE_TYPE"
|
||||
|
||||
# Verify with qemu-img
|
||||
log_step "4. Verifying image with qemu-img..."
|
||||
if ssh root@$PROXMOX_HOST "qemu-img info '$IMAGE_PATH' 2>&1"; then
|
||||
log_info "✓ Image appears valid"
|
||||
else
|
||||
log_error "✗ Image verification failed"
|
||||
log_warn "Image may be corrupted. See TROUBLESHOOTING_VM_9000.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check disk space
|
||||
log_step "5. Checking available disk space..."
|
||||
ssh root@$PROXMOX_HOST "df -h /var/lib/vz | tail -1"
|
||||
|
||||
# Check for I/O errors in dmesg
|
||||
log_step "6. Checking for recent I/O errors..."
|
||||
IO_ERRORS=$(ssh root@$PROXMOX_HOST "dmesg | grep -i 'i/o error' | tail -5")
|
||||
if [ -z "$IO_ERRORS" ]; then
|
||||
log_info "✓ No recent I/O errors found"
|
||||
else
|
||||
log_warn "Recent I/O errors detected:"
|
||||
echo "$IO_ERRORS"
|
||||
log_warn "This may indicate storage issues"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "========================================="
|
||||
log_info "Verification Complete"
|
||||
log_info "========================================="
|
||||
log_info ""
|
||||
log_info "If all checks passed, you can proceed with VM creation."
|
||||
log_info "If errors were found, see TROUBLESHOOTING_VM_9000.md"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user