HEX
Server: nginx/1.29.3
System: Linux 11979.bigscoots-wpo.com 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64
User: nginx (1068)
PHP: 7.4.33
Disabled: exec,system,passthru,shell_exec,proc_open,proc_close,popen,show_source,cmd# Do not modify this line # 1684243876
Upload Files
File: //bigscoots/wpo/cluster/cluster-mgr.sh
#!/bin/bash

# Source BigScoots common functions
source /bigscoots/includes/common.sh

CLUSTER_DIR="/root/.bigscoots/cluster"
NODES_FILE="$CLUSTER_DIR/nodes"
ROLE_FILE="$CLUSTER_DIR/role"
DB_SERVER_FILE="$CLUSTER_DIR/db_server"
LOG_FILE="/var/log/cluster-mgr.log"
SSH_PORT=2222

# Ensure local state dir exists
mkdir -p "$CLUSTER_DIR"

# --- Internal Helpers ---
get_master_ip() { [[ -f "$ROLE_FILE" ]] && cat "$ROLE_FILE" | cut -d':' -f2; }
status_done() { echo -e "  -> $1... \033[0;32m[ OK ]\033[0m"; }
status_fail() { echo -e "  -> $1... \033[0;31m[ FAIL ]\033[0m (Check $LOG_FILE)"; }

# --- Helper: Safely update /etc/hosts ---
update_hosts_entry() {
    local ip=$1
    local hostname=$2
    local comment="# [cluster-managed]"
    if grep -q "$hostname" /etc/hosts; then
        sed -i "/$hostname/c\\$ip $hostname $comment" /etc/hosts
    else
        echo "$ip $hostname $comment" >> /etc/hosts
    fi
}

# --- Function: Sync cluster config ---
sync_cluster_config() {
    local master_ip=$(get_master_ip)
    [[ -z "$master_ip" ]] && { echo "Set master first."; exit 1; }

    echo "Broadcasting cluster configuration (Master: $master_ip)..."

    while read -r line; do
        [[ -z "$line" || "$line" == "#"* ]] && continue
        local node_ip=$(echo "$line" | cut -d':' -f1)
        local node_port=$(echo "$line" | grep ":" | cut -d':' -f2); node_port=${node_port:-2222}

        [[ "$node_ip" == "$master_ip" ]] && continue
        echo "Processing Node: $node_ip"

        # 1. Prep & Install Lsyncd
        ssh -p "$node_port" -o ConnectTimeout=5 "$node_ip" "yum install -y lsyncd && mkdir -p $CLUSTER_DIR /var/log/lsyncd" < /dev/null >> "$LOG_FILE" 2>&1

        # 2. Sync Files
        rsync -avz -q -e "ssh -p $node_port" --exclude='role' "$CLUSTER_DIR/" "${node_ip}:${CLUSTER_DIR}/" >> "$LOG_FILE" 2>&1
        rsync -avz -q -e "ssh -p $node_port" "/root/.my.cnf" "${node_ip}:/root/.my.cnf" >> "$LOG_FILE" 2>&1
        [[ $? -eq 0 ]] && status_done "Config Sync" || status_fail "Config Sync"

        # 3. Provision Role and Network
        ssh -p "$node_port" "$node_ip" "echo 'slave:$master_ip' > $ROLE_FILE; sed -i '/internal.bscoots/d' /etc/hosts; echo '$master_ip objectcache.internal.bscoots # [cluster-managed]' >> /etc/hosts; echo '$master_ip sessions.internal.bscoots # [cluster-managed]' >> /etc/hosts; systemctl stop lsyncd 2>/dev/null; systemctl disable lsyncd 2>/dev/null;" < /dev/null >> "$LOG_FILE" 2>&1
        [[ $? -eq 0 ]] && status_done "Role & Network Provisioning" || status_fail "Role & Network Provisioning"
    done < "$NODES_FILE"

    # 4. Update Lsyncd Master Config Targets
    if [[ -f "/etc/lsyncd.conf" ]] && grep -q -e "-- TARGET_LIST_MARKER" /etc/lsyncd.conf; then
        echo "Updating Lsyncd target list on Master..."
        local new_targets=$(grep -v "$master_ip" "$NODES_FILE" | cut -d':' -f1 | sed 's/.*/"&"/' | paste -sd, - | sed 's/,/, /g')
        sed -i "/-- TARGET_LIST_MARKER/,/local targets/ s/local targets = {.*}/local targets = { $new_targets }/" /etc/lsyncd.conf
        status_done "Master Lsyncd Config Updated"
    fi

    echo "Restarting Lsyncd on Master..."
    systemctl restart lsyncd >> "$LOG_FILE" 2>&1
}

# --- Function: Sync DB permissions ---
sync_all_dbs() {
    local db_host="localhost"
    [[ -f "$DB_SERVER_FILE" ]] && db_host=$(head -n 1 "$DB_SERVER_FILE" | cut -d':' -f1)

    echo "Syncing DB permissions to $db_host..."

    find_wp_installs | while read -r wp_path; do
        local config="$wp_path/wp-config.php"
        [[ ! -f "$config" ]] && continue
        local db_name=$(grep "DB_NAME" "$config" | cut -d"'" -f4)
        local db_user=$(grep "DB_USER" "$config" | cut -d"'" -f4)
        local db_pass=$(grep "DB_PASSWORD" "$config" | cut -d"'" -f4)

        while read -r line; do
            [[ -z "$line" || "$line" == "#"* ]] && continue
            local node_ip=$(echo "$line" | cut -d':' -f1)

            # Using IDENTIFIED BY ensures the user is created for that IP if it doesn't exist
            mysql -h "$db_host" -e "GRANT ALL PRIVILEGES ON \`${db_name}\`.* TO '${db_user}'@'${node_ip}' IDENTIFIED BY '${db_pass}';" >> "$LOG_FILE" 2>&1
        done < "$NODES_FILE"
        [[ $? -eq 0 ]] && status_done "DB Grant: $db_name" || status_fail "DB Grant: $db_name"
    done
    mysql -h "$db_host" -e "FLUSH PRIVILEGES;" >> "$LOG_FILE" 2>&1
}

