#!/usr/bin/env bash # Multi-node deployment script - distributes containers across available nodes set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" source "$PROJECT_ROOT/lib/common.sh" source "$PROJECT_ROOT/lib/proxmox-api.sh" # Load configuration load_config # Node assignment strategies assign_node_auto() { local memory="$1" local disk="$2" local storage="${3:-${PROXMOX_STORAGE}}" proxmox_find_available_node "$memory" "$disk" "$storage" } assign_node_round_robin() { local nodes nodes=$(echo "${PROXMOX_NODES:-${PROXMOX_NODE}}" | tr ',' ' ') local node_array=($nodes) local index=$((RANDOM % ${#node_array[@]})) echo "${node_array[$index]}" } assign_node_manual() { # For manual assignment, use configuration file mapping local vmid="$1" local node_mapping="${NODE_MAPPING:-}" if [[ -n "$node_mapping" && -f "$node_mapping" ]]; then local assigned_node assigned_node=$(grep "^${vmid}:" "$node_mapping" | cut -d: -f2 || echo "") if [[ -n "$assigned_node" ]]; then echo "$assigned_node" return 0 fi fi # Fallback to default node echo "${PROXMOX_NODE}" } # Get node for container based on strategy get_assignment_node() { local vmid="$1" local memory="$2" local disk="$3" local storage="${4:-${PROXMOX_STORAGE}}" local strategy="${NODE_ASSIGNMENT_STRATEGY:-auto}" case "$strategy" in auto) assign_node_auto "$memory" "$disk" "$storage" ;; round-robin) assign_node_round_robin ;; manual) assign_node_manual "$vmid" ;; *) log_warn "Unknown strategy: $strategy, using default node" echo "${PROXMOX_NODE}" ;; esac } # Function to create container on specific node create_container_on_node() { local vmid="$1" local hostname="$2" local ip_address="$3" local memory="$4" local cores="$5" local disk="$6" local network_config="$7" local target_node="$8" local storage="${9:-${PROXMOX_STORAGE}}" log_info "Creating container $vmid ($hostname) on node $target_node..." if pct list | grep -q "^\s*$vmid\s"; then log_warn "Container $vmid already exists, skipping creation" return 0 fi # If on target node, create directly, otherwise use API if [[ "$(hostname)" == "$target_node" ]] || [[ "${PROXMOX_HOST}" == "$target_node" ]]; then pct create "$vmid" \ "${CONTAINER_OS_TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst}" \ --storage "$storage" \ --hostname "$hostname" \ --memory "$memory" \ --cores "$cores" \ --rootfs "${storage}:${disk}" \ --net0 "$network_config" \ --unprivileged 0 \ --features nesting=1,keyctl=1 log_success "Container $vmid created on node $target_node" else # Use API to create on remote node proxmox_create_container "$vmid" \ "${CONTAINER_OS_TEMPLATE}" \ "$storage" \ "$hostname" \ "$memory" \ "$cores" \ "$disk" \ "$network_config" \ "$target_node" fi } # Example: Deploy validators across nodes deploy_validators_multi_node() { local count="${1:-4}" local start_vmid="${VMID_VALIDATORS_START:-1000}" log_info "Deploying $count validators across available nodes..." for i in $(seq 0 $((count - 1))); do local vmid=$((start_vmid + i)) local hostname="validator-$((i + 1))" local ip_octet=$((10 + i)) local ip_address="${SUBNET_BASE:-10.3.1}.${ip_octet}" # Assign node local target_node target_node=$(get_assignment_node "$vmid" "${VALIDATOR_MEMORY:-8192}" "${VALIDATOR_DISK:-100}") log_info "Validator $((i + 1)) will be deployed on node: $target_node" # Network config local vlan="${VLAN_VALIDATORS:-100}" local network_config="bridge=${PROXMOX_BRIDGE:-vmbr0},tag=$vlan,name=eth0,ip=${ip_address}/${NETMASK:-24},gw=${GATEWAY:-10.3.1.1}" # Create container create_container_on_node \ "$vmid" \ "$hostname" \ "$ip_address" \ "${VALIDATOR_MEMORY:-8192}" \ "${VALIDATOR_CORES:-4}" \ "${VALIDATOR_DISK:-100}" \ "$network_config" \ "$target_node" done } # Main if [[ $# -eq 0 ]]; then echo "Usage: $0 [options]" echo "" echo "Commands:" echo " validators [count] - Deploy validators across nodes" echo " list-nodes - List available nodes" echo " check-resources - Check resource availability on all nodes" exit 1 fi check_root COMMAND="$1" shift || true case "$COMMAND" in validators) COUNT="${1:-4}" deploy_validators_multi_node "$COUNT" ;; list-nodes) log_info "Available nodes:" local nodes_output nodes_output=$(proxmox_list_nodes 2>/dev/null) if command_exists jq && [[ -n "$nodes_output" ]]; then echo "$nodes_output" | jq -r '.data[] | " - \(.node): \(.status)"' 2>/dev/null else if [[ -n "${PROXMOX_NODES:-}" ]]; then echo "$PROXMOX_NODES" | tr ',' '\n' | while read -r node; do log_info " - $node" done else log_info " - ${PROXMOX_NODE} (default node)" fi fi ;; check-resources) log_info "Checking resources on all nodes..." local nodes nodes=$(echo "${PROXMOX_NODES:-${PROXMOX_NODE}}" | tr ',' ' ') for node in $nodes; do log_info "Node: $node" proxmox_get_storage_usage "$node" "${PROXMOX_STORAGE}" done ;; *) error_exit "Unknown command: $COMMAND" ;; esac log_success "Multi-node deployment completed!"