#!/bin/bash # Comprehensive Deployment Validation for ml110-01 # Validates all prerequisites, configurations, and deployment readiness set +e # Don't exit on errors - we want to collect all results SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" DEPLOY_DIR="$PROJECT_ROOT/smom-dbis-138-proxmox" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Status tracking declare -a PASSED_ITEMS declare -a FAILED_ITEMS declare -a WARNING_ITEMS pass() { echo -e "${GREEN}✅${NC} $1" PASSED_ITEMS+=("$1") } fail() { echo -e "${RED}❌${NC} $1" FAILED_ITEMS+=("$1") } warn() { echo -e "${YELLOW}⚠️${NC} $1" WARNING_ITEMS+=("$1") } info() { echo -e "${BLUE}ℹ️${NC} $1" } section() { echo "" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}$1${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo "" } # Load environment ENV_FILE="$HOME/.env" if [ -f "$ENV_FILE" ]; then source <(grep -E "^PROXMOX_" "$ENV_FILE" 2>/dev/null | sed 's/^/export /' || true) fi TARGET_HOST="${PROXMOX_HOST:-192.168.11.10}" TARGET_NODE="${PROXMOX_NODE:-ml110-01}" echo "" echo -e "${BLUE}╔════════════════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║${NC} Deployment Validation for ml110-01 (${TARGET_HOST}) ${BLUE}║${NC}" echo -e "${BLUE}╚════════════════════════════════════════════════════════════════════════╝${NC}" echo "" # ============================================================================ # SECTION 1: PREREQUISITES # ============================================================================ section "1. PREREQUISITES CHECK" # Check required tools REQUIRED_TOOLS=("curl" "jq" "git" "bash") for tool in "${REQUIRED_TOOLS[@]}"; do if command -v "$tool" &> /dev/null; then VERSION=$($tool --version 2>&1 | head -1 | cut -d' ' -f1-3) pass "$tool installed ($VERSION)" else fail "$tool not found (required)" fi done # Check deployment directory if [ -d "$DEPLOY_DIR" ]; then pass "Deployment directory exists: $DEPLOY_DIR" else fail "Deployment directory not found: $DEPLOY_DIR" exit 1 fi # ============================================================================ # SECTION 2: PROXMOX CONNECTION # ============================================================================ section "2. PROXMOX CONNECTION VALIDATION" if [ -z "${PROXMOX_USER:-}" ] || [ -z "${PROXMOX_TOKEN_NAME:-}" ] || [ -z "${PROXMOX_TOKEN_VALUE:-}" ]; then fail "Proxmox credentials not configured in .env" info "Required: PROXMOX_USER, PROXMOX_TOKEN_NAME, PROXMOX_TOKEN_VALUE" else pass "Proxmox credentials found in .env" fi # Test API connectivity info "Testing connection to $TARGET_HOST:8006" API_RESPONSE=$(curl -k -s -w "\n%{http_code}" -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/version" 2>&1) HTTP_CODE=$(echo "$API_RESPONSE" | tail -1) RESPONSE_BODY=$(echo "$API_RESPONSE" | sed '$d') if [ "$HTTP_CODE" = "200" ]; then VERSION=$(echo "$RESPONSE_BODY" | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['version'])" 2>/dev/null || echo "unknown") pass "API connection successful (Proxmox version: $VERSION)" else fail "API connection failed (HTTP $HTTP_CODE)" if [ -n "$RESPONSE_BODY" ]; then info "Response: $(echo "$RESPONSE_BODY" | head -c 200)" fi fi # Get node list info "Retrieving node list..." NODES_RESPONSE=$(curl -k -s -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/nodes" 2>&1) if echo "$NODES_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)['data']" 2>/dev/null; then NODE_COUNT=$(echo "$NODES_RESPONSE" | python3 -c "import sys, json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) NODE_NAMES=$(echo "$NODES_RESPONSE" | python3 -c "import sys, json; print(' '.join([n['node'] for n in json.load(sys.stdin)['data']]))" 2>/dev/null) pass "Retrieved $NODE_COUNT node(s): $NODE_NAMES" # Check if target node exists if echo "$NODES_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin)['data']; exit(0 if any(n['node']=='${TARGET_NODE}' for n in data) else 1)" 2>/dev/null; then pass "Target node '$TARGET_NODE' found" else FIRST_NODE=$(echo "$NODES_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin)['data'][0]['node'])" 2>/dev/null) warn "Target node '$TARGET_NODE' not found, will use: $FIRST_NODE" TARGET_NODE="$FIRST_NODE" fi else fail "Failed to retrieve nodes" fi # ============================================================================ # SECTION 3: STORAGE VALIDATION # ============================================================================ section "3. STORAGE VALIDATION" STORAGE_RESPONSE=$(curl -k -s -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/nodes/${TARGET_NODE}/storage" 2>&1) if echo "$STORAGE_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)['data']" 2>/dev/null; then STORAGE_COUNT=$(echo "$STORAGE_RESPONSE" | python3 -c "import sys, json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) pass "Found $STORAGE_COUNT storage pool(s)" # Check for container storage CONTAINER_STORAGE=$(echo "$STORAGE_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin)['data']; result=[s['storage'] for s in data if 'rootdir' in s.get('content', '').split(',')]; print(result[0] if result else '')" 2>/dev/null) if [ -n "$CONTAINER_STORAGE" ]; then pass "Container storage found: $CONTAINER_STORAGE" else fail "No container storage (rootdir) found" fi # Check for template storage TEMPLATE_STORAGE=$(echo "$STORAGE_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin)['data']; result=[s['storage'] for s in data if 'vztmpl' in s.get('content', '').split(',')]; print(result[0] if result else '')" 2>/dev/null) if [ -n "$TEMPLATE_STORAGE" ]; then pass "Template storage found: $TEMPLATE_STORAGE" else warn "No template storage (vztmpl) found" fi else fail "Failed to retrieve storage information" fi # ============================================================================ # SECTION 4: TEMPLATE VALIDATION # ============================================================================ section "4. LXC TEMPLATE VALIDATION" if [ -n "$TEMPLATE_STORAGE" ]; then info "Checking templates on storage: $TEMPLATE_STORAGE" TEMPLATES_RESPONSE=$(curl -k -s -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/nodes/${TARGET_NODE}/storage/${TEMPLATE_STORAGE}/content?content=vztmpl" 2>&1) if echo "$TEMPLATES_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)['data']" 2>/dev/null; then TEMPLATE_COUNT=$(echo "$TEMPLATES_RESPONSE" | python3 -c "import sys, json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) pass "Found $TEMPLATE_COUNT template(s)" # Check for Debian 12 template DEBIAN_TEMPLATE=$(echo "$TEMPLATES_RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin)['data']; result=[t['volid'] for t in data if 'debian-12' in t.get('volid', '')]; print(result[0] if result else '')" 2>/dev/null) if [ -n "$DEBIAN_TEMPLATE" ]; then pass "Debian 12 template found: $DEBIAN_TEMPLATE" else fail "Debian 12 template not found (required)" info "Available templates:" echo "$TEMPLATES_RESPONSE" | python3 -c "import sys, json; [print(' -', t['volid']) for t in json.load(sys.stdin)['data'][:10]]" 2>/dev/null fi else warn "Could not retrieve template list" fi else warn "Cannot check templates - no template storage configured" fi # ============================================================================ # SECTION 5: CONFIGURATION FILES # ============================================================================ section "5. CONFIGURATION FILES VALIDATION" CONFIG_DIR="$DEPLOY_DIR/config" if [ -d "$CONFIG_DIR" ]; then pass "Config directory exists: $CONFIG_DIR" # Check proxmox.conf if [ -f "$CONFIG_DIR/proxmox.conf" ]; then pass "proxmox.conf exists" source "$CONFIG_DIR/proxmox.conf" 2>/dev/null || true if [ -n "${PROXMOX_HOST:-}" ]; then if [ "$PROXMOX_HOST" = "$TARGET_HOST" ]; then pass "PROXMOX_HOST configured correctly: $PROXMOX_HOST" elif [ "$PROXMOX_HOST" = "proxmox.example.com" ]; then warn "PROXMOX_HOST still set to example value" else warn "PROXMOX_HOST mismatch: $PROXMOX_HOST (expected: $TARGET_HOST)" fi else fail "PROXMOX_HOST not set in proxmox.conf" fi [ -n "${PROXMOX_NODE:-}" ] && pass "PROXMOX_NODE configured: $PROXMOX_NODE" || warn "PROXMOX_NODE not set" [ -n "${PROXMOX_STORAGE:-}" ] && pass "PROXMOX_STORAGE configured: $PROXMOX_STORAGE" || warn "PROXMOX_STORAGE not set" [ -n "${CONTAINER_OS_TEMPLATE:-}" ] && pass "CONTAINER_OS_TEMPLATE configured: $CONTAINER_OS_TEMPLATE" || warn "CONTAINER_OS_TEMPLATE not set" else if [ -f "$CONFIG_DIR/proxmox.conf.example" ]; then fail "proxmox.conf not found - copy from proxmox.conf.example and configure" else fail "proxmox.conf not found" fi fi # Check network.conf if [ -f "$CONFIG_DIR/network.conf" ]; then pass "network.conf exists" source "$CONFIG_DIR/network.conf" 2>/dev/null || true [ -n "${SUBNET_BASE:-}" ] && pass "SUBNET_BASE configured: $SUBNET_BASE" || warn "SUBNET_BASE not set" [ -n "${GATEWAY:-}" ] && pass "GATEWAY configured: $GATEWAY" || warn "GATEWAY not set" else if [ -f "$CONFIG_DIR/network.conf.example" ]; then warn "network.conf not found (example exists)" else warn "network.conf not found" fi fi else fail "Config directory not found: $CONFIG_DIR" fi # ============================================================================ # SECTION 6: DEPLOYMENT SCRIPTS # ============================================================================ section "6. DEPLOYMENT SCRIPTS VALIDATION" SCRIPTS_DIR="$DEPLOY_DIR/scripts/deployment" if [ -d "$SCRIPTS_DIR" ]; then pass "Deployment scripts directory exists" REQUIRED_SCRIPTS=( "deploy-all.sh" "deploy-besu-nodes.sh" "deploy-services.sh" "deploy-hyperledger-services.sh" "deploy-monitoring.sh" "deploy-explorer.sh" ) for script in "${REQUIRED_SCRIPTS[@]}"; do SCRIPT_PATH="$SCRIPTS_DIR/$script" if [ -f "$SCRIPT_PATH" ]; then if [ -x "$SCRIPT_PATH" ]; then pass "$script exists and is executable" # Check syntax if bash -n "$SCRIPT_PATH" 2>&1 | grep -q "error"; then fail "$script has syntax errors" fi else warn "$script exists but is not executable" fi else warn "$script not found" fi done else fail "Deployment scripts directory not found: $SCRIPTS_DIR" fi # Check libraries LIB_DIR="$DEPLOY_DIR/lib" [ -f "$LIB_DIR/common.sh" ] && pass "common.sh library exists" || fail "common.sh library not found" [ -f "$LIB_DIR/proxmox-api.sh" ] && pass "proxmox-api.sh library exists" || warn "proxmox-api.sh library not found" # ============================================================================ # SECTION 7: INSTALLATION SCRIPTS # ============================================================================ section "7. INSTALLATION SCRIPTS VALIDATION" INSTALL_DIR="$DEPLOY_DIR/install" if [ -d "$INSTALL_DIR" ]; then pass "Install scripts directory exists" REQUIRED_INSTALL_SCRIPTS=( "besu-validator-install.sh" "besu-sentry-install.sh" "besu-rpc-install.sh" "oracle-publisher-install.sh" "ccip-monitor-install.sh" "keeper-install.sh" "monitoring-stack-install.sh" "blockscout-install.sh" ) for script in "${REQUIRED_INSTALL_SCRIPTS[@]}"; do [ -f "$INSTALL_DIR/$script" ] && pass "$script exists" || warn "$script not found" done else fail "Install scripts directory not found: $INSTALL_DIR" fi # ============================================================================ # SECTION 8: RESOURCE REQUIREMENTS # ============================================================================ section "8. RESOURCE REQUIREMENTS VALIDATION" # Load config if available if [ -f "$CONFIG_DIR/proxmox.conf" ]; then source "$CONFIG_DIR/proxmox.conf" 2>/dev/null || true # Estimate resources VALIDATOR_COUNT="${VALIDATORS_COUNT:-4}" SENTRY_COUNT="${SENTRIES_COUNT:-3}" RPC_COUNT="${RPC_COUNT:-3}" ESTIMATED_MEMORY=$(( (VALIDATOR_MEMORY * VALIDATOR_COUNT) + (SENTRY_MEMORY * SENTRY_COUNT) + (RPC_MEMORY * RPC_COUNT) + MONITORING_MEMORY )) ESTIMATED_DISK=$(( (VALIDATOR_DISK * VALIDATOR_COUNT) + (SENTRY_DISK * SENTRY_COUNT) + (RPC_DISK * RPC_COUNT) + MONITORING_DISK )) info "Estimated requirements:" info " Memory: ~$((ESTIMATED_MEMORY / 1024))GB" info " Disk: ~${ESTIMATED_DISK}GB" # Get node resources NODE_STATUS=$(curl -k -s -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/nodes/${TARGET_NODE}/status" 2>&1) if echo "$NODE_STATUS" | python3 -c "import sys, json; json.load(sys.stdin)['data']" 2>/dev/null; then NODE_MEMORY=$(echo "$NODE_STATUS" | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['memory']['total'])" 2>/dev/null || echo "0") NODE_FREE_MEMORY=$(echo "$NODE_STATUS" | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['memory']['free'])" 2>/dev/null || echo "0") if [ "$NODE_MEMORY" != "0" ] && [ -n "$NODE_MEMORY" ]; then MEMORY_GB=$((NODE_MEMORY / 1024 / 1024 / 1024)) FREE_MEMORY_GB=$((NODE_FREE_MEMORY / 1024 / 1024 / 1024)) info "Node resources: ${MEMORY_GB}GB total, ${FREE_MEMORY_GB}GB free" if [ $FREE_MEMORY_GB -ge $((ESTIMATED_MEMORY / 1024)) ]; then pass "Sufficient memory available" else warn "Limited free memory: need ~$((ESTIMATED_MEMORY / 1024))GB, have ${FREE_MEMORY_GB}GB" fi fi fi fi # ============================================================================ # SECTION 9: EXISTING CONTAINERS # ============================================================================ section "9. EXISTING CONTAINERS CHECK" CONTAINERS_RESPONSE=$(curl -k -s -m 10 \ -H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \ "https://${TARGET_HOST}:8006/api2/json/nodes/${TARGET_NODE}/lxc" 2>&1) if echo "$CONTAINERS_RESPONSE" | python3 -c "import sys, json; json.load(sys.stdin)['data']" 2>/dev/null; then EXISTING_COUNT=$(echo "$CONTAINERS_RESPONSE" | python3 -c "import sys, json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) if [ "$EXISTING_COUNT" -gt 0 ]; then info "Found $EXISTING_COUNT existing container(s)" # Check for VMID conflicts EXPECTED_VMIDS=(106 107 108 109 110 111 112 115 116 117 120 121 122 130 140 150 151 152 153) CONFLICTS=$(echo "$CONTAINERS_RESPONSE" | python3 -c " import sys, json data = json.load(sys.stdin)['data'] existing = [c['vmid'] for c in data] expected = [106, 107, 108, 109, 110, 111, 112, 115, 116, 117, 120, 121, 122, 130, 140, 150, 151, 152, 153] conflicts = [v for v in expected if v in existing] print(' '.join(map(str, conflicts)))" 2>/dev/null) if [ -n "$CONFLICTS" ]; then warn "VMID conflicts: $CONFLICTS (containers already exist)" else pass "No VMID conflicts detected" fi else pass "No existing containers (clean slate)" fi else warn "Could not retrieve container list" fi # ============================================================================ # SUMMARY # ============================================================================ section "VALIDATION SUMMARY" TOTAL_PASSED=${#PASSED_ITEMS[@]} TOTAL_FAILED=${#FAILED_ITEMS[@]} TOTAL_WARNINGS=${#WARNING_ITEMS[@]} echo -e "${CYAN}Results:${NC}" echo -e " ${GREEN}Passed:${NC} $TOTAL_PASSED" echo -e " ${RED}Failed:${NC} $TOTAL_FAILED" echo -e " ${YELLOW}Warnings:${NC} $TOTAL_WARNINGS" echo "" if [ $TOTAL_FAILED -eq 0 ]; then echo -e "${GREEN}✅ Deployment validation PASSED${NC}" echo "" echo "Next steps:" echo " 1. Review and update configuration files in $CONFIG_DIR/" echo " 2. Run deployment: cd $DEPLOY_DIR && ./scripts/deployment/deploy-all.sh" exit 0 else echo -e "${RED}❌ Deployment validation FAILED${NC}" echo "" echo "Critical issues found. Please fix the failures above before deployment." exit 1 fi