#!/usr/bin/env bash # ============================================================================== # NexusPBX HA - Notification Keepalived : Transition vers l'état MASTER # ============================================================================== set -euo pipefail export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH" LOG_FILE="/var/log/nexus_cluster.log" TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') echo "[$TIMESTAMP] [CLUSTER] >>> Transition vers l'état MASTER (VIP active) <<<" >> "$LOG_FILE" # 0. Réconciliation préalable au Failback : si le pair était actif et a des modifications plus récentes, # récupérer sa réplication AVANT d'ouvrir les flux SIP et de servir le trafic ! if [ -f "/var/www/nexus-pbx/scripts/nexus_cluster_sync.php" ]; then echo "[$TIMESTAMP] [CLUSTER] Vérification réconciliation failback avec le pair..." >> "$LOG_FILE" php /var/www/nexus-pbx/scripts/nexus_cluster_sync.php --failback-check >> "$LOG_FILE" 2>&1 || true fi # 1. Ouverture immédiate des ports SIP entrants sur le nouveau MASTER (levée d'isolation) iptables -D INPUT -p udp -m multiport --dports 5060,5061 -j REJECT --reject-with icmp-port-unreachable 2>/dev/null || true iptables -D INPUT -p tcp -m multiport --dports 5060,5061 -j REJECT --reject-with tcp-reset 2>/dev/null || true # 1. Mise à jour de l'état en base de données locale si SQLite disponible DB_PATH="/var/www/nexus-pbx/storage/database.sqlite" ASTDB_PATH="/var/lib/asterisk/astdb.sqlite3" if [ -f "$DB_PATH" ]; then sqlite3 "$DB_PATH" "UPDATE cluster_settings SET last_sync_status='master_active', updated_at=CURRENT_TIMESTAMP WHERE id=1;" 2>/dev/null || true chown www-data:asterisk "$DB_PATH" "${DB_PATH}-wal" "${DB_PATH}-shm" 2>/dev/null || true chmod 660 "$DB_PATH" "${DB_PATH}-wal" "${DB_PATH}-shm" 2>/dev/null || true fi # 2. Vérification et réparation d'intégrité de la base SQLite AstDB (contacts téléphones) if [ -f "$ASTDB_PATH" ]; then INTEGRITY=$(sqlite3 "$ASTDB_PATH" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt") if [ "$INTEGRITY" != "ok" ]; then echo "[$TIMESTAMP] [CLUSTER] ALERTE: astdb.sqlite3 corrompu ($INTEGRITY). Auto-réparation immédiate..." >> "$LOG_FILE" mv -f "$ASTDB_PATH" "${ASTDB_PATH}.corrupt.$(date +%s)" 2>/dev/null || true sqlite3 "$ASTDB_PATH" "CREATE TABLE astdb(key TEXT, value TEXT, PRIMARY KEY(key));" 2>/dev/null || true chown asterisk:asterisk "$ASTDB_PATH" 2>/dev/null || true chmod 664 "$ASTDB_PATH" 2>/dev/null || true echo "[$TIMESTAMP] [CLUSTER] astdb.sqlite3 recréé proprement." >> "$LOG_FILE" fi fi # 3. Purge préventive des canaux résiduels / fantômes et rechargement PJSIP/Dialplan # Si le nœud a été mis en pause ou déconnecté, les canaux antérieurs sont morts. # Leur raccrochage réinitialise immédiatement les hints à l'état Idle et éteint les BLF. if pidof asterisk >/dev/null 2>&1; then /usr/sbin/asterisk -rx "channel request hangup all" >> "$LOG_FILE" 2>&1 || true /usr/sbin/asterisk -rx "pjsip reload" >> "$LOG_FILE" 2>&1 || true /usr/sbin/asterisk -rx "pjsip send register *" >> "$LOG_FILE" 2>&1 || true for reg in $(/usr/sbin/asterisk -rx "pjsip show registrations" 2>/dev/null | grep -o '[a-zA-Z0-9_\-]*-reg' | sort -u); do /usr/sbin/asterisk -rx "pjsip send register $reg" >> "$LOG_FILE" 2>&1 || true done /usr/sbin/asterisk -rx "dialplan reload" >> "$LOG_FILE" 2>&1 || true fi # 4. Gratuitous ARP pour forcer la mise à jour immédiate des tables ARP des switches et téléphones SIP VIP=$(sqlite3 "$DB_PATH" "SELECT virtual_ip FROM cluster_settings WHERE id=1;" 2>/dev/null || echo "") IFACE=$(sqlite3 "$DB_PATH" "SELECT interface FROM cluster_settings WHERE id=1;" 2>/dev/null || echo "eth0") VIP_CLEAN=$(echo "$VIP" | cut -d'/' -f1) if [ -n "$VIP_CLEAN" ] && command -v arping >/dev/null 2>&1; then echo "[$TIMESTAMP] [CLUSTER] Émission Gratuitous ARP pour $VIP_CLEAN sur $IFACE..." >> "$LOG_FILE" arping -c 5 -U -I "$IFACE" "$VIP_CLEAN" >> "$LOG_FILE" 2>&1 || true fi # 5. Réveil progressif et réabonnement BLF des postes téléphoniques # Dès que les postes se réenregistrent sur ce Master (via la VIP), un signal silencieux check-sync # leur est transmis pour forcer la synchronisation immédiate de l'ensemble des touches BLF. if pidof asterisk >/dev/null 2>&1; then echo "[$TIMESTAMP] [CLUSTER] Lancement du réveil BLF progressif en arrière-plan..." >> "$LOG_FILE" ( for delay in 2 5 12 25; do sleep "$delay" if ! pidof asterisk >/dev/null 2>&1; then break; fi # Cibler uniquement les postes actuellement joignables (Avail) for ep in $(/usr/sbin/asterisk -rx "pjsip show contacts" 2>/dev/null | grep -E 'Contact:.*Avail' | awk '{print $2}' | cut -d'/' -f1 | sort -u); do if [ -n "$ep" ]; then /usr/sbin/asterisk -rx "pjsip send notify check-sync endpoint $ep" >> "$LOG_FILE" 2>&1 || true fi done done ) >/dev/null 2>&1 & fi echo "[$TIMESTAMP] [CLUSTER] Prise de relais MASTER terminée avec succès." >> "$LOG_FILE" exit 0