# --- Function: Comprehensive Status ---
show_status() {
    local role_raw=$(cat "$ROLE_FILE" 2>/dev/null)
    local role=$(echo "$role_raw" | cut -d':' -f1)
    local master_ip=$(echo "$role_raw" | cut -d':' -f2)
    local db_host="localhost"
    [[ -f "$DB_SERVER_FILE" ]] && db_host=$(head -n 1 "$DB_SERVER_FILE" | cut -d':' -f1)

    local local_host=$(hostname)
    local local_load=$(cat /proc/loadavg | awk '{print $1}')

    echo -e "\033[1m--- Local Node Identity ---\033[0m"
    echo "Hostname:   $local_host"
    echo "Role:       ${role^^}"

    echo -e "\n\033[1m--- Local Services ---\033[0m"
    pgrep lsyncd > /dev/null && echo -e "Lsyncd:          \033[0;32m[ RUNNING ]\033[0m" || echo -e "Lsyncd:          \033[0;31m[ STOPPED ]\033[0m"
    ss -tulpn | grep -q ":6380" && echo -e "Redis ObjCache:  \033[0;32m[ RUNNING ]\033[0m (6380)" || echo -e "Redis ObjCache:  \033[0;31m[ STOPPED ]\033[0m"
    ss -tulpn | grep -q ":6381" && echo -e "Redis Sessions:  \033[0;32m[ RUNNING ]\033[0m (6381)" || echo -e "Redis Sessions:  \033[0;31m[ STOPPED ]\033[0m"

    echo -e "\n\033[1m--- Cluster: Database Tier ---\033[0m"
    if [[ -f "$DB_SERVER_FILE" ]]; then
        while read -r db_line; do
            [[ -z "$db_line" || "$db_line" == "#"* ]] && continue
            local d_ip=$(echo "$db_line" | cut -d':' -f1)
            local d_role=$(echo "$db_line" | cut -d':' -f2); d_role=${d_role:-master}
            local d_data=$(ssh -p $SSH_PORT -o ConnectTimeout=2 "$d_ip" "echo \$(hostname):\$(systemctl is-active mysql 2>/dev/null || systemctl is-active mariadb 2>/dev/null):\$(cat /proc/loadavg | awk '{print \$1}')" < /dev/null 2>/dev/null)
            local d_host=$(echo $d_data | cut -d':' -f1)
            local d_status=$(echo $d_data | cut -d':' -f2)
            local d_load=$(echo $d_data | cut -d':' -f3)

            if [[ "$d_status" == "active" ]]; then
                echo -e "DB Node ($d_ip): \033[0;32m[ ONLINE / ${d_role^^} ]\033[0m - Load: $d_load - $d_host"
            else
                echo -e "DB Node ($d_ip): \033[0;31m[ OFFLINE / UNREACHABLE ]\033[0m - $d_host"
            fi
        done < "$DB_SERVER_FILE"
    fi

    echo -e "\n\033[1m--- Cluster: Web Tier ---\033[0m"
    echo -e "Web Node ($(hostname -I | awk '{print $1}')): \033[0;36m[ ONLINE / ${role^^} ]\033[0m - Load: $local_load (Local)"

    if [[ "$role" == "master" ]]; then
        while read -r line; do
            [[ -z "$line" || "$line" == "#"* ]] && continue
            local node_ip=$(echo "$line" | cut -d':' -f1)
            local node_port=$(echo "$line" | grep ":" | cut -d':' -f2); node_port=${node_port:-2222}

            if [[ "$node_ip" != "$master_ip" ]]; then
                local remote_data=$(ssh -p "$node_port" -o ConnectTimeout=2 "$node_ip" "echo \$(hostname):\$(cat $ROLE_FILE 2>/dev/null | cut -d':' -f1):\$(cat /proc/loadavg | awk '{print \$1}')" < /dev/null 2>/dev/null)
                local r_host=$(echo $remote_data | cut -d':' -f1)
                local r_role=$(echo $remote_data | cut -d':' -f2)
                local r_load=$(echo $remote_data | cut -d':' -f3)

                if [[ "$r_role" == "slave" ]]; then
                    echo -e "Web Node ($node_ip): \033[0;32m[ ONLINE / SLAVE ]\033[0m - Load: $r_load - $r_host"
                else
                    echo -e "Web Node ($node_ip): \033[0;31m[ UNREACHABLE / UNCONFIGURED ]\033[0m"
                fi
            fi
        done < "$NODES_FILE"
    fi
}

# --- Function: Promote to Master ---
promote_master() {
    local master_ip=$1
    echo "master:$master_ip" > "$ROLE_FILE"
    update_hosts_entry "127.0.0.1" "objectcache.internal.bscoots"
    update_hosts_entry "127.0.0.1" "sessions.internal.bscoots"

    systemctl enable --now redis-objectcache redis-sessions > /dev/null 2>&1
    for port in 6380 6381; do
        redis-cli -p $port CONFIG SET protected-mode no 2>/dev/null
        redis-cli -p $port CONFIG REWRITE 2>/dev/null
    done

    systemctl restart lsyncd > /dev/null 2>&1
    echo "This node is now MASTER and Redis is open to the cluster."
}

case "$1" in
    set-master)   promote_master "$2" ;;
    sync-cluster) sync_cluster_config ;;
    sync-dbs)     sync_all_dbs ;;
    status)       show_status ;;
    *) echo "Usage: $0 {set-master <ip>|sync-cluster|sync-dbs|status}"; exit 1 ;;
esac