fix(ha): orchestration Keepalived root, support unicast VRRP, preservation identite backup et synchro HTTPS
This commit is contained in:
1 parent
93f842a363
commit
da0805fd5f
7 files changed
+291
-71
No files matched your search
+10
-4
@@ -171,8 +171,12 @@ mkdir -p /var/lib/asterisk/sounds/custom
|
|||||||
mkdir -p /var/spool/asterisk/voicemail/default
|
mkdir -p /var/spool/asterisk/voicemail/default
|
||||||
mkdir -p /var/log/asterisk /var/log/asterisk/cdr-csv
|
mkdir -p /var/log/asterisk /var/log/asterisk/cdr-csv
|
||||||
mkdir -p /var/log/nexus-pbx
|
mkdir -p /var/log/nexus-pbx
|
||||||
mkdir -p /etc/keepalived
|
mkdir -p /etc/keepalived/scripts
|
||||||
mkdir -p "$TARGET_DIR/scripts/cluster"
|
mkdir -p "$TARGET_DIR/scripts/cluster"
|
||||||
|
touch /var/log/nexus_cluster.log
|
||||||
|
chmod 666 /var/log/nexus_cluster.log
|
||||||
|
sysctl -w net.ipv4.ip_nonlocal_bind=1 >/dev/null 2>&1 || true
|
||||||
|
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-nexus-ha.conf 2>/dev/null || true
|
||||||
|
|
||||||
touch /var/log/asterisk/messages /var/log/asterisk/security.log /var/log/asterisk/cdr-csv/Master.csv
|
touch /var/log/asterisk/messages /var/log/asterisk/security.log /var/log/asterisk/cdr-csv/Master.csv
|
||||||
chown -R asterisk:asterisk /var/log/asterisk 2>/dev/null || true
|
chown -R asterisk:asterisk /var/log/asterisk 2>/dev/null || true
|
||||||
@@ -201,7 +205,7 @@ setfacl -R -d -m u:www-data:rwx,g:asterisk:rwx /etc/asterisk /var/lib/asterisk/s
|
|||||||
# Droits sudoers pour l'interface web (www-data)
|
# Droits sudoers pour l'interface web (www-data)
|
||||||
cat << 'EOF' > /etc/sudoers.d/nexus-pbx
|
cat << 'EOF' > /etc/sudoers.d/nexus-pbx
|
||||||
# Privilèges spécifiques NexusPBX pour l'utilisateur Web www-data
|
# Privilèges spécifiques NexusPBX pour l'utilisateur Web www-data
|
||||||
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables, /var/www/nexus-pbx/scripts/update.sh, /usr/local/bin/nexus-update
|
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables, /var/www/nexus-pbx/scripts/update.sh, /usr/local/bin/nexus-update, /var/www/nexus-pbx/scripts/nexus-cluster
|
||||||
EOF
|
EOF
|
||||||
chmod 0440 /etc/sudoers.d/nexus-pbx
|
chmod 0440 /etc/sudoers.d/nexus-pbx
|
||||||
|
|
||||||
@@ -213,7 +217,7 @@ for fpm_svc in /lib/systemd/system/php*-fpm.service /usr/lib/systemd/system/php*
|
|||||||
cat << 'EOF_OVERRIDE' > "/etc/systemd/system/${svc_name}.d/override.conf"
|
cat << 'EOF_OVERRIDE' > "/etc/systemd/system/${svc_name}.d/override.conf"
|
||||||
[Service]
|
[Service]
|
||||||
ProtectSystem=false
|
ProtectSystem=false
|
||||||
ReadWritePaths=/etc/asterisk /etc/fail2ban /etc/nginx /var/log /var/lib/asterisk /var/spool/asterisk /var/www /usr/local/bin /etc/sudoers.d /etc/letsencrypt /var/lib/letsencrypt
|
ReadWritePaths=/etc/asterisk /etc/fail2ban /etc/nginx /var/log /var/lib/asterisk /var/spool/asterisk /var/www /usr/local/bin /etc/sudoers.d /etc/letsencrypt /var/lib/letsencrypt /etc/keepalived
|
||||||
EOF_OVERRIDE
|
EOF_OVERRIDE
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -483,10 +487,12 @@ chmod 0644 /etc/cron.d/nexus-backup
|
|||||||
touch /var/log/nexus_backup.log && chown www-data:asterisk /var/log/nexus_backup.log && chmod 664 /var/log/nexus_backup.log
|
touch /var/log/nexus_backup.log && chown www-data:asterisk /var/log/nexus_backup.log && chmod 664 /var/log/nexus_backup.log
|
||||||
|
|
||||||
# CLI symlinks
|
# CLI symlinks
|
||||||
chmod +x "$TARGET_DIR/scripts/nexus-firewall" "$TARGET_DIR/scripts/nexus-banner.sh" "$TARGET_DIR/scripts/update.sh"
|
chmod +x "$TARGET_DIR/scripts/nexus-firewall" "$TARGET_DIR/scripts/nexus-banner.sh" "$TARGET_DIR/scripts/update.sh" "$TARGET_DIR/scripts/nexus-cluster"
|
||||||
|
chmod +x "$TARGET_DIR"/scripts/cluster/*.sh 2>/dev/null || true
|
||||||
ln -sf "$TARGET_DIR/scripts/nexus-firewall" /usr/local/bin/nexus-firewall
|
ln -sf "$TARGET_DIR/scripts/nexus-firewall" /usr/local/bin/nexus-firewall
|
||||||
ln -sf "$TARGET_DIR/scripts/nexus-banner.sh" /usr/local/bin/nexus-banner
|
ln -sf "$TARGET_DIR/scripts/nexus-banner.sh" /usr/local/bin/nexus-banner
|
||||||
ln -sf "$TARGET_DIR/scripts/update.sh" /usr/local/bin/nexus-update
|
ln -sf "$TARGET_DIR/scripts/update.sh" /usr/local/bin/nexus-update
|
||||||
|
ln -sf "$TARGET_DIR/scripts/nexus-cluster" /usr/local/bin/nexus-cluster
|
||||||
|
|
||||||
# Bannière interactive lors de chaque connexion terminal (SSH / Console)
|
# Bannière interactive lors de chaque connexion terminal (SSH / Console)
|
||||||
cat << 'EOF' > /etc/profile.d/nexus-motd.sh
|
cat << 'EOF' > /etc/profile.d/nexus-motd.sh
|
||||||
|
|||||||
@@ -3,22 +3,22 @@
|
|||||||
# NexusPBX HA - Script de surveillance de santé d'Asterisk pour Keepalived VRRP
|
# NexusPBX HA - Script de surveillance de santé d'Asterisk pour Keepalived VRRP
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
|
||||||
|
|
||||||
# 1. Vérifie si le binaire et le démon asterisk tournent
|
# 1. Vérifie si le binaire et le démon asterisk tournent
|
||||||
if ! pidof asterisk >/dev/null 2>&1; then
|
if ! pidof asterisk >/dev/null 2>&1; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Teste si Asterisk répond aux requêtes AMI/CLI (core ping)
|
# 2. Teste si Asterisk répond aux requêtes CLI (core ping)
|
||||||
PING_RESP=$(asterisk -rx "core ping" 2>/dev/null || true)
|
PING_RESP=$(/usr/sbin/asterisk -rx "core ping" 2>/dev/null || true)
|
||||||
if echo "$PING_RESP" | grep -q "Pong"; then
|
if echo "$PING_RESP" | grep -q "Pong"; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. Tentative alternative via core show version
|
# 3. Tentative alternative via core show version
|
||||||
if asterisk -rx "core show version" >/dev/null 2>&1; then
|
if /usr/sbin/asterisk -rx "core show version" >/dev/null 2>&1; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
# NexusPBX HA - Notification Keepalived : Transition vers l'état BACKUP / STANDBY
|
# NexusPBX HA - Notification Keepalived : Transition vers l'état BACKUP / STANDBY
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
set -euo pipefail
|
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"
|
LOG_FILE="/var/log/nexus_cluster.log"
|
||||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
@@ -15,6 +16,13 @@ if [ -f "$DB_PATH" ]; then
|
|||||||
sqlite3 "$DB_PATH" "UPDATE cluster_settings SET last_sync_status='standby_idle', updated_at=CURRENT_TIMESTAMP WHERE id=1;" 2>/dev/null || true
|
sqlite3 "$DB_PATH" "UPDATE cluster_settings SET last_sync_status='standby_idle', updated_at=CURRENT_TIMESTAMP WHERE id=1;" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 2. Désenregistrement des Trunks PJSIP pour éviter les conflits d'appels entrants avec le Master
|
||||||
|
if pidof asterisk >/dev/null 2>&1; then
|
||||||
|
/usr/sbin/asterisk -rx "pjsip send unregister *" >> "$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 unregister $reg" >> "$LOG_FILE" 2>&1 || true
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
echo "[$TIMESTAMP] [CLUSTER] Mode Standby actif. En écoute des battements de cœur VRRP." >> "$LOG_FILE"
|
echo "[$TIMESTAMP] [CLUSTER] Mode Standby actif. En écoute des battements de cœur VRRP." >> "$LOG_FILE"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
# NexusPBX HA - Notification Keepalived : Transition vers l'état FAULT / ERREUR
|
# NexusPBX HA - Notification Keepalived : Transition vers l'état FAULT / ERREUR
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
set -euo pipefail
|
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"
|
LOG_FILE="/var/log/nexus_cluster.log"
|
||||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
@@ -15,4 +16,3 @@ if [ -f "$DB_PATH" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
# NexusPBX HA - Notification Keepalived : Transition vers l'état MASTER
|
# NexusPBX HA - Notification Keepalived : Transition vers l'état MASTER
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
set -euo pipefail
|
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"
|
LOG_FILE="/var/log/nexus_cluster.log"
|
||||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
@@ -17,8 +18,12 @@ fi
|
|||||||
|
|
||||||
# 2. Réactivation des enregistrements Trunks PJSIP et rechargement de la config
|
# 2. 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
|
||||||
asterisk -rx "pjsip reload" >> "$LOG_FILE" 2>&1 || true
|
/usr/sbin/asterisk -rx "pjsip reload" >> "$LOG_FILE" 2>&1 || true
|
||||||
asterisk -rx "dialplan 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
|
fi
|
||||||
|
|
||||||
# 3. Gratuitous ARP optionnel si arping est présent
|
# 3. Gratuitous ARP optionnel si arping est présent
|
||||||
@@ -30,4 +35,3 @@ 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"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ==============================================================================
|
||||||
|
# NexusPBX HA - Script Helper Root pour l'orchestration Keepalived
|
||||||
|
# ==============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
|
||||||
|
|
||||||
|
CMD="${1:-status}"
|
||||||
|
|
||||||
|
case "$CMD" in
|
||||||
|
apply)
|
||||||
|
# 1. Activation kernel sysctl pour liaison IP non-locale (indispensable HA)
|
||||||
|
sysctl -w net.ipv4.ip_nonlocal_bind=1 >/dev/null 2>&1 || true
|
||||||
|
if [ ! -f /etc/sysctl.d/99-nexus-ha.conf ]; then
|
||||||
|
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-nexus-ha.conf 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Vérification et installation éventuelle de Keepalived
|
||||||
|
if ! command -v keepalived >/dev/null 2>&1; then
|
||||||
|
apt-get update -qq && apt-get install -y -qq keepalived >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Création et sécurisation stricte du dossier des scripts Keepalived (exigences strictes Keepalived 2.x)
|
||||||
|
mkdir -p /etc/keepalived/scripts
|
||||||
|
if [ -d "/var/www/nexus-pbx/scripts/cluster" ]; then
|
||||||
|
cp -f /var/www/nexus-pbx/scripts/cluster/*.sh /etc/keepalived/scripts/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
chown -R root:root /etc/keepalived
|
||||||
|
chmod 755 /etc/keepalived
|
||||||
|
chmod 755 /etc/keepalived/scripts
|
||||||
|
chmod 755 /etc/keepalived/scripts/*.sh 2>/dev/null || true
|
||||||
|
|
||||||
|
# 4. Déploiement de la configuration générée depuis /tmp
|
||||||
|
if [ -f "/tmp/keepalived.conf.tmp" ]; then
|
||||||
|
cp -f /tmp/keepalived.conf.tmp /etc/keepalived/keepalived.conf
|
||||||
|
chown root:root /etc/keepalived/keepalived.conf
|
||||||
|
chmod 644 /etc/keepalived/keepalived.conf
|
||||||
|
rm -f /tmp/keepalived.conf.tmp
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fichier journal du cluster accessible en écriture
|
||||||
|
touch /var/log/nexus_cluster.log
|
||||||
|
chmod 666 /var/log/nexus_cluster.log
|
||||||
|
|
||||||
|
# 5. Activation et démarrage du service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl unmask keepalived >/dev/null 2>&1 || true
|
||||||
|
systemctl enable keepalived >/dev/null 2>&1 || true
|
||||||
|
systemctl restart keepalived
|
||||||
|
echo "KEEPALIVED_APPLIED_OK"
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
systemctl stop keepalived >/dev/null 2>&1 || true
|
||||||
|
systemctl disable keepalived >/dev/null 2>&1 || true
|
||||||
|
echo "KEEPALIVED_STOPPED_OK"
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
if systemctl is-active --quiet keepalived 2>/dev/null; then
|
||||||
|
echo "RUNNING"
|
||||||
|
else
|
||||||
|
echo "STOPPED"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
check)
|
||||||
|
if pidof asterisk >/dev/null 2>&1 && /usr/sbin/asterisk -rx "core ping" 2>/dev/null | grep -q "Pong"; then
|
||||||
|
echo "HEALTHY"
|
||||||
|
else
|
||||||
|
echo "DEGRADED"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {apply|stop|status|check}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
+181
-58
@@ -16,6 +16,7 @@ class ClusterManager
|
|||||||
private PDO $db;
|
private PDO $db;
|
||||||
public const KEEPALIVED_CONF_PATH = '/etc/keepalived/keepalived.conf';
|
public const KEEPALIVED_CONF_PATH = '/etc/keepalived/keepalived.conf';
|
||||||
public const CLUSTER_LOG_PATH = '/var/log/nexus_cluster.log';
|
public const CLUSTER_LOG_PATH = '/var/log/nexus_cluster.log';
|
||||||
|
public const CLUSTER_HELPER_PATH = '/var/www/nexus-pbx/scripts/nexus-cluster';
|
||||||
|
|
||||||
public function __construct(?PDO $db = null)
|
public function __construct(?PDO $db = null)
|
||||||
{
|
{
|
||||||
@@ -75,6 +76,22 @@ class ClusterManager
|
|||||||
throw new InvalidArgumentException("L'adresse IP Virtuelle partagée (VIP) est requise pour activer le cluster HA.");
|
throw new InvalidArgumentException("L'adresse IP Virtuelle partagée (VIP) est requise pour activer le cluster HA.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empêcher l'utilisateur de configurer l'IP locale de ce serveur comme IP du pair
|
||||||
|
if ($peerIp !== '') {
|
||||||
|
$localInterfaces = $this->detectNetworkInterfaces();
|
||||||
|
foreach ($localInterfaces as $ifaceInfo) {
|
||||||
|
if (($ifaceInfo['primary'] ?? '') === $peerIp) {
|
||||||
|
throw new InvalidArgumentException("L'adresse IP du pair ({$peerIp}) correspond à l'adresse locale de cette machine ! Veuillez renseigner l'IP du second nœud.");
|
||||||
|
}
|
||||||
|
foreach ($ifaceInfo['ips'] ?? [] as $cidr) {
|
||||||
|
$ipOnly = explode('/', $cidr)[0] ?? '';
|
||||||
|
if ($ipOnly === $peerIp) {
|
||||||
|
throw new InvalidArgumentException("L'adresse IP du pair ({$peerIp}) correspond à l'adresse locale de cette machine ! Veuillez renseigner l'IP du second nœud.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$stmt = $this->db->prepare('
|
$stmt = $this->db->prepare('
|
||||||
UPDATE cluster_settings SET
|
UPDATE cluster_settings SET
|
||||||
enabled = :enabled,
|
enabled = :enabled,
|
||||||
@@ -106,12 +123,12 @@ class ClusterManager
|
|||||||
':auto_sync' => $autoSync,
|
':auto_sync' => $autoSync,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Application automatique de la configuration Keepalived si demandé
|
// Application automatique de la configuration Keepalived
|
||||||
$applyResult = $this->applyKeepalivedConfig();
|
$applyResult = $this->applyKeepalivedConfig();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Configuration Haute Disponibilité enregistrée.',
|
'message' => 'Configuration Haute Disponibilité enregistrée et appliquée.',
|
||||||
'apply_result' => $applyResult,
|
'apply_result' => $applyResult,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -129,14 +146,13 @@ class ClusterManager
|
|||||||
if ($line === '') {
|
if ($line === '') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Format typique ip -o: "2: eth0 inet 10.10.0.4/16 brd 10.10.255.255 scope global eth0\..."
|
|
||||||
if (preg_match('/^\d+:\s+([a-zA-Z0-9_\-\.]+)\s+inet\s+([0-9.]+)\/(\d+)/', $line, $matches)) {
|
if (preg_match('/^\d+:\s+([a-zA-Z0-9_\-\.]+)\s+inet\s+([0-9.]+)\/(\d+)/', $line, $matches)) {
|
||||||
$iface = $matches[1];
|
$iface = $matches[1];
|
||||||
$ip = $matches[2];
|
$ip = $matches[2];
|
||||||
$mask = $matches[3];
|
$mask = $matches[3];
|
||||||
|
|
||||||
if ($iface === 'lo') {
|
if ($iface === 'lo') {
|
||||||
continue; // Ignorer loopback
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($interfaces[$iface])) {
|
if (!isset($interfaces[$iface])) {
|
||||||
@@ -162,9 +178,13 @@ class ClusterManager
|
|||||||
if ($vip === '') {
|
if ($vip === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$escapedVip = escapeshellarg($vip);
|
$output = shell_exec('ip -o -4 addr show 2>/dev/null') ?: '';
|
||||||
$output = shell_exec("ip addr show 2>/dev/null | grep -F " . $escapedVip) ?: '';
|
foreach (preg_split('/\r?\n/', $output) ?: [] as $line) {
|
||||||
return trim($output) !== '';
|
if (preg_match('/\sinet\s+' . preg_quote($vip, '/') . '\//', $line)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,19 +197,23 @@ class ClusterManager
|
|||||||
$peerIp = $settings['peer_ip'] ?? '';
|
$peerIp = $settings['peer_ip'] ?? '';
|
||||||
$isVipActive = $this->isVirtualIpActive($vip);
|
$isVipActive = $this->isVirtualIpActive($vip);
|
||||||
|
|
||||||
// 1. Vérification de Keepalived
|
// 1. Statut Keepalived
|
||||||
$keepalivedInstalled = !empty(shell_exec('command -v keepalived 2>/dev/null'));
|
$keepalivedInstalled = !empty(shell_exec('command -v keepalived 2>/dev/null'));
|
||||||
$keepalivedRunning = false;
|
$keepalivedRunning = false;
|
||||||
if ($keepalivedInstalled) {
|
if ($keepalivedInstalled) {
|
||||||
$psOut = shell_exec('pidof keepalived 2>/dev/null') ?: '';
|
$helperStatus = trim((string)shell_exec('sudo -n ' . escapeshellcmd(self::CLUSTER_HELPER_PATH) . ' status 2>/dev/null'));
|
||||||
$keepalivedRunning = trim($psOut) !== '';
|
if ($helperStatus === 'RUNNING' || trim((string)shell_exec('pidof keepalived 2>/dev/null')) !== '') {
|
||||||
|
$keepalivedRunning = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Vérification d'Asterisk
|
// 2. Statut Asterisk
|
||||||
$asteriskRunning = trim((string)shell_exec('pidof asterisk 2>/dev/null')) !== '';
|
$asteriskRunning = trim((string)shell_exec('pidof asterisk 2>/dev/null')) !== '';
|
||||||
$asteriskPing = false;
|
$asteriskPing = false;
|
||||||
if ($asteriskRunning) {
|
if ($asteriskRunning) {
|
||||||
$pingOut = shell_exec("asterisk -rx 'core ping' 2>/dev/null") ?: '';
|
$pingOut = shell_exec("asterisk -rx 'core ping' 2>/dev/null")
|
||||||
|
?: shell_exec("sudo -n /usr/sbin/asterisk -rx 'core ping' 2>/dev/null")
|
||||||
|
?: '';
|
||||||
$asteriskPing = str_contains($pingOut, 'Pong');
|
$asteriskPing = str_contains($pingOut, 'Pong');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +231,7 @@ class ClusterManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rôle réel effectif
|
// 4. Rôle effectif
|
||||||
$effectiveState = 'STANDALONE';
|
$effectiveState = 'STANDALONE';
|
||||||
if (!empty($settings['enabled'])) {
|
if (!empty($settings['enabled'])) {
|
||||||
if ($isVipActive) {
|
if ($isVipActive) {
|
||||||
@@ -217,7 +241,7 @@ class ClusterManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dernières lignes du log cluster
|
// 5. Dernières lignes de journalisation HA
|
||||||
$recentLogs = [];
|
$recentLogs = [];
|
||||||
if (file_exists(self::CLUSTER_LOG_PATH)) {
|
if (file_exists(self::CLUSTER_LOG_PATH)) {
|
||||||
$lines = file(self::CLUSTER_LOG_PATH, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
|
$lines = file(self::CLUSTER_LOG_PATH, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
|
||||||
@@ -256,10 +280,20 @@ class ClusterManager
|
|||||||
$iface = $settings['interface'] ?? 'eth0';
|
$iface = $settings['interface'] ?? 'eth0';
|
||||||
$vip = $settings['virtual_ip'] ?? '';
|
$vip = $settings['virtual_ip'] ?? '';
|
||||||
$vmask = $settings['virtual_netmask'] ?? '24';
|
$vmask = $settings['virtual_netmask'] ?? '24';
|
||||||
|
$peerIp = trim((string)($settings['peer_ip'] ?? ''));
|
||||||
$vrid = (int)($settings['vrid'] ?? 51);
|
$vrid = (int)($settings['vrid'] ?? 51);
|
||||||
$prio = (int)($settings['priority'] ?? ($role === 'MASTER' ? 100 : 90));
|
$prio = (int)($settings['priority'] ?? ($role === 'MASTER' ? 100 : 90));
|
||||||
$secret = substr($settings['cluster_secret'] ?? 'NexusPBX', 0, 8); // Keepalived max 8 chars auth_pass
|
$secret = substr($settings['cluster_secret'] ?? 'NexusPBX', 0, 8); // Keepalived max 8 chars auth_pass
|
||||||
|
|
||||||
|
// Détection de l'IP locale sur l'interface pour unicast VRRP
|
||||||
|
$localIp = '';
|
||||||
|
foreach ($this->detectNetworkInterfaces() as $net) {
|
||||||
|
if ($net['name'] === $iface) {
|
||||||
|
$localIp = $net['primary'] ?? '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$conf = [];
|
$conf = [];
|
||||||
$conf[] = "! ==============================================================================";
|
$conf[] = "! ==============================================================================";
|
||||||
$conf[] = "! Configuration Keepalived générée automatiquement par NexusPBX HA";
|
$conf[] = "! Configuration Keepalived générée automatiquement par NexusPBX HA";
|
||||||
@@ -273,7 +307,7 @@ class ClusterManager
|
|||||||
$conf[] = "}";
|
$conf[] = "}";
|
||||||
$conf[] = "";
|
$conf[] = "";
|
||||||
$conf[] = "vrrp_script chk_asterisk {";
|
$conf[] = "vrrp_script chk_asterisk {";
|
||||||
$conf[] = " script \"/var/www/nexus-pbx/scripts/cluster/check_asterisk.sh\"";
|
$conf[] = " script \"/etc/keepalived/scripts/check_asterisk.sh\"";
|
||||||
$conf[] = " interval 2";
|
$conf[] = " interval 2";
|
||||||
$conf[] = " weight 2";
|
$conf[] = " weight 2";
|
||||||
$conf[] = " fall 2";
|
$conf[] = " fall 2";
|
||||||
@@ -287,6 +321,16 @@ class ClusterManager
|
|||||||
$conf[] = " priority {$prio}";
|
$conf[] = " priority {$prio}";
|
||||||
$conf[] = " advert_int 1";
|
$conf[] = " advert_int 1";
|
||||||
$conf[] = "";
|
$conf[] = "";
|
||||||
|
|
||||||
|
// Unicast VRRP si l'IP du nœud pair est configurée (indispensable en environnement virtualisé type Proxmox/VMware)
|
||||||
|
if ($peerIp !== '' && $localIp !== '') {
|
||||||
|
$conf[] = " unicast_src_ip {$localIp}";
|
||||||
|
$conf[] = " unicast_peer {";
|
||||||
|
$conf[] = " {$peerIp}";
|
||||||
|
$conf[] = " }";
|
||||||
|
$conf[] = "";
|
||||||
|
}
|
||||||
|
|
||||||
$conf[] = " authentication {";
|
$conf[] = " authentication {";
|
||||||
$conf[] = " auth_type PASS";
|
$conf[] = " auth_type PASS";
|
||||||
$conf[] = " auth_pass {$secret}";
|
$conf[] = " auth_pass {$secret}";
|
||||||
@@ -300,9 +344,9 @@ class ClusterManager
|
|||||||
$conf[] = " chk_asterisk";
|
$conf[] = " chk_asterisk";
|
||||||
$conf[] = " }";
|
$conf[] = " }";
|
||||||
$conf[] = "";
|
$conf[] = "";
|
||||||
$conf[] = " notify_master \"/var/www/nexus-pbx/scripts/cluster/notify_master.sh\"";
|
$conf[] = " notify_master \"/etc/keepalived/scripts/notify_master.sh\"";
|
||||||
$conf[] = " notify_backup \"/var/www/nexus-pbx/scripts/cluster/notify_backup.sh\"";
|
$conf[] = " notify_backup \"/etc/keepalived/scripts/notify_backup.sh\"";
|
||||||
$conf[] = " notify_fault \"/var/www/nexus-pbx/scripts/cluster/notify_fault.sh\"";
|
$conf[] = " notify_fault \"/etc/keepalived/scripts/notify_fault.sh\"";
|
||||||
$conf[] = "}";
|
$conf[] = "}";
|
||||||
$conf[] = "";
|
$conf[] = "";
|
||||||
|
|
||||||
@@ -310,7 +354,7 @@ class ClusterManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applique la configuration Keepalived sur le système Linux
|
* Applique la configuration Keepalived sur le système Linux via le helper root
|
||||||
*/
|
*/
|
||||||
public function applyKeepalivedConfig(): array
|
public function applyKeepalivedConfig(): array
|
||||||
{
|
{
|
||||||
@@ -318,9 +362,7 @@ class ClusterManager
|
|||||||
$enabled = !empty($settings['enabled']);
|
$enabled = !empty($settings['enabled']);
|
||||||
|
|
||||||
if (!$enabled) {
|
if (!$enabled) {
|
||||||
// Désactivation du service Keepalived si le cluster est désactivé
|
shell_exec('sudo -n ' . escapeshellcmd(self::CLUSTER_HELPER_PATH) . ' stop 2>&1');
|
||||||
shell_exec('systemctl stop keepalived 2>/dev/null || true');
|
|
||||||
shell_exec('systemctl disable keepalived 2>/dev/null || true');
|
|
||||||
return [
|
return [
|
||||||
'status' => 'disabled',
|
'status' => 'disabled',
|
||||||
'message' => 'Cluster HA désactivé (service Keepalived stoppé).',
|
'message' => 'Cluster HA désactivé (service Keepalived stoppé).',
|
||||||
@@ -335,23 +377,21 @@ class ClusterManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
$confContent = $this->generateKeepalivedConfig();
|
$confContent = $this->generateKeepalivedConfig();
|
||||||
if (!is_dir('/etc/keepalived')) {
|
file_put_contents('/tmp/keepalived.conf.tmp', $confContent);
|
||||||
mkdir('/etc/keepalived', 0755, true);
|
chmod('/tmp/keepalived.conf.tmp', 0644);
|
||||||
|
|
||||||
|
$out = trim((string)shell_exec('sudo -n ' . escapeshellcmd(self::CLUSTER_HELPER_PATH) . ' apply 2>&1'));
|
||||||
|
|
||||||
|
if (!str_contains($out, 'KEEPALIVED_APPLIED_OK')) {
|
||||||
|
return [
|
||||||
|
'status' => 'warning',
|
||||||
|
'message' => 'Configuration transmise mais le démarrage a retourné : ' . ($out ?: 'Erreur inconnue'),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents(self::KEEPALIVED_CONF_PATH, $confContent);
|
|
||||||
chmod(self::KEEPALIVED_CONF_PATH, 0600);
|
|
||||||
|
|
||||||
// S'assure que les scripts de cluster sont bien exécutables
|
|
||||||
shell_exec('chmod +x /var/www/nexus-pbx/scripts/cluster/*.sh 2>/dev/null || true');
|
|
||||||
|
|
||||||
// Activation et rechargement / démarrage de Keepalived
|
|
||||||
shell_exec('systemctl enable keepalived 2>/dev/null || true');
|
|
||||||
shell_exec('systemctl restart keepalived 2>/dev/null || true');
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
'message' => 'Configuration Keepalived générée et service redémarré avec succès.',
|
'message' => 'Configuration Keepalived générée et service activé avec succès.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,13 +408,24 @@ class ClusterManager
|
|||||||
throw new RuntimeException("L'adresse IP du Nœud pair (Peer IP) n'est pas configurée.");
|
throw new RuntimeException("L'adresse IP du Nœud pair (Peer IP) n'est pas configurée.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Création du paquet de données en mémoire ou fichier temporaire
|
// Port d'administration HTTPS configuré
|
||||||
|
$adminPort = 8443;
|
||||||
|
try {
|
||||||
|
$st = $this->db->query("SELECT admin_https_port FROM settings WHERE id = 1")->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (!empty($st['admin_https_port'])) {
|
||||||
|
$adminPort = (int)$st['admin_https_port'];
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$adminPort = 8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Création de l'archive de réplication
|
||||||
$dbPath = Database::getDatabasePath();
|
$dbPath = Database::getDatabasePath();
|
||||||
if (!file_exists($dbPath)) {
|
if (!file_exists($dbPath)) {
|
||||||
throw new RuntimeException("Fichier de base de données introuvable : {$dbPath}");
|
throw new RuntimeException("Fichier de base de données introuvable : {$dbPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forcer le checkpoint SQLite WAL pour avoir un dump 100% à jour
|
// Forcer le checkpoint SQLite WAL pour un dump intègre
|
||||||
$this->db->exec('PRAGMA wal_checkpoint(FULL);');
|
$this->db->exec('PRAGMA wal_checkpoint(FULL);');
|
||||||
|
|
||||||
$tmpArchive = '/tmp/nexus_cluster_sync_' . time() . '.tar.gz';
|
$tmpArchive = '/tmp/nexus_cluster_sync_' . time() . '.tar.gz';
|
||||||
@@ -397,35 +448,59 @@ class ClusterManager
|
|||||||
'archive_b64' => base64_encode($archiveData),
|
'archive_b64' => base64_encode($archiveData),
|
||||||
];
|
];
|
||||||
|
|
||||||
$signature = hash_hmac('sha256', json_encode($payload), $secret);
|
$jsonPayload = json_encode($payload);
|
||||||
|
$signature = hash_hmac('sha256', $jsonPayload, $secret);
|
||||||
|
|
||||||
$ch = curl_init("http://{$peerIp}/api.php?action=cluster_receive_sync");
|
// URLs d'accès candidates vers l'API du second nœud
|
||||||
|
$candidateUrls = [
|
||||||
|
"https://{$peerIp}:{$adminPort}/api.php?action=cluster_receive_sync",
|
||||||
|
];
|
||||||
|
if ($adminPort !== 443) {
|
||||||
|
$candidateUrls[] = "https://{$peerIp}/api.php?action=cluster_receive_sync";
|
||||||
|
}
|
||||||
|
if ($adminPort !== 8443) {
|
||||||
|
$candidateUrls[] = "https://{$peerIp}:8443/api.php?action=cluster_receive_sync";
|
||||||
|
}
|
||||||
|
$candidateUrls[] = "http://{$peerIp}/api.php?action=cluster_receive_sync";
|
||||||
|
$candidateUrls = array_values(array_unique($candidateUrls));
|
||||||
|
|
||||||
|
$lastError = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
foreach ($candidateUrls as $targetUrl) {
|
||||||
|
$ch = curl_init($targetUrl);
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload);
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
'Content-Type: application/json',
|
'Content-Type: application/json',
|
||||||
'X-Nexus-Cluster-Signature: ' . $signature,
|
'X-Nexus-Cluster-Signature: ' . $signature,
|
||||||
]);
|
]);
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 4);
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
$err = curl_error($ch);
|
$err = curl_error($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
if ($httpCode !== 200 || !empty($err)) {
|
if ($httpCode === 200 && !empty($response)) {
|
||||||
$errorMsg = "Échec d'envoi vers le pair {$peerIp} (HTTP {$httpCode}) : {$err}";
|
$resJson = json_decode((string)$response, true);
|
||||||
$this->updateSyncStatus('error', $errorMsg);
|
if (!empty($resJson['success'])) {
|
||||||
throw new RuntimeException($errorMsg);
|
$success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$lastError = "Rejet par le pair ({$targetUrl}) : " . ($resJson['message'] ?? $resJson['error'] ?? 'Erreur inconnue');
|
||||||
|
} else {
|
||||||
|
$lastError = "Échec d'envoi vers {$targetUrl} (HTTP {$httpCode})" . ($err ? " : {$err}" : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$resJson = json_decode((string)$response, true);
|
if (!$success) {
|
||||||
if (empty($resJson['success'])) {
|
$this->updateSyncStatus('error', $lastError);
|
||||||
$errorMsg = "Le pair a rejeté la réplication : " . ($resJson['error'] ?? 'Erreur inconnue');
|
throw new RuntimeException($lastError);
|
||||||
$this->updateSyncStatus('error', $errorMsg);
|
|
||||||
throw new RuntimeException($errorMsg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateSyncStatus('success', "Synchronisation réussie avec le nœud {$peerIp}");
|
$this->updateSyncStatus('success', "Synchronisation réussie avec le nœud {$peerIp}");
|
||||||
@@ -463,13 +538,20 @@ class ClusterManager
|
|||||||
$tmpArchive = '/tmp/nexus_cluster_recv_' . time() . '.tar.gz';
|
$tmpArchive = '/tmp/nexus_cluster_recv_' . time() . '.tar.gz';
|
||||||
file_put_contents($tmpArchive, $archiveRaw);
|
file_put_contents($tmpArchive, $archiveRaw);
|
||||||
|
|
||||||
// Sauvegarde d'urgence de la DB actuelle avant écrasement
|
// Sauvegarde d'urgence de la base actuelle avant écrasement
|
||||||
$dbPath = Database::getDatabasePath();
|
$dbPath = Database::getDatabasePath();
|
||||||
if (file_exists($dbPath)) {
|
if (file_exists($dbPath)) {
|
||||||
copy($dbPath, $dbPath . '.pre_cluster_sync.bak');
|
copy($dbPath, $dbPath . '.pre_cluster_sync.bak');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fermeture de la connexion PDO pour libérer les verrous SQLite
|
// Sauvegarder la configuration locale cluster et réseau pour ne pas l'écraser par celle du master
|
||||||
|
$localClusterSettings = $settings;
|
||||||
|
$localSettingsRow = null;
|
||||||
|
try {
|
||||||
|
$localSettingsRow = $this->db->query("SELECT local_net, local_networks_json, admin_https_port FROM settings WHERE id = 1")->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (\Throwable $e) {}
|
||||||
|
|
||||||
|
// Libérer les verrous PDO SQLite
|
||||||
Database::resetConnection();
|
Database::resetConnection();
|
||||||
|
|
||||||
// Extraction de l'archive
|
// Extraction de l'archive
|
||||||
@@ -479,18 +561,60 @@ class ClusterManager
|
|||||||
}
|
}
|
||||||
@unlink($tmpArchive);
|
@unlink($tmpArchive);
|
||||||
|
|
||||||
// Réouverture de la connexion DB et régénération des configs Asterisk
|
// Réouverture de la connexion DB
|
||||||
$newDb = Database::getConnection();
|
$newDb = Database::getConnection();
|
||||||
$this->db = $newDb;
|
$this->db = $newDb;
|
||||||
|
|
||||||
// Préserver le rôle local configuré (pour ne pas qu'un master force l'esclave à se croire master)
|
// Restauration de l'identité cluster et réseau propre à CE serveur
|
||||||
$localRole = $settings['role'] ?? 'backup';
|
$updateStmt = $newDb->prepare('
|
||||||
$newDb->exec("UPDATE cluster_settings SET role = '{$localRole}', last_sync_at = CURRENT_TIMESTAMP, last_sync_status = 'synced', last_sync_message = 'Base et sons synchronisés depuis le Master' WHERE id = 1");
|
UPDATE cluster_settings SET
|
||||||
|
role = :role,
|
||||||
|
interface = :iface,
|
||||||
|
peer_ip = :peer_ip,
|
||||||
|
priority = :priority,
|
||||||
|
virtual_ip = :virtual_ip,
|
||||||
|
virtual_netmask = :virtual_netmask,
|
||||||
|
vrid = :vrid,
|
||||||
|
last_sync_at = CURRENT_TIMESTAMP,
|
||||||
|
last_sync_status = :status,
|
||||||
|
last_sync_message = :msg
|
||||||
|
WHERE id = 1
|
||||||
|
');
|
||||||
|
$updateStmt->execute([
|
||||||
|
':role' => $localClusterSettings['role'] ?? 'backup',
|
||||||
|
':iface' => $localClusterSettings['interface'] ?? 'eth0',
|
||||||
|
':peer_ip' => $localClusterSettings['peer_ip'] ?? '',
|
||||||
|
':priority' => $localClusterSettings['priority'] ?? 90,
|
||||||
|
':virtual_ip' => $localClusterSettings['virtual_ip'] ?? '',
|
||||||
|
':virtual_netmask' => $localClusterSettings['virtual_netmask'] ?? '24',
|
||||||
|
':vrid' => $localClusterSettings['vrid'] ?? 51,
|
||||||
|
':status' => 'synced',
|
||||||
|
':msg' => 'Base et sons synchronisés depuis le Master',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($localSettingsRow) {
|
||||||
|
$newDb->prepare('
|
||||||
|
UPDATE settings SET
|
||||||
|
local_net = :net,
|
||||||
|
local_networks_json = :nets,
|
||||||
|
admin_https_port = :port
|
||||||
|
WHERE id = 1
|
||||||
|
')->execute([
|
||||||
|
':net' => $localSettingsRow['local_net'] ?? '192.168.1.0/24',
|
||||||
|
':nets' => $localSettingsRow['local_networks_json'] ?? '["192.168.1.0/24"]',
|
||||||
|
':port' => $localSettingsRow['admin_https_port'] ?? 8443,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Régénérer les configurations PJSIP et Asterisk
|
// Régénérer les configurations PJSIP et Asterisk
|
||||||
$generator = new ConfigGenerator($newDb);
|
$generator = new ConfigGenerator($newDb);
|
||||||
$generator->generateAndApply();
|
$generator->generateAndApply();
|
||||||
|
|
||||||
|
// Si le cluster est actif, actualiser le service Keepalived
|
||||||
|
if (!empty($localClusterSettings['enabled'])) {
|
||||||
|
$this->applyKeepalivedConfig();
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Base de données et fichiers audio appliqués avec succès.',
|
'message' => 'Base de données et fichiers audio appliqués avec succès.',
|
||||||
@@ -525,4 +649,3 @@ class ClusterManager
|
|||||||
$stmt->execute([':status' => $status, ':msg' => $message]);
|
$stmt->execute([':status' => $status, ':msg' => $message]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user