Skip to main content

Command Palette

Search for a command to run...

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

Updated
14 min readView as Markdown
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)

  1. Так как у нас будет использоватся 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": ""
}

More from this blog

IT Notes

23 posts