Freepbx отказоустойчивый кластер, бесплатно

Для реалезации отказоустойчевого кластера на Freepbx 16.0.40 будем использовать CARP протокол. Устанавливаем два сервера Freepbx на которых будет настроен отказоустойчивый кластер.
Freepbx01(Master) - 192.168.98.214
Freepbx02(Slave) - 192.168.98.215
HA(общий адрес) - 192.168.98.14
Устанавливаем CARP на оба сервера:
yum install ucarp
Редактируем файлы конфигурации:
Freepbx01(Master) - 192.168.98.214
vi /etc/ucarp/vip-001.conf
VIP_ADDRESS="192.168.98.14"
ID=001
PASSFILE=/etc/ucarp/vip-001.pwd
BIND_INTERFACE="eth0"
SOURCE_ADDRESS="192.168.98.214"
OPTIONS="-k 2 --shutdown --preempt"
в файле /etc/ucarp/vip-001.pwd указывыем свой пароль (любой)
в файле vip-common.conf все коментируем
- Повторяем все тоже самое на втором сервере
Freepbx02(Slave) - 192.168.98.215
vi /etc/ucarp/vip-001.conf
VIP_ADDRESS="192.168.98.14"
ID=001
PASSFILE=/etc/ucarp/vip-001.pwd
BIND_INTERFACE="eth0"
SOURCE_ADDRESS="192.168.98.215"
OPTIONS="-k 10 --shutdown"
- Запускаем службу и добавляем в автозагрузку:
systemctl enable ucarp@001.service
systemctl start ucarp@001.service
systemctl daemon-reload
- Проверяем работоспособность Carp, вводим
ip a
Carp интерфейс появился 192.168.98.14. Также добавляю скрипт который будет мониторить состояние сервиса asterisk, и если по какой то причине он не работает, то интерфейс CARP будет отключен, соответственно все устройства переподключатся на резервный сервер, так как он станем Мастером.
vi /home/script/sevrice_up_down.sh
#!/bin/bash
while true; do
if ps -C asterisk >/dev/null 2>&1; then
sleep 10
else
sleep 10
if ps -C asterisk >/dev/null 2>&1; then
systemctl start ucarp@001.service
sleep 10
fwconsole reload
else
systemctl stop ucarp@001.service
fi
fi
done
У меня возникла проблема, при переходе на резервный сервер транк не всегда подымался. Решалось командой fwconsole reload. По этой причине добавил в скрипт.
Также на одной из АТС у меня был SIP Trunk от провайдера. Так как провайдер ограничил нас одной регестрацией, добавляю в скрипт отключение транка в зависимости от статуса CARP IP.
#!/bin/bash
# Переменная для хранения предыдущего статуса IP-адреса
previous_ip_status="absent"
while true; do
# Проверка наличия процесса asterisk
if ps -C asterisk >/dev/null 2>&1; then
sleep 10
else
sleep 10
# Если процесс asterisk не запущен, попытка его запуска
if ps -C asterisk >/dev/null 2>&1; then
systemctl start ucarp@001.service
sleep 10
fwconsole reload
else
systemctl stop ucarp@001.service
fi
fi
# Проверка наличия IP-адреса 192.168.98.14
if ip a | grep '192.168.98.14'; then
current_ip_status="present"
else
current_ip_status="absent"
fi
# Управление транком в зависимости от статуса IP
if [ "$current_ip_status" = "present" ]; then
fwconsole trunk --enable 1
else
fwconsole trunk --disable 1
fi
# Если статус IP изменился, выполнить reload
if [ "$previous_ip_status" != "$current_ip_status" ]; then
previous_ip_status="$current_ip_status"
sleep 5
fwconsole reload
fi
done
Делаем файл исполняемым:
chmod 777 /home/script/sevrice_up_down.shСоздаю службу автозапуска скрипта:
vi /etc/systemd/system/sevrice_up_down.service
[Unit]
Description=sevrice_up_down
After=network.target
[Service]
User=root
ExecStart=/home/script/sevrice_up_down.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
sudo systemctl enable sevrice_up_down.service
sudo systemctl start sevrice_up_down.service
Сервис успешно запущен. Можно попробовать остановить Asterisk командой fwconsole stop , при этом ip carp должен перейти на ре зервный сервер. На этом с карпом мы закончили.
Настраеваем Freepbx для синхронизации между собой.
Первым делом нужно настроить доступ SSH. Для этого переходим на сервер Freepbx01(Master) - 192.168.98.214 и выполняем:
sudo -u asterisk ssh-copy-id -i /home/asterisk/.ssh/id_rsa.pub root@SecondaryServer
и введите пароль при появлении запроса.
Если эта команда выполнена без ошибок, вы готовы к тестированию:
В командной строке введите:
ssh -i /home/asterisk/.ssh/id_rsa root@SecondaryServerIPЕсли все прошло хорошо, вы должны войти на дополнительный сервер.
Настройка модуля хранилища файлов
После завершения настройки SSH нам нужно настроить модуль Filestore для SSH.
Нажав «Добавить SSH-сервер», вы можете добавить свой SSH-сервер для хранения резервной копии. Всякий раз, когда вы запускаете резервное копирование с сервером SSH, установленным в качестве местоположения хранилища файлов, файл резервной копии будет передан на путь к серверам SSH, который вы добавили в хранилище файлов. Если резервное копирование по какой-то причине не удалось или не удалось подключиться к SSH-серверу, резервная копия будет сохранена в локальной системе (/var/spool/asterisk/backup/имя_резервной_копии/файл_резервной копии).
Переходим на Freepbx02(Slave) - 192.168.98.215 и идем в раздел Connectivity - API, нам нужно сгенерировать ключи.
Нажимаем Machine-to-Machine app, в окне вводим Your App Name, Description и Allowed Scopes он должен быть:
gql:backup:write
Генерируются ключи и сохраняем их в свой файлик *.txt.
Переходим на каждый из серверов Freepbx и идем в раздел Settings-File-Local
Добавляем новое месторасположения бекапов:
_ASTSPOOLDIR_/backup
- Переходим на
Freepbx01(Master) - 192.168.98.214и идем в раздел Store Admin-Backup & Restore там создаем новый бекап.
Даем имя бекапу, выбираем модули которые хотите бекапить. Важно отключить копирование двух модулей:
Backup & Restore
System Admin
В разделе Custom Files можно указать копирование дополнительных каталогов, например tftpboot если используется. Также настраивается уведомленя по почте. В разделе Storage Location указываем ранее созданое место для бекапов. Настраиваем Schedule and Maintenance по своим нуждам.
И теперь самы важный раздел, Warm Spare в котором мы должны ввести данные которые сохраняли в блокноте.
Сохраняем и запускаем бекап. После успешного переноса бекапа на второй сервер, вы должны увидеть сообщение.
Доработака Freepbx (Sip&IAX2)
Так как у нас будет использоватся Carp ip адрес как основной нужно до настроить Sip а у кого используется IAX2 то и его тоже.
Переходим в Settings- Asterisk SIP Settings и указываем в качестве External Address адрес вашого CARP. В Local Networks указываем подсети с которых будет проводится регистрация sip клиентов.
Для IAX2 переходим в транк и в Outgoing нужно дописать**:**
sourceaddress=(Carp ip address) например sourceaddress=192.168.98.14Теперь наш транк будет отбащатся через адрес 192.168.98.14
Мы получили полностью отказоустойчивую АТС бесплатно.
######################
Обновление Freepbx
yum upgrade -y
fwconsole ma upgradeall
fwconsole chown
fwconsole reload
fwconsole chown
fwconsole reload
fwconsole versionupgrade --check
fwconsole versionupgrade --upgrade
fwconsole chown
fwconsole reload
reboot or fwconsole restart
Дополнительные настройки.
Zabbix
rpm -Uvh https://repo.zabbix.com/zabbix/6.0/rhel/7/x86_64/zabbix-release-6.0-4.el7.noarch.rpm
yum install zabbix-agent2
systemctl restart zabbix-agent2
systemctl enable zabbix-agent2
Скопировть конфиг с старого сервера
Tftp
sudo vi /etc/selinux/config
SELINUX=disabled
sudo yum install tftp tftp-server xinetd
sudo systemctl enable xinetd
sudo systemctl enable tftp
sudo systemctl restart xinetd
Vpn Up Down
Для выполнения (спец) скриптов от пользователя asterisk может понадобится интерпритатор.
yum install expect
проверяем
su - asterisk
Для некоторых скриптов добавляю права доступа в mcedit /etc/sudoers
root<-->ALL=(ALL) <---->ALL
pbx<------>ALL=(ALL)<----->ALL
zabbix<><------>ALL=NOPASSWD: /usr/sbin/asterisk
asterisk<------>ALL=(ALL) NOPASSWD: /home/script/disable_port12.sh, /home/script/shutdown_thinstantion.sh, /home/script/shutdown_thinstantion.sh_bk
Проверяем каждый скрипт в ручную!!!
Для фиксации ip которые попадают в БАН настраиваем лог агента. Установка..
pwd
/usr/src
yum localinstall nxlog-ce-3.2.2329_rhel7.x86_64.rpm
systemctl enable nxlog
Копируем конфиги /etc/nxlog
systemctl restart nxlog
На Astarisk добавляем мониторинг prometheus+grafana
/etc/asterisk/prometheus.conf
[general]
enabled = yes
core_metrics_enabled = yes
uri = metrics
auth_username = prometheus
auth_password = pass
auth_realm = Asterisk Prometheus Metrics
asterisk -vvvvvvr
module reload res_prometheus.so
asterisk -rx "module show like prometheus"
В /etc/prometheus/prometheus.yml на prometheus добавляем сервера
- job_name: 'asterisk_vip'
metrics_path: /metrics
basic_auth:
username: prometheus
password: pass
static_configs:
- targets: ['192.168.98.14:8088']
labels: {pair: 'idp'}
- targets: ['192.168.121.14:8088']
labels: {pair: 'pldpbx'}
- job_name: 'asterisk_nodes'
metrics_path: /metrics
basic_auth:
username: prometheus
password: pass
static_configs:
- targets: ['192.168.98.214:8088']
labels: {pair: 'idp', node: 'idppbx-214'}
- targets: ['192.168.98.215:8088']
labels: {pair: 'idp', node: 'idppbx-215'}
- targets: ['192.168.121.214:8088']
labels: {pair: 'pldpbx', node: 'pldpbx-214'}
- targets: ['192.168.121.215:8088']
labels: {pair: 'pldpbx', node: 'pldpbx-215'}
И делаем JSON для Grafana
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "Оберіть свій Prometheus datasource",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__requires": [],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"liveNow": false,
"templating": {
"list": [
{
"current": {
"text": "asterisk_vip",
"value": "asterisk_vip"
},
"description": "job у prometheus.yml, що скрейпить CARP VIP",
"hide": 0,
"label": "VIP job",
"name": "vip_job",
"options": [
{
"text": "asterisk_vip",
"value": "asterisk_vip",
"selected": true
}
],
"query": "asterisk_vip",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"text": "asterisk_nodes",
"value": "asterisk_nodes"
},
"description": "job, що скрейпить фізичні ноди",
"hide": 0,
"label": "Nodes job",
"name": "nodes_job",
"options": [
{
"text": "asterisk_nodes",
"value": "asterisk_nodes",
"selected": true
}
],
"query": "asterisk_nodes",
"skipUrlSync": false,
"type": "textbox"
},
{
"allValue": null,
"current": {
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"definition": "label_values(asterisk_core_uptime_seconds{job=\"$vip_job\"}, pair)",
"description": "Список PBX береться автоматично з лейбла pair у prometheus.yml",
"hide": 0,
"includeAll": true,
"label": "PBX (pair)",
"multi": true,
"name": "pair",
"options": [],
"query": {
"qryType": 1,
"query": "label_values(asterisk_core_uptime_seconds{job=\"$vip_job\"}, pair)",
"refId": "PrometheusVariableQueryEditor-VariableQuery"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 100,
"panels": [],
"repeat": "pair",
"repeatDirection": "v",
"title": "━━━ PBX: $pair ━━━",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"unit": "short",
"color": {
"mode": "fixed",
"fixedColor": "blue"
},
"displayName": "${__field.labels.node}"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 1
},
"id": 1,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "name"
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "(asterisk_core_uptime_seconds{job=\"$vip_job\", pair=\"$pair\"} > bool 0) * on(eid) group_left(node) (asterisk_core_uptime_seconds{job=\"$nodes_job\"} > bool 0)",
"instant": true,
"legendFormat": "{{node}}",
"range": false,
"refId": "A"
}
],
"title": "Поточний майстер",
"description": "Хто зараз тримає CARP VIP. Визначається автоматично: eid з VIP-скрейпу зіставляється з лейблом node у job нод.",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"unit": "short",
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 6,
"y": 1
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "count(asterisk_endpoints_state{job=\"$vip_job\", pair=\"$pair\", resource=~\"[0-9]+\"} == 2) or vector(0)",
"instant": true,
"legendFormat": "онлайн",
"range": false,
"refId": "A"
}
],
"title": "Екстеншени онлайн",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"unit": "short",
"color": {
"mode": "fixed",
"fixedColor": "text"
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 10,
"y": 1
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "count(asterisk_endpoints_state{job=\"$vip_job\", pair=\"$pair\", resource=~\"[0-9]+\"}) or vector(0)",
"instant": true,
"legendFormat": "всього",
"range": false,
"refId": "A"
}
],
"title": "Екстеншени всього",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"unit": "short",
"color": {
"mode": "fixed",
"fixedColor": "purple"
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 4,
"x": 14,
"y": 1
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "count(asterisk_endpoints_channels_count{job=\"$vip_job\", pair=\"$pair\", resource=~\"[0-9]+\"} > 0) or vector(0)",
"instant": true,
"legendFormat": "розмов",
"range": false,
"refId": "A"
}
],
"title": "Активні розмови",
"description": "Кількість екстеншенів, що зараз мають активні канали.",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"0": {
"text": "DOWN",
"color": "red",
"index": 0
},
"1": {
"text": "UP",
"color": "green",
"index": 1
}
}
}
],
"unit": "short",
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 1
}
]
},
"displayName": "${__field.labels.node}"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 1
},
"id": 5,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "up{job=\"$nodes_job\", pair=\"$pair\"}",
"instant": true,
"legendFormat": "{{node}}",
"range": false,
"refId": "A"
}
],
"title": "Ноди HA",
"description": "Здоров'я кожної фізичної ноди окремо — ловить мертвий standby.",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"filterable": true,
"inspect": false
},
"mappings": [
{
"type": "value",
"options": {
"0": {
"text": "Unknown",
"color": "text",
"index": 0
},
"1": {
"text": "Offline",
"color": "red",
"index": 1
},
"2": {
"text": "Online",
"color": "green",
"index": 2
}
}
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "transparent",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Стан"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-background",
"mode": "basic",
"applyToRow": true
}
}
]
}
]
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 5
},
"id": 6,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"count"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"displayName": "Стан",
"desc": false
}
]
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "asterisk_endpoints_state{job=\"$vip_job\", pair=\"$pair\", resource!~\"[0-9]+\"}",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A",
"format": "table"
}
],
"title": "Транки",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"job": true,
"eid": true,
"pair": true,
"instance": true
},
"indexByName": {},
"renameByName": {
"resource": "Транк",
"tech": "Тех",
"Value": "Стан"
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"filterable": true,
"inspect": false
},
"mappings": [
{
"type": "value",
"options": {
"0": {
"text": "Unknown",
"color": "text",
"index": 0
},
"1": {
"text": "Offline",
"color": "red",
"index": 1
},
"2": {
"text": "Online",
"color": "green",
"index": 2
}
}
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "transparent",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Стан"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-background",
"mode": "basic",
"applyToRow": true
}
}
]
}
]
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 5
},
"id": 7,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": true,
"fields": "",
"reducer": [
"count"
],
"show": true
},
"showHeader": true,
"sortBy": [
{
"displayName": "Екстеншен",
"desc": false
}
]
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "asterisk_endpoints_state{job=\"$vip_job\", pair=\"$pair\", resource=~\"[0-9]+\"} == 1",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A",
"format": "table"
}
],
"title": "Екстеншени офлайн",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"job": true,
"eid": true,
"pair": true,
"instance": true,
"tech": true
},
"indexByName": {},
"renameByName": {
"resource": "Екстеншен",
"Value": "Стан"
}
}
}
],
"type": "table",
"description": "Сюди потрапляють і номери, створені у FreePBX без фізичного апарата — вони будуть офлайн постійно."
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"filterable": true,
"inspect": false
},
"mappings": [
{
"type": "range",
"options": {
"from": 1,
"to": 99,
"result": {
"color": "green",
"index": 0
}
}
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "transparent",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Каналів"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-background",
"mode": "basic",
"applyToRow": true
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 14
},
"id": 8,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": true,
"fields": "",
"reducer": [
"count"
],
"show": true
},
"showHeader": true,
"sortBy": [
{
"displayName": "Номер",
"desc": false
}
]
},
"pluginVersion": "10.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "asterisk_endpoints_channels_count{job=\"$vip_job\", pair=\"$pair\"} > 0",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A",
"format": "table"
}
],
"title": "Хто зараз розмовляє",
"transformations": [
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"__name__": true,
"job": true,
"eid": true,
"pair": true,
"instance": true
},
"indexByName": {},
"renameByName": {
"resource": "Номер",
"tech": "Тех",
"Value": "Каналів"
}
}
}
],
"type": "table",
"description": "Активні канали по ендпоінтах. Метрики res_prometheus не містять другої сторони дзвінка та тривалості — для цього потрібен AMI/ARI."
}
],
"refresh": "30s",
"schemaVersion": 39,
"tags": [
"asterisk",
"voip",
"ha",
"freepbx"
],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "FreePBX / Asterisk — мультинодовий моніторинг",
"uid": "asterisk-multi",
"version": 1,
"weekStart": ""
}

