fix(cluster): ensure AstDB transactional sync, auto-healing, and Gratuitous ARP on master take-over
This commit is contained in:
1 parent
062495a162
commit
a537fc6469
4 files changed
+90
-12
No files matched your search
+1
-1
@@ -65,7 +65,7 @@ apt-get install -y --no-install-recommends \
|
|||||||
sshpass
|
sshpass
|
||||||
|
|
||||||
# Composants optionnels Haute Disponibilité (Cluster HA)
|
# Composants optionnels Haute Disponibilité (Cluster HA)
|
||||||
apt-get install -y --no-install-recommends keepalived 2>/dev/null || true
|
apt-get install -y --no-install-recommends keepalived iputils-arping 2>/dev/null || true
|
||||||
systemctl stop keepalived >/dev/null 2>&1 || true
|
systemctl stop keepalived >/dev/null 2>&1 || true
|
||||||
systemctl disable keepalived >/dev/null 2>&1 || true
|
systemctl disable keepalived >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,26 @@ echo "[$TIMESTAMP] [CLUSTER] >>> Transition vers l'état MASTER (VIP active) <<<
|
|||||||
|
|
||||||
# 1. Mise à jour de l'état en base de données locale si SQLite disponible
|
# 1. Mise à jour de l'état en base de données locale si SQLite disponible
|
||||||
DB_PATH="/var/www/nexus-pbx/storage/database.sqlite"
|
DB_PATH="/var/www/nexus-pbx/storage/database.sqlite"
|
||||||
|
ASTDB_PATH="/var/lib/asterisk/astdb.sqlite3"
|
||||||
|
|
||||||
if [ -f "$DB_PATH" ]; then
|
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
|
sqlite3 "$DB_PATH" "UPDATE cluster_settings SET last_sync_status='master_active', updated_at=CURRENT_TIMESTAMP WHERE id=1;" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Réactivation des enregistrements Trunks PJSIP et rechargement de la config
|
# 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. Réactivation des enregistrements Trunks PJSIP et rechargement de la config
|
||||||
if pidof asterisk >/dev/null 2>&1; then
|
if pidof asterisk >/dev/null 2>&1; then
|
||||||
/usr/sbin/asterisk -rx "pjsip reload" >> "$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
|
/usr/sbin/asterisk -rx "pjsip send register *" >> "$LOG_FILE" 2>&1 || true
|
||||||
@@ -26,11 +41,13 @@ if pidof asterisk >/dev/null 2>&1; then
|
|||||||
/usr/sbin/asterisk -rx "dialplan reload" >> "$LOG_FILE" 2>&1 || true
|
/usr/sbin/asterisk -rx "dialplan reload" >> "$LOG_FILE" 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Gratuitous ARP optionnel si arping est présent
|
# 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 "")
|
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")
|
IFACE=$(sqlite3 "$DB_PATH" "SELECT interface FROM cluster_settings WHERE id=1;" 2>/dev/null || echo "eth0")
|
||||||
if [ -n "$VIP" ] && command -v arping >/dev/null 2>&1; then
|
VIP_CLEAN=$(echo "$VIP" | cut -d'/' -f1)
|
||||||
arping -c 3 -U -I "$IFACE" "$VIP" >> "$LOG_FILE" 2>&1 || true
|
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
|
fi
|
||||||
|
|
||||||
echo "[$TIMESTAMP] [CLUSTER] Prise de relais MASTER terminée avec succès." >> "$LOG_FILE"
|
echo "[$TIMESTAMP] [CLUSTER] Prise de relais MASTER terminée avec succès." >> "$LOG_FILE"
|
||||||
|
|||||||
+57
-3
@@ -15,9 +15,9 @@ case "$CMD" in
|
|||||||
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-nexus-ha.conf 2>/dev/null || true
|
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-nexus-ha.conf 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Vérification et installation éventuelle de Keepalived
|
# 2. Vérification et installation éventuelle de Keepalived et arping
|
||||||
if ! command -v keepalived >/dev/null 2>&1; then
|
if ! command -v keepalived >/dev/null 2>&1 || ! command -v arping >/dev/null 2>&1; then
|
||||||
apt-get update -qq && apt-get install -y -qq keepalived >/dev/null 2>&1 || true
|
apt-get update -qq && apt-get install -y -qq keepalived iputils-arping >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Création et sécurisation stricte du dossier des scripts Keepalived (exigences strictes Keepalived 2.x)
|
# 3. Création et sécurisation stricte du dossier des scripts Keepalived (exigences strictes Keepalived 2.x)
|
||||||
@@ -64,6 +64,60 @@ case "$CMD" in
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
restore-astdb)
|
||||||
|
SRC="${2:-/tmp/astdb_cluster_sync.sqlite3}"
|
||||||
|
DEST="/var/lib/asterisk/astdb.sqlite3"
|
||||||
|
if [ ! -f "$SRC" ] || [ ! -s "$SRC" ]; then
|
||||||
|
echo "ASTDB_RESTORE_NO_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérification intégrité de la source
|
||||||
|
SRC_INTEGRITY=$(sqlite3 "$SRC" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt")
|
||||||
|
if [ "$SRC_INTEGRITY" != "ok" ]; then
|
||||||
|
echo "ASTDB_SOURCE_CORRUPT: $SRC_INTEGRITY"
|
||||||
|
rm -f "$SRC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fusion ou remplacement propre
|
||||||
|
if [ -f "$DEST" ]; then
|
||||||
|
DEST_INTEGRITY=$(sqlite3 "$DEST" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt")
|
||||||
|
if [ "$DEST_INTEGRITY" = "ok" ]; then
|
||||||
|
# Fusion SQLite transactionnelle sans fermer les descripteurs d'Asterisk
|
||||||
|
sqlite3 "$DEST" "ATTACH '$SRC' AS src; INSERT OR REPLACE INTO astdb SELECT * FROM src.astdb; DETACH src;" 2>/dev/null || cp -f "$SRC" "$DEST"
|
||||||
|
else
|
||||||
|
cp -f "$SRC" "$DEST"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cp -f "$SRC" "$DEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chown asterisk:asterisk "$DEST" 2>/dev/null || true
|
||||||
|
chmod 664 "$DEST" 2>/dev/null || true
|
||||||
|
rm -f "$SRC"
|
||||||
|
echo "ASTDB_RESTORED_OK"
|
||||||
|
;;
|
||||||
|
|
||||||
|
fix-astdb)
|
||||||
|
DEST="/var/lib/asterisk/astdb.sqlite3"
|
||||||
|
if [ -f "$DEST" ]; then
|
||||||
|
INTEGRITY=$(sqlite3 "$DEST" "PRAGMA integrity_check;" 2>/dev/null || echo "corrupt")
|
||||||
|
if [ "$INTEGRITY" != "ok" ]; then
|
||||||
|
mv -f "$DEST" "${DEST}.corrupt.$(date +%s)" 2>/dev/null || true
|
||||||
|
sqlite3 "$DEST" "CREATE TABLE astdb(key TEXT, value TEXT, PRIMARY KEY(key));" 2>/dev/null || true
|
||||||
|
chown asterisk:asterisk "$DEST" 2>/dev/null || true
|
||||||
|
chmod 664 "$DEST" 2>/dev/null || true
|
||||||
|
if pidof asterisk >/dev/null 2>&1; then
|
||||||
|
/usr/sbin/asterisk -rx "pjsip reload" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
echo "ASTDB_FIXED"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "ASTDB_OK"
|
||||||
|
;;
|
||||||
|
|
||||||
check)
|
check)
|
||||||
if pidof asterisk >/dev/null 2>&1 && /usr/sbin/asterisk -rx "core show uptime" 2>/dev/null | grep -qi "uptime"; then
|
if pidof asterisk >/dev/null 2>&1 && /usr/sbin/asterisk -rx "core show uptime" 2>/dev/null | grep -qi "uptime"; then
|
||||||
echo "HEALTHY"
|
echo "HEALTHY"
|
||||||
|
|||||||
+11
-4
@@ -353,6 +353,9 @@ class ClusterManager
|
|||||||
$conf[] = " virtual_router_id {$vrid}";
|
$conf[] = " virtual_router_id {$vrid}";
|
||||||
$conf[] = " priority {$prio}";
|
$conf[] = " priority {$prio}";
|
||||||
$conf[] = " advert_int 1";
|
$conf[] = " advert_int 1";
|
||||||
|
$conf[] = " garp_master_delay 1";
|
||||||
|
$conf[] = " garp_master_repeat 5";
|
||||||
|
$conf[] = " garp_master_refresh 5";
|
||||||
$conf[] = "";
|
$conf[] = "";
|
||||||
|
|
||||||
// Unicast VRRP si l'IP du nœud pair est configurée (indispensable en environnement virtualisé type Proxmox/VMware)
|
// Unicast VRRP si l'IP du nœud pair est configurée (indispensable en environnement virtualisé type Proxmox/VMware)
|
||||||
@@ -473,7 +476,12 @@ class ClusterManager
|
|||||||
@unlink($tmpAstdb);
|
@unlink($tmpAstdb);
|
||||||
shell_exec("sqlite3 " . escapeshellarg($astdbPath) . " \".backup " . escapeshellarg($tmpAstdb) . "\" 2>/dev/null || cp -f " . escapeshellarg($astdbPath) . " " . escapeshellarg($tmpAstdb) . " 2>/dev/null || true");
|
shell_exec("sqlite3 " . escapeshellarg($astdbPath) . " \".backup " . escapeshellarg($tmpAstdb) . "\" 2>/dev/null || cp -f " . escapeshellarg($astdbPath) . " " . escapeshellarg($tmpAstdb) . " 2>/dev/null || true");
|
||||||
if (file_exists($tmpAstdb) && filesize($tmpAstdb) > 0) {
|
if (file_exists($tmpAstdb) && filesize($tmpAstdb) > 0) {
|
||||||
|
$integrity = trim(shell_exec("sqlite3 " . escapeshellarg($tmpAstdb) . " 'PRAGMA integrity_check;' 2>/dev/null") ?? '');
|
||||||
|
if ($integrity === 'ok') {
|
||||||
$hasAstdb = true;
|
$hasAstdb = true;
|
||||||
|
} else {
|
||||||
|
@unlink($tmpAstdb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,10 +629,9 @@ class ClusterManager
|
|||||||
// Restauration de l'astdb (contacts et IP actives des téléphones PJSIP)
|
// Restauration de l'astdb (contacts et IP actives des téléphones PJSIP)
|
||||||
shell_exec("tar -xzf " . escapeshellarg($tmpArchive) . " -C /tmp astdb_cluster_sync.sqlite3 2>/dev/null || true");
|
shell_exec("tar -xzf " . escapeshellarg($tmpArchive) . " -C /tmp astdb_cluster_sync.sqlite3 2>/dev/null || true");
|
||||||
if (file_exists('/tmp/astdb_cluster_sync.sqlite3')) {
|
if (file_exists('/tmp/astdb_cluster_sync.sqlite3')) {
|
||||||
copy('/tmp/astdb_cluster_sync.sqlite3', '/var/lib/asterisk/astdb.sqlite3');
|
// Utilisation du helper root pour fusionner de manière transactionnelle via SQLite ATTACH
|
||||||
@chown('/var/lib/asterisk/astdb.sqlite3', 'asterisk');
|
// sans casser les descripteurs de fichiers d'Asterisk ni risquer d'écrasement corrompu
|
||||||
@chgrp('/var/lib/asterisk/astdb.sqlite3', 'asterisk');
|
shell_exec('sudo -n ' . escapeshellcmd(self::CLUSTER_HELPER_PATH) . ' restore-astdb /tmp/astdb_cluster_sync.sqlite3 2>&1');
|
||||||
@chmod('/var/lib/asterisk/astdb.sqlite3', 0664);
|
|
||||||
@unlink('/tmp/astdb_cluster_sync.sqlite3');
|
@unlink('/tmp/astdb_cluster_sync.sqlite3');
|
||||||
}
|
}
|
||||||
@unlink($tmpArchive);
|
@unlink($tmpArchive);
|
||||||
|
|||||||
Reference in new issue
Block a user