Ровно год назад к нам обратились бывшие коллеги, с предложением принять участие в модификации движка VoIP оператора связи. Задача сводилась к полной переделке личного кабинета, обеспечению масштабирования системы, создания системы биллинга, LCR, мониторинга расходов пользователей, контроля длительности разговоров, аналитики по звонкам. История закончилась печально, т.к. заложенный нами расширенный функционал системы якобы не соответствовал ТЗ, никак не формализованному на бумаге и находящемуся только в головах менеджеров оператора. В связи с тем, что за разработанный функционал, который заказчику очень понравился, менеджеры платить не захотели, отношения мы разорвали. NDA и договора у нас не было, поэтому посоветовавшись с коллегами мы решили часть наработок выложить в свободный доступ. Я думаю, что это будет серия статей. И начнём пожалуй с базовых вещей и архитектуры. Каждый администратор, который хотя бы раз в жизни имел дело с IP телефонией знает, что услуги IP телефонии могут предоставляться конечному абоненту несколькими способами: Компании имеющие представительства в нескольких странах или желающие получить присутствие в другой стране, могут купить, для удобства своих клиентов, телефонный номер скажем в Великобритании и обрабатывать поступающие звонки в Москве. При этом оператор предоставивший им такой номер может не заниматься доставкой звонков в Великобританию. Некоторые страны, например республика Беларусь, не предоставляют номера нерезидентам.  Итак, исходим из следующих начальных требований: Небольшая ремарка: описываемая конфигурация является универсальной, но в большей степени подходит для сервисов имеющих клиенто-ориентированное направление, например call центров, в случаях когда необходима персонализация клиентов и привязка клиента к одному менеджеру или группе менеджеров. Большинство описываемых механизмов универсально и может эффективно использоваться в других конфигурациях. Требований довольно много, так с чего начать? Принимать и совершать звонки будет Asterisk, подготовкой вызовов будет заниматься Application Server на python, все рабочие данные мы будем хранить в MariaDB, а большинство логики будет реализовано в виде процедур. Это позволит нам максимально дистанцировать логику работы asterisk от лишних правил в диалплане и обеспечить масштабируемость совместно с унификацией конфигурации. Определимся с нашим трафиком. Нам необходимо присутствие скажем на Кипре, Америке, Великобритании и России. С точки зрения Российского трафика выгоднее работать через Российских операторов связи. Например Весткол, IPPort и прочих. Российские(московские) операторы также по довольно приемлемой цене предоставляют в аренду телефонные номера в кодах 495 и 499. Присутствие на Кипре, в Америке и Великобритании мы можем обеспечить себе купив номера у Zadarma, Multilel или кого-то другого. Так как международные вызовы у Российских операторов довольно дороги, то можно купить трафик у зарубежных операторов для примера VoiceBuy или VoxBeam. При подключении к оператору связи в варианте "логин/пароль" звонки на нашу АТС будут приходить с IP адреса сервера оператора, на котором происходит регистрация. Однако в случае исходящих звонков адрес сервера может быть другим. При подключении к оператору через SIP транк входящие звонки могут приходить с нескольких серверов из пула оператора, а исходящие звонки осуществляются по единому DNS имени, на котором в большинстве случаев находится несколько IP адресов. Как мы уже определились ранее, оператор может иметь возможность замены CallerID для исходящего вызова, что может регулироваться тарифным планом либо договором. Замена CallerID(подмена номера) в России при исходящих вызовах запрещена. С зарубежными операторами дело обстоит намного проще и замена CallerID вполне себе поддерживается. Большинство публикуемых в данной статье данных и таблиц являются выдержками из ядра реальной базы данных, за исключением номеров телефонов и IP адресов В связи с тем, что у нас имеются мультивалютные операторы связи, в таблице операторов связи должна присутствовать ссылка на таблицу валют. Чуть позже мы рассмотрим возможность мультивалютного пересчёта тарифов операторов. Итак. Операторы. Обычно у зарубежных операторов 2-3 тарифа по которым они доставляют трафик до вызываемого абонента. Про различия CLI и NonCLI техник можно прочитать здесь. Для выбора тарифного плана обычно используется префикс (noncli_prefix, cli_prefix, premcli_prefix) указываемый перед вызываемым номером в процессе формирования строки вызова.  Описание полей: В случае с WestCall и другими операторами предоставляющими подключение через SIP транк, вы покупаете пул номеров и осуществляя вызовы можете менять исходящий номер на любой номер из данного пула. IPPort как и прочие операторы, при использовании схемы логин/пароль с единственным купленным номером, не позволяют менять исходящий номер. Операторы VoxBeam и VoiceBuy используются для исходящих звонков для любых направлений и позволяют менять исходящий номер на любой другой. Правда здесь есть одно но! И заключается оно в том, что конечные операторы связи доставляющие звонок до вызываемого абонента, могут отклонить вызов или поменять номер вызывающего абонента на свой, если вызывающий номер соответствует внутреннему номеру страны или местности. Т.е. скажем при звонке из России в Украину(Киев) мы меняем CallerID на украинский номер в Киеве и оператор связи через которого идёт вызов может данный звонок просто "отбить", т.к. он не соответствует внутренней политике обработки звонков "Звонок через внешний входящий международный транк не может содержать внутренних номеров". Операторы Zadarma и Multitel у нас будут использоваться в качестве поставщиков номеров и исходящий трафик мы через них отправлять не будем, хотя при желании могли бы. Важным аспектом обработки вызова является безопасность. Довольно часто встречаются ситуации, когда неверно настроенный сервер IP телефонии позволяет пропускать сквозь себя чужие звонки. Для обеспечения безопасности контуров обрабатывающих входящие и исходящие звонки, дополнительно к разделению операторов по различным контекстам, стоит создать базу IP адресов операторов связи предоставляющих нам услуги связи. Во первых это позволит настроить экспорт адресов в firewall и убрать лишние попытки "прозвона" через нас, во вторых даст более полную информацию о способах подключения к операторам связи. Описание полей: Таким образом связка оператора и адресов будет выглядеть следующим образом. Для чего это нужно ? Попробуем получить строку вызова для номера 74957777777. Как мы видим, мы получили готовые данные для дозвона, которые нужно передать приложению Dial сервера Asterisk. На этом пока остановимся. Для привязки арендованных у оператора связи номеров, нам необходимо задать список серверов на которых данные номера будут обслуживаться. Описание полей: Для чего нужна эта таблица и почему она такая странная? Для межсерверного соединения серверов asterisk удобнее всего использовать протокол IAX. Причин несколько: С серверами на начальном этапе разобрались. Теперь необходимо сделать привязку арендуемых номеров к определенному серверу. Это необходимо для корректной маршрутизации вызовов. CREATE TABLE numbers_pool (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
number VARCHAR(50) NULL DEFAULT '0' COMMENT 'Арендуемый номер' COLLATE 'utf8mb4_unicode_ci',
provider_id INT(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Прикрепление номера к оператору',
server_id INT(11) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Прикрепление номера к серверу',
enabled BIT(1) NOT NULL DEFAULT b'1' COMMENT 'Включение/отключение номера',
direction ENUM('IN','OUT','IN/OUT') NULL DEFAULT 'IN' COMMENT 'Направление звонка по умолчанию' COLLATE 'utf8mb4_unicode_ci',
virtualflag BIT(1) NOT NULL DEFAULT b'1' COMMENT 'Виртуальный или реальный номер',
echotest TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Тестирование номера 0/1 — выключено/включено',
PRIMARY KEY (id),
UNIQUE INDEX number (number),
INDEX FK_number_assignment_copy_providers (provider_id),
INDEX FK_number_assignment_servers (server_id),
INDEX number btree (number),
CONSTRAINT FK_provider FOREIGN KEY (provider_id) REFERENCES providers (id),
CONSTRAINT FK_server FOREIGN KEY (server_id) REFERENCES servers (id)
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
; Описание полей: На момент разработки системы количество арендованных номеров было около 400 штук, количество серверов 4. Для тестирования маршрутизации вызовов даже добавляли Мультифон от Мегафона. При использовании "реальных" номеров, привязанных к логину/паролю, необходимо обеспечить осуществление вызова через правильный диалпир. Так как asterisk не может знать через какой диалпир должен маршрутизироваться вызов, необходимо создать таблицу связей. Описание полей: На этом настройка базы для обслуживания вызовов практически закончена. Я намеренно не рассматриваю сейчас работу тарификатора и систем контроля вызова, так как это очень большая тема, которую стоит вынести в отдельную статью. В связи с тем, что основная логика обработки и маршрутизации вызова вынесена из Asterisk в Application Server, а процедуры находятся в MariaDB, необходимо обеспечить механизмы отладки и контроля работы процедур. Для этого можно использовать следующую схему.
Для хранения отладочной информации создаём дополнительную базу данных debug и создаём там таблицу debug_records Для записи в данную таблицу данных создаём процедуру debug Теперь отладка и контроль работы процедуры на этапе разработки превращается в удовольствие. В начале каждой процедуры мы определяем две переменные, первая — это название процедуры, вторая — это флаг определяющий включена отладка или нет. И далее в различных местах, где процедура может повести себя некорректно — предварительно добавляем запись в отладочный журнал.  Обращаю Ваше внимание на то, что использование данной логики не рекомендовано!!! в продакшн режиме на больших нагрузках, так как вывод отладочной информации может сильно снизить быстродействие. Перейдём к настройке Asterisk сервера. Стандартная процедура обработки вызовов попадающих в контекст public, в большинстве существующих конфигураций, выглядит следующим образом обработка исходящего вызова из контекста dialout выглядит так В общем кто как привык и кому как нравится. Как мы видим при такой организации исходящих вызовов практически нет информации о состоянии вызова. Мы модифицировали Dialplan обработки звонков и добавили в него сервисные функции Контекст  содержит в себе сервисные функции такие как: Контекст  вызывается перед соединением абонентов при успешном поднятии трубки на вызываемой стороне.
В контекст  попадают все внешние звонки.
В контекст  попадают все звонки, которые необходимо переадресовать с сервера на сервер.
Контекст  является основным контекстом пользователей для подготовки маршрутов для вызовов.
Контекст  является основным контекстом при осуществлении исходящего вызова. Рассмотрим более подробно процедуру обработки входящего вызова: Все внутренние и внешние исходящие вызовы происходят через контексты  и . Рассмотрим подробнее процедуру исходящего вызова: На этом логика работы сервера Asterisk заканчивается. Используя данную связку базы данных и приложений, добавление серверов в обслуживающий кластер происходит путём копирования конфигурационной директории сервера Asterisk, сменой имени сервера, генерацией новых RSA ключей для IAX соединений и прописыванием идентификационных данных сервера в одной таблице. Вся остальная логика работы формируется через веб интерфейс путём привязки номеров и операторов к серверам. Использование AGI приложений на Python обусловлено единообразием среды разработки при проектировании Application сервера. Начальная версия AGI приложений использовала прямое подключение к базе. После запуска системы и отладки работы ядра, AGI приложения были переписаны под использование протокола HTTP и Application Server на uwsgi. Часть операций не требующих проверки условий и вычислений, например проверка категорий номеров, была переписана для скорости на использование CURL в диалплане. Следующая статья посвящена тарификатору, LCR и определению геопозиции вызываемых или вызывающих абонентов. © Aborche 2017
 IT волшебник