Кластер Asterisk. Централізація інформації про реєстрацію

У більшості адміністраторів, що працюють з телефонією на базі Asterisk, в компаніях, де штат перевищує 500+ співробітників, рано або пізно встає питання про повноцінної кластеризації Active/Active. Передумовами до цього може бути і наявність регіональних відгалужень, і бажання зробити систему надійніше. Тема обширна і не є метою даної статті в повному обсязі, яка написана з метою показати один із самих швидких і надійних способів добути інформацію про реєстрацію пристроїв на серверах в кластері, з метою подальшої централізації або/та дистрибуції всередині кластера. Логічно припустити, що найпродуктивніший спосіб — це бути частиною самого Asterisk.

Тому, щоб не тягти кота за хвіст, шаблон завантажувального модуля для Asterisk:

#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")

#include "asterisk/module.h"
#include "asterisk/logger.h"

static int load_module(void){
// Init code here
return AST_MODULE_LOAD_SUCCESS;
}

static int unload_module(void){
// Destroy code here
return 0;
}

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");

Це мінімально необхідний код, який потрібно помістити в папку з сорцами Asterisk в підкаталог res. При компіляції, буде зібраний новий модуль з назвою «ім'я файлу» і дискрипцией «Hello World».

Відмінно, всередину Asterisk ми потрапили, що далі? Нам потрібно отримати інформацію про реєстрацію телефону, бонусом ми хочемо знати IP-адреса телефону, можливе його джитер і статус (USE/NOT_USE/HOLD).

В Asterisk для цього існує Stasis . На жаль, єдиний спосіб розібратися в роботі цього механізму — це вивчити вихідні коди. Отже, зробимо 3 простих кроки:

1. Підписуємося на отримання подій від шини стазис

stasis_subscribe(ast_endpoint_topic_all(),acl_change_stasis_dev_status,NULL);

ast_endpoint_topic_all() — повертає назву «Топіка» повідомлення.
acl_change_stasis_dev_status — Це функція, яка буде викликана, коли в стазисі з'явитися потрібне для нас повідомлення з вказаного топіка.
2. Створюємо функцію, в якій будемо ловити потрібні повідомлення.
static void acl_change_stasis_dev_status(void *data, struct stasis_subscription *sub, struct stasis_message *msg){

}

3. Власне найсмачніше — код обробки події.
Перед тим як начеркать своє, потрібно зрозуміти, а в якому вигляді вона до нас прийде? Для цього ліземо в сорцы і знаходимо ось такий шматок:

ast_endpoint_blob_publish(peer->endpoint, ast_endpoint_state_type(), blob);

і далі:

void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
if (blob) {
message = ast_endpoint_blob_create(endpoint, type, blob);
}
if (message) {
stasis_publish(ast_endpoint_topic(endpoint), message);
}
}

struct stasis_message *ast_endpoint_blob_create(struct ast_endpoint *endpoint,
struct stasis_message_type *type, struct ast_json *blob)
{
RAII_VAR(struct ast_endpoint_blob *, obj, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);

if (!type) {
return NULL;
}
if (!blob) {
blob = ast_json_null();
}

if (!(obj = ao2_alloc(sizeof(*obj), endpoint_blob_dtor))) {
return NULL;
}

if (endpoint) {
if (!(obj->snapshot = ast_endpoint_snapshot_create(endpoint))) {
return NULL;
}
}

obj->blob = ast_json_ref(blob);

if (!(msg = stasis_message_create(type, obj))) {
return NULL;
}

ao2_ref(msg, +1);
return msg;
}

Що дає нам це шматок коду? Розуміння того, що в якості корисного навантаження в нашу функцію ми отримаємо ast_endpoint_blob, всередині якого буде ast_endpoint_snapshot і JSON.

Тепер наш код:

if(ast_endpoint_state_type() != stasis_message_type(msg))return; // Перевіримо, ми отримали потрібне нам повідомлення?

// Все інше выдергиваеться з вихідних кодів Asterisk при тривалому і уважному вивченні
struct ast_endpoint_blob * n=stasis_message_data(msg); // Корисне навантаження в даному топіку ходить у вигляді структури ast_endpoint_blob

// Відомості про реєстрацію конвентированы в JSON
struct ast_json * m;

struct ast_endpoint_snapshot *snap; // А ось інформація про бенкет лежить поруч з JSON в структурі ast_endpoint_snapshot
snap=n->snapshot; // Отримуємо інформацію про бенкет
m=n->blob; // Отримуємо JSON

char buffer[1050];
sprintf(buffer,"Device %s (%s): %s\n",snap->id,ast_endpoint_state_to_string(snap->state),ast_json_dump_string_format(m,AST_JSON_COMPACT));
ast_log(LOG_NOTICE,buffer); // виводимо в лог 

Описані вище структури потягнуть за собою наступні інклуд:

#include "asterisk/stasis_endpoints.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_system.h"
#include "asterisk/devicestate.h"
#include "asterisk/json.h"

Про них теж потрібно не забути, при створенні модуля.

Загальна:

#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")

#include "asterisk/module.h"
#include "asterisk/logger.h"

#include "asterisk/stasis_endpoints.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_system.h"
#include "asterisk/devicestate.h"
#include "asterisk/json.h"

static void acl_change_stasis_dev_status(void *data, struct stasis_subscription *sub, struct stasis_message *msg){
if(ast_endpoint_state_type() != stasis_message_type(msg))return;

struct ast_endpoint_blob * n=stasis_message_data(msg);

struct ast_json * m;
struct ast_endpoint_snapshot *snap;
snap=n->snapshot;
m=n->blob;

char buffer[1050];
sprintf(buffer,"Device %s (%s): %s\n",snap->id,ast_endpoint_state_to_string(snap->state),ast_json_dump_string_format(m,AST_JSON_COMPACT));
ast_log(LOG_NOTICE,buffer);
}

static int load_module(void){
stasis_subscribe(ast_endpoint_topic_all(),acl_change_stasis_dev_status,NULL);
return AST_MODULE_LOAD_SUCCESS;
}

static int unload_module(void){
// Тут треба відписатися від події
return 0;
}

Для чого це потрібно? Отриману інформацію можна класти в єдину базу кластера, яка завжди буде знати на якому конкретно сервері зареєстровано пристрій. Смаршутизировать дзвінок, спираючись на цю інформацію вже можна засобами диалплана.

Та, власне, як це виглядає:

image
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